編程學(xué)習(xí)過程中,我曾經(jīng)我犯過一個錯誤(我想多數(shù)人也跟我一樣心急)?!皩W(xué)”完C語言后緊接著學(xué)C++,等稍微有基礎(chǔ)了之后開始接觸C++ GUI Qt編程等。但學(xué)習(xí)Qt圖形化編程的過程中又發(fā)現(xiàn)寫程序的關(guān)鍵點(diǎn)無外乎編寫函數(shù)或方法。自己對函數(shù)編寫的理解不太深入而導(dǎo)致不會寫“自己”的C/C++函數(shù)(或方法)。雖然我們能夠?qū)⑿枨蠓纸獬啥鄠€模塊或函數(shù),但這并不意味著我們也能將需求編寫成函數(shù)(或方法)來讓程序正常運(yùn)行。因此,本文簡要總結(jié)一下有關(guān)函數(shù)的一些概念及個人學(xué)習(xí)體會。
圖1 人腦將概念分解成“屬性”和“函數(shù)”的過程
1、內(nèi)存中用一維空間來表示多維世界
也許我們活在一維世界里,只是我們認(rèn)為這個世界是三維(3D)或更多維的復(fù)雜多樣。 為什么這么說呢?因?yàn)橛嬎銠C(jī)的世界就是由一維數(shù)組升級成多維數(shù)組形式給我們演變出了多維畫面和模型。比如,內(nèi)存存最小地址標(biāo)識單位為字節(jié)(byte),然后我們將一維內(nèi)存通過多維形式標(biāo)識并在此基礎(chǔ)上借助數(shù)組、指針、結(jié)構(gòu)體等數(shù)據(jù)結(jié)構(gòu)構(gòu)造了更復(fù)雜的模型和需求,最終能夠在內(nèi)存的一維空間中完成多維現(xiàn)實(shí)世界的“模擬”需求。如下圖2所示一維數(shù)組多種標(biāo)識方法就是其中的具體案例之一。計算機(jī)內(nèi)存其實(shí)一維的,一維空間來表示多維空間是我們不可否認(rèn)的事實(shí)。
圖2 內(nèi)存中一維數(shù)組來表示多維數(shù)組
2、為什編寫函數(shù)是程序員的基本功
雖然我們通過一維內(nèi)存空間來表示出多維現(xiàn)實(shí)世界,但其現(xiàn)實(shí)過程需要較理解不同數(shù)據(jù)類型及其在內(nèi)存中的布局、程序的運(yùn)行原理、內(nèi)存中多種數(shù)據(jù)結(jié)構(gòu)的融融應(yīng)用等基礎(chǔ)概念及原理。我之前對編程感到恐怖,因?yàn)榭吹侥切┐a時腦袋的形成的形象是空白的(或者說一維的),腦海里沒有形成這些代碼在磁盤、內(nèi)存、寄存器、CPU等之間“流”進(jìn)“流”的過程和狀態(tài)。
在這樣的狀態(tài)下,我們無論學(xué)C或面向?qū)ο蟮腃++及Qt等其他編程語言及工具,最終編寫函數(shù)或?qū)ο蟮姆椒ǖ倪^程中總會感覺到無從下手。我想這也是我們覺得C/C++難學(xué)原因之一,因此我回頭重學(xué)了C語言的基礎(chǔ)知識。在這個過程中,兩個內(nèi)容的回顧對我?guī)砹艘庀氩坏降氖斋@。其中之一是C語言面向?qū)ο缶幊?,尤其是用C語言實(shí)現(xiàn)封裝和繼承特性。其二是用C語句描述算法的相關(guān)解釋說明。
圖3 程序在內(nèi)存中布局
3、用C語句描述算法
程序就是對計算機(jī)要執(zhí)行的一組操作序列的描述。高級語言源程序的基本組成單位是語句。語句按功能可以分為兩類: 一類用于描述計算機(jī)要執(zhí)行的操作運(yùn)算(如賦值語句),另一類是控制上述操作運(yùn)算的執(zhí)行順序(如循環(huán)控制語句) 。前一類稱為操作運(yùn)算語句,后一類稱為流程控制語句。
C語言是一種表達(dá)式語言,所有的操作運(yùn)算都通過表達(dá)式來實(shí)現(xiàn)。由表達(dá)式組成的語句稱為表達(dá)式語句,它由一個表達(dá)式后接一個分號組成(注意,沒有分號的不是語句)。表達(dá)式語句可以分為以下三種基本類型:
(1) 賦值語句:由賦值表達(dá)式加一個分號組成。例如:i=1;
(2) 函數(shù)調(diào)用語句:
(3) 空語句
高級語言一般以兩種形式提供流程控制:
(1)形成流程控制結(jié)構(gòu)(如if、while、for語句)。
(2)簡單的流程轉(zhuǎn)向。
控制結(jié)構(gòu)分為順序、選擇和循環(huán)等三種基本結(jié)構(gòu),大多數(shù)高級語言都提供這三種控制結(jié)構(gòu)。準(zhǔn)確地說,是后兩種。因?yàn)轫樞蛐褪亲匀恍纬傻模瑹o須在程序中加以專門的控制。
圖4 if或switch語句模擬多路選擇結(jié)構(gòu)的開關(guān)電路
限定轉(zhuǎn)向語句(簡單的流程轉(zhuǎn)向)不形成控制結(jié)構(gòu),只是簡單地使流程從其所在處轉(zhuǎn)向另一處。但是它不允許用戶自己指定轉(zhuǎn)向,而是按系統(tǒng)事先規(guī)定的原則向某一點(diǎn)轉(zhuǎn)移,用戶不必指定轉(zhuǎn)向。C語言中屬于這類的語句有三種:
(1) break 語句:它的功能是把流程從所在處轉(zhuǎn)向所在的循環(huán)結(jié)構(gòu)或多路選擇結(jié)構(gòu)之后,或者說是中止執(zhí)行這些結(jié)構(gòu)(見圖5)。
(2) continue 語句:使本次循環(huán)體的執(zhí)行提前結(jié)束(不再執(zhí)行continue下面的語句),然后再根據(jù)循環(huán)條件是否滿足,決定是否進(jìn)入下次循環(huán)(見圖5)。
圖5 限定轉(zhuǎn)向語句(簡單的流程轉(zhuǎn)向)
(3) 函數(shù)調(diào)用和返回: 函數(shù)調(diào)用的功能是使流程轉(zhuǎn)向所調(diào)用的函數(shù)體。return語句的功能是使流程從被調(diào)用函數(shù)返回主調(diào)函數(shù)。這兩種流程控制都可能伴隨有參數(shù)傳遞。
綜前所述,函數(shù)編寫的關(guān)鍵在理解和善用操作語句(賦值語句)和控制語句,把C語言中的基本語句歸納如下:
圖6 C語言中的基本語句
4、理解指針(地址)和結(jié)構(gòu)體
指針是一個特殊的變量,它里面存儲的數(shù)值被解釋成為內(nèi)存里的一個地址。要搞清一個指針需要搞清指針的四方面的內(nèi)容:指針的類型、指針?biāo)赶虻念愋?、指針的值或者叫指針?biāo)赶虻膬?nèi)存區(qū)、指針本身所占據(jù)的內(nèi)存區(qū)。
結(jié)構(gòu)體是構(gòu)造復(fù)雜數(shù)據(jù)類型的最有效的工具,對這個概念還不了解,基本上無法構(gòu)造數(shù)據(jù)模型,一般能日常使用的程序中沒有一個業(yè)務(wù)體是完全使用原生數(shù)據(jù)類型來完成的,如下圖6所示。設(shè)計數(shù)據(jù)模型的時候,一般先把頭文件中的結(jié)構(gòu)體數(shù)據(jù)整理出來。然后設(shè)計好功能函數(shù)的參數(shù),以及名字,然后才真正開始寫C源碼。
圖7 用C語言來封裝屬性和函數(shù)
其實(shí)C語言也能編寫面向?qū)ο缶幊田L(fēng)格的程序,如附件1所示的封裝特性演示代碼就采用封裝特性 ,還有繼承特性的實(shí)現(xiàn),篇幅原因不再贅述。當(dāng)看懂了這段代碼后,我突然明白了函數(shù)指針、結(jié)構(gòu)體、面向?qū)ο缶幊讨械膖his(或self),及構(gòu)造函數(shù)等等的來歷。也領(lǐng)悟到了將函數(shù)封裝到的類(class或?qū)ο螅├镏?,通過點(diǎn)或指針訪問函數(shù)(方法)來實(shí)現(xiàn)對結(jié)構(gòu)體成員訪問和修改在內(nèi)存中的實(shí)現(xiàn)過程。
5、結(jié)束語
函數(shù)、指針、結(jié)構(gòu)體這三大塊硬骨頭是學(xué)習(xí)C語言(或編程)的絆腳石 ,下功夫拿掉基本上C語言的大動脈就打通了,如果想開發(fā)實(shí)際能用到的程序,那么也需要了解 文件和數(shù)據(jù)庫的讀寫等第四塊常被我們忽視的骨頭。 尤其是每當(dāng)通過new來創(chuàng)建對象或定義結(jié)構(gòu)體變量來創(chuàng)建數(shù)據(jù)模型時,我們會感覺到計算機(jī)世界里創(chuàng)建資源和使用資源時多么“簡單”和“直接”的。如果在現(xiàn)實(shí)世界,從0到1的價值創(chuàng)造(不是資源調(diào)配)是多么難的事情,需要我們要么用金錢換來或其他方式對等交換。因此,雖然編程很“難”(與其說難、不如說我們貪),但相對于現(xiàn)實(shí)世界的種種困難,讓我們在計算機(jī)虛擬世界里擁有無限的資源和可能性。
圖8 電腦對“程序”的理解和處理過程
參考資料:
C語言程序設(shè)計教程 譚浩強(qiáng) 張基溫 唐永炎 高等教育出版社
C語言游戲?qū)崙?zhàn)教程
附件1:C語言封裝特性演示代碼
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
struct student {
void (*setStudentId)(struct student* s, int year, int classNum, int serialNum);
const char* (*getGender)(struct student* s);
void (*setGender)(struct student* s, const char* strGender);
int id; // 學(xué)號
char name[20]; // 姓名
int gender; // 性別
int mark; // 分?jǐn)?shù)
};
void setStudentId(struct student* s, int year, int classNum, int serialNum)
{
char buffer[20];
sprintf(buffer, "%d%d%d", year, classNum, serialNum);
int id = atoi(buffer);
s->id = id;
}
const char* getGender(struct student* s)
{
if (s->gender == 0)
{
return "女";
}
else if (s->gender == 1)
{
return "男";
}
return "未知";
}
void setGender(struct student* s, const char* strGender)
{
int numGender;
if (strcmp("男", strGender) == 0)
{
numGender = 1;
}
else if (strcmp("女", strGender) == 0)
{
numGender = 0;
}
else
{
numGender = -1;
}
s->gender = numGender;
}
void initStudent(struct student* s)
{
s->setStudentId = setStudentId;
s->getGender = getGender;
s->setGender = setGender;
}
int main()
{
struct student stu;
// 初始化student
initStudent(&stu);
// 學(xué)號:202212326
// 姓名:小明
// 性別: 男
// 分?jǐn)?shù):98
stu.setStudentId(&stu, 2022, 123, 26);
strcpy(stu.name, "小明");
stu.setGender(&stu, "男");
stu.mark = 98;
// 打印這些數(shù)值
printf("學(xué)號:%d\\n", stu.id);
printf("姓名:%s\\n", stu.name);
const char* gender = stu.getGender(&stu);
printf("性別:%s\\n", gender);
printf("分?jǐn)?shù):%d\\n", stu.mark);
return 0;
}
收錄于合集 **#**軟件工程
9個
-
計算機(jī)
+關(guān)注
關(guān)注
19文章
7525瀏覽量
88319 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
3042瀏覽量
74177 -
模型
+關(guān)注
關(guān)注
1文章
3279瀏覽量
48974
發(fā)布評論請先 登錄
相關(guān)推薦
評論