21天学通c语言总结(3)
第15天:有關(guān)指針的高級(jí)用法
指向指針的指針
int multi[3][4]; int (*ptr)[4]; int *p; p = (int *)ptr;上面multi是一個(gè)指針指向multi[0],multi[0]也是一個(gè)指針指向multi[0][0]。
ptr是一個(gè)能指向包含4個(gè)int變量的數(shù)組的指針變量,數(shù)組指針。必須用圓括號(hào)否則聲明的是指針數(shù)組,因?yàn)殚g接運(yùn)算符的優(yōu)先級(jí)低于[]。
(int *)是強(qiáng)制類型轉(zhuǎn)換,暫時(shí)將ptr的數(shù)據(jù)類型從聲明的類型轉(zhuǎn)換為新的類型。
指針數(shù)組
char *mes[20];
將指針數(shù)組傳遞給函數(shù):
void pfs(char *p[], int n);
void pfs(char **p, int n);
上面兩個(gè)均可以傳遞指針數(shù)組,實(shí)際上傳遞的是指向指針的指針。
int指針數(shù)組,int *p[4]; p[0]+1;的內(nèi)容并不是p[1]而是p[0]所表示的地址加上4個(gè)字節(jié)。
malloc()默認(rèn)返回void類型的指針。前面加(char *)強(qiáng)制轉(zhuǎn)換。
函數(shù)指針
type (*ptr_to_func)(paremeter_list);
*ptr_to_func加括號(hào)是因?yàn)?*的優(yōu)先級(jí)小于()。如果不加則定義的是返回指針的函數(shù)。
被指向函數(shù)的返回類型和參數(shù)列表必須和指針聲明的返回值和參數(shù)列表相同。
int square(int);
int (*ptr)(int);
ptr = square;
不帶括號(hào)的函數(shù)名是一個(gè)指向函數(shù)的指針常量。
- 聲明函數(shù)指針一定用圓括號(hào);
- 不要將函數(shù)指針指向返回類型和參數(shù)列表不匹配的函數(shù)。
鏈表
鏈表中的數(shù)據(jù)被存儲(chǔ)在結(jié)構(gòu)中。
struct person{ char name[20]; struct person *next; };組成鏈表的結(jié)構(gòu)可稱為鏈接、節(jié)點(diǎn)或元素。
最后一個(gè)節(jié)點(diǎn)的next指針為NULL,頭指針(非結(jié)構(gòu))指向第一個(gè)節(jié)點(diǎn)。
刪除后節(jié)點(diǎn)仍然在內(nèi)存中,但是不在鏈表中,實(shí)際程序應(yīng)歸還被刪除節(jié)點(diǎn)占用的內(nèi)存,仍然通過(guò)free()完成,見第20天。
總結(jié)
- 數(shù)組指針
- 指針數(shù)組(指向指針的指針),更方便處理字符串。
- 函數(shù)指針。
- 鏈表。
第16天:使用磁盤文件
磁盤文件
- 文本流與文本模式文件關(guān)聯(lián)
- 二進(jìn)制流與二進(jìn)制模式相關(guān)聯(lián)
c文件路徑名:
c:\\data\\list.txt;
打開文件
FILE *fopen(const char *filename, const char *mode);
讀取前聲明文件指針,FILE是stdio.h中定義的結(jié)構(gòu),調(diào)用fopen時(shí)創(chuàng)建FILE實(shí)例,成功則返回指向該實(shí)例的指針,失敗返回NULL。
打開模式mode:
- r:只讀,不存在返回NULL
- w:只寫,不存在創(chuàng)建,存在則刪除后創(chuàng)建
- a:附加,不存在創(chuàng)建,存在追加到末尾
- r+:讀寫,不存在創(chuàng)建,存在寫在文件開頭,覆蓋原數(shù)據(jù)
- w+:讀寫,不-------建,存在則覆蓋
- a+:讀寫,---------建,存在則追加到末尾
默認(rèn)為文本模式,以二進(jìn)制打開在模式后加上b,rb,wb,ab等。
使用fopen()時(shí),一定要檢查是否出錯(cuò)。
讀寫文件數(shù)據(jù)
三種方式
- 格式化輸出,只能用于文本文件,主要供其他文件讀取。
- 字符輸出,主要用于文本文件。
- 直接輸出(direct output),將內(nèi)存的內(nèi)容直接保存到磁盤文件,只適用于二進(jìn)制文件,供c程序使用。
讀取時(shí)也是類似三種。
上面的規(guī)則并非不可逾越。
格式化輸入/輸出
int fprintf(FILE *fp, char *fmt, ...);
fprintf與printf區(qū)別只是前者可以指定流,如果指定stdout則沒有區(qū)別。
int fscanf(FILE *fp, const char *fmt, ...);
與scanf的差別也僅是指定流。
字符輸入/輸出
getc(),fgetc()。這兩個(gè)函數(shù)相同,成功返回讀取的字符,失敗返回EOF(-1)。
int getc(FILE *fp);
返回int是因?yàn)?#xff0c;讀取文件時(shí)必須讀取文件尾標(biāo)記,而有些系統(tǒng)標(biāo)記為int,而不是char。
fgets(),char *fgets(char *str, int n, FILE *fp); str是指向緩沖區(qū)的指針,n(讀n-1個(gè),最后加空字符)是要讀取的最大字符數(shù),fp是FILE指針,由fopen返回的。
失敗返回NULL。
putc(),fputc()。int putc(int ch, FILE *fp); 將一個(gè)字符寫入流,成功返回字符,失敗返回EOF。
fputs()。char fputs(char *str, FILE *fp); 與puts的差別,可以指定流,不會(huì)在末尾加換行符。
直接輸入/輸出
fwrite()
int fwrite(void *buf, int size, int count, FILE *fp);
buf是指向內(nèi)存區(qū)域的指針(任何類型),size指定每項(xiàng)數(shù)據(jù)的長(zhǎng)度(字節(jié)),count數(shù)據(jù)項(xiàng)數(shù)。
成功返回count,失敗返回小于count的數(shù)。
struct adress data[50];寫入文件,可以:
fwrite(data, sizeof(adress), 50, fp);或fwrite(data, sizeof(data), 1, fp);二者等價(jià)。
fread()
int fread(void *buf, int size, int count, FILE *fp);
用法類似fwrite()。但使用fread時(shí)要小心,明確數(shù)據(jù)類型,否則中不會(huì)報(bào)錯(cuò)但結(jié)果錯(cuò)誤。
文件緩沖技術(shù):關(guān)閉和刷新文件
int fclose(FILE *fp); fp是與流相關(guān)聯(lián)的FILE指針,成功返回0,失敗-1,關(guān)閉時(shí)刷新文件的緩沖區(qū)(寫入文件中)。
void fcloseall(void);刷新所有的流緩沖區(qū)(標(biāo)準(zhǔn)流之外),返回關(guān)閉的流。(不是返回void么)。除非要關(guān)閉所有的流,否則不要用fcloseall()。
也可以不關(guān)閉流刷新緩沖區(qū),int fflush(FILE *fp);,不想關(guān)閉文件同時(shí)想將文件緩沖區(qū)的內(nèi)容寫入時(shí)使用,成功返回0,失敗返回EOF。int flushall(void);,需要刷新所有打開的流的緩沖區(qū)時(shí)使用,返回打開的流的數(shù)目。
順序文件存取和隨機(jī)文件存取
每個(gè)打開的文件都有一個(gè)相應(yīng)的位置指示器。
void rewind(FILE *fp);調(diào)用后指示器指向開頭(字節(jié)0)。
long ftell(FILE *fp); 成功則返回當(dāng)前位置離開頭的字節(jié)數(shù),錯(cuò)誤返回-1L。
int fseek(FILE *fp, long offset, int origin);,調(diào)用后可以將位置指向任何位置,offset指定移動(dòng)的距離,origin指定相對(duì)什么的位置。
origin可以是SEEK_SET表示0開始,SEEK_CUR表示1當(dāng)前,SEEK_END表示2末尾。成功返回0,錯(cuò)誤返回非0。
if ( fseek(fp, (offset * sizeof(int)), SEEK_SET) != 0 )
檢測(cè)文件尾
文本文件:while((c = fgetc(fp)) != EOF),因?yàn)锳SCII碼都不為-1,當(dāng)讀入-1時(shí)可以斷定到末尾。
二進(jìn)制文件及文本文件:int feof(FILE *fp);未到末尾返回0,否則返回非0。
文件管理函數(shù)
刪除文件
int remove(const char *filename);,filename指向要?jiǎng)h除的文件,刪除成功返回0,不成功返回-1。
重命名文件
int rename(const char *oldname, const char *newname);,兩個(gè)文件必須位于相同的磁盤驅(qū)動(dòng)器,成功0,失敗-1。
復(fù)制文件
c語(yǔ)言沒有復(fù)制文件的庫(kù)函數(shù)。但因?yàn)閏使用流。因此自己編寫很簡(jiǎn)單。
1.以二進(jìn)制rb讀,可以保證能夠復(fù)制任何文件,而不是僅僅是文本文件。
2.二進(jìn)制wb模式打開目標(biāo)文件。
3.fgetc()讀取一個(gè)字符,rb打開指示器在開頭,無(wú)需顯式的放置。
4.feof()表明到結(jié)尾(非0),則關(guān)閉兩個(gè)文件,返回0。
5.未到末尾,字符寫入目標(biāo)文件,回到3。
臨時(shí)文件
char *tmpnam(char *s);,用來(lái)創(chuàng)建有效且不與任何已有文件沖突的名稱。s指向的緩沖區(qū)必須足夠大。
兩種方式創(chuàng)建臨時(shí)文件:
1:char buffer[10], *c; tmpnam(buffer); // 傳遞指針
2:c=tmpnam(NULL); //傳遞空指針
一定要?jiǎng)h除臨時(shí)文件。remove(buffer); remove(c);
第17天:操縱字符串
-
確定長(zhǎng)度
size_t strlen(char *str);返回字符串長(zhǎng)度,不包含空字符。
size_t表示unsigned的型。 -
復(fù)制字符串
char *strcpy(char *destination, const char *source);,必須分配空間。
char *strncpy(char *destination, const char *source, size_t n);,復(fù)制前n個(gè)字符。小于n加上空字符,多于n不加空字符。
char *strdup(char *source);不是ANSI標(biāo)準(zhǔn)函數(shù),調(diào)用時(shí)自動(dòng)調(diào)用malloc分配內(nèi)存。 -
拼接字符串
char *strcat(char *str1, const char *str2);
char *strncat(char *str1, const char *str2, size_t n); -
比較字符串
int strcmp(const char *str1, const char *str2);比較兩字符串直到出現(xiàn)不相同字符,相等0,小于<0,大于>0.
int strcmp(const char *str1, const char *str2, size_t n);,一直比較直到不相等字符或比較了n個(gè)字符。 -
查找字符串
strchr(),strrchr()。查找字符第一次和最后一次出現(xiàn)的位置。
strcspn(),strspn()。前者在一個(gè)字符串中查找另一個(gè)字符串的字符第一次出現(xiàn)的位置,找不到返回strlen(str1)。后者指出從str1開頭到什么位置之間所有的字符都包含在str2中。
strpbrk(),類似strcspn,不同是返回指針,找不到返回null。
最有用的strstr()。char *strstr(const char *str1, const char *str2);,查找一個(gè)字符串在另一個(gè)字符串中第一次出現(xiàn)的位置。返回位置指針。可以通過(guò)指針減法獲得離str1開頭的距離。
-
字符串轉(zhuǎn)換
char *strlwr(char *str); char *strupr(char *str);
分別為將大寫轉(zhuǎn)換為小寫,和小寫->大寫。
不是ANSI標(biāo)準(zhǔn)。 -
其他字符串函數(shù)
char *strrev(char *str); 逆序
char *strset(char *str, int ch); 空字符外全改為ch.
char *strnset(char *str, int ch, size_t n); 前n個(gè)改為ch. -
字符串轉(zhuǎn)數(shù)字
atoi,atol,atof -
字符檢測(cè)函數(shù)
isxxxx() ctype.h中定義的宏
ANSI對(duì)大小寫轉(zhuǎn)換的支持:toupper(),tolower()。一次處理一個(gè)字符,利用返回值。ch = toupper(ch);而不是toupper(ch);
總結(jié)
這些庫(kù)函數(shù),返回指針的失敗后返回NULL,返回int的失敗返回EOF(-1),成功返回0。
做練習(xí)時(shí)寫了一個(gè)查找文本中某字符串出現(xiàn)次數(shù)及出現(xiàn)在第幾行的程序,感覺挺有趣。。
第18天:有關(guān)函數(shù)的高級(jí)主題
將指針傳遞給函數(shù)
傳遞方式有兩種:按值傳遞和按引用傳遞。
前者不能修改參數(shù)值,通過(guò)返回值也只能修改一個(gè)參數(shù)。
后者可以修改多個(gè)參數(shù)的值。定義時(shí)需要用*解除引用。
void類型的指針
指向任何對(duì)象,解除引用之前需要強(qiáng)制類型轉(zhuǎn)換。
無(wú)法算數(shù)運(yùn)算。
可變數(shù)目函數(shù)
頭文件stdarg.h
宏:va_list定義存取各參數(shù)的指針arg_ptr。
va_start():初始化參數(shù)列表,將指針和最后一個(gè)固定參數(shù)傳遞給他。
va_arg():依次取回各個(gè)參數(shù),并將指針指向下一個(gè)參數(shù),接受arg_ptr和下一個(gè)參數(shù)的類型。
va_end():清理。
至少要有一個(gè)固定參數(shù)。
返回指針的函數(shù)
不要和函數(shù)指針弄混。
第19天:函數(shù)庫(kù)
數(shù)學(xué)函數(shù)
頭文件math.h
三角函數(shù)sin(), cos(), tan(), asin(), acos(), atan(), atan2();
指數(shù)對(duì)數(shù)exp(), log(), log10();
雙曲cosh(), sinh(), tanh();
求根sqrt()取整ceil(), floor();x的y次方pow(x, y);返回x/y的余數(shù)fmod(x, y);
所有數(shù)學(xué)函數(shù) 返回值為double類型。
時(shí)間函數(shù)
頭文件time.h
兩種表示時(shí)間的方式:
第一是記錄從1970年1月1日午夜開始過(guò)去的秒數(shù),負(fù)數(shù)表示之前的某時(shí)刻。
第二是將其劃分為年月日等,使用結(jié)構(gòu)tm。
處理時(shí)間
時(shí)間函數(shù)的time_t和clock_t均為longlong型。
-
time_t time(time_t, *timeptr);返回自1970年的秒數(shù)。
兩種調(diào)用形式:now = time(0); 或 time(&now);
-
struct tm *localtime(time_t *ptr); time_t mktime(struct tm *ntime);
前者將time_t轉(zhuǎn)換成tm結(jié)構(gòu),后者將tm結(jié)構(gòu)轉(zhuǎn)換成time_t。 -
clock_t clock(void);返回程序從開始執(zhí)行起過(guò)去的時(shí)間。
-
顯示時(shí)間char *asctime(struct tm *ptr);、 char *ctime(time_t *ptr);以及size_t strftime(char *s, size_t max, char *fmt, struct tm *ptr);
第一個(gè)接受tm結(jié)構(gòu)作為參數(shù),第二個(gè)結(jié)構(gòu)time_t變量作為參數(shù),都返回如下格式:
Thu Jun 13 10:22:23 1991
第三個(gè)可以用格式化字符串的方式對(duì)時(shí)間的格式進(jìn)行格式化,常見的:
%a,%A縮寫星期及完整星期。%b,%B縮寫月份及完整月份
%D相當(dāng)于%m/%d/%y。%F相當(dāng)于%Y-%m-%d
// 上面試了下不對(duì)。。
H, M, S24小時(shí)制的時(shí)分秒。%T相當(dāng)于%H:%M:%S
%U0-53表示的星期。%x日期如30-Jun-91。%X時(shí)間如10:41:50。
strftime(buf1, 80, "the data is %D, week %U of year %Y.", ptr);
put(buf1);
處理錯(cuò)誤
void assert(int expression);
如果expression為錯(cuò),則assert強(qiáng)行終止程序。
可以定義#define NDEBUG使assert處于關(guān)閉狀態(tài)。
errno.h頭文件
需要配合void perror(const char *msg);使用。
查找和排序
void qsort(void *base, size_t num, size_t size, int (* cmp)(const void *element1, const void *element2));
void besearch(const void *key, void *base, size_t num, size_t width, int (* cmp)(const void *element1, const void *element2));
前者升序排序,后者以二分法查找鍵值(要求數(shù)組升序)。
key指向要查找的數(shù)據(jù)項(xiàng),base指向數(shù)組第一個(gè)元素,num是數(shù)組元素?cái)?shù),width(size)是每個(gè)元素的長(zhǎng)度,cmp是一個(gè)指向比較函數(shù)的指針。比較函數(shù)滿足:大于返回大于0,等于返回0,小于返回小于0。
第20天:管理內(nèi)存
類型轉(zhuǎn)換
自動(dòng)轉(zhuǎn)換
-
計(jì)算表達(dá)式時(shí)類型提升
char<short<int<long<long long<float<double<long double -
賦值時(shí)轉(zhuǎn)換
int i; float f; f = i;時(shí)i被提示為float。
顯示轉(zhuǎn)換
- 強(qiáng)制轉(zhuǎn)換算數(shù)表達(dá)式
int i1 = 100, i2 = 40;
f1 = (float)i1 / i2;
計(jì)算時(shí)先把i1強(qiáng)制轉(zhuǎn)換,i2會(huì)自動(dòng)提升。 - 裝換指針
對(duì)void解除引用時(shí)必須將其轉(zhuǎn)換為相應(yīng)的類型。
分配內(nèi)存空間
-
void *malloc(size_t num);
-
void *calloc(size_t num, size_t size);
上面兩個(gè)類似,calloc分配時(shí)會(huì)自動(dòng)把內(nèi)存內(nèi)容設(shè)置為0。前者num指字節(jié)數(shù),后者num為對(duì)象數(shù),size為對(duì)象的大小(字節(jié))。 -
void *realloc(void *ptr, size_t size);
改變分配內(nèi)存的大小,ptr為原來(lái)內(nèi)存的指針,size為新的大小。
ptr為NULL,則類似于malloc()。size為0,則釋放內(nèi)存,返回NULL。 -
free()釋放內(nèi)存
void free(void *ptr);
操縱內(nèi)存塊
-
void *memset(void *dest, int c, size_t count);
將指定的字節(jié)設(shè)置成特定的值。
dest指向要初始化的內(nèi)存,c被視為char(0-255有效),count指定字節(jié)數(shù)。
例如:memset(message + 2, '#', 10); -
memcpy, void *memmove(void dest, void *src, size_t count);
復(fù)制數(shù)據(jù)。
前者處理重疊部分時(shí)會(huì)出錯(cuò),因此可以完全使用后者。例如 :
memmove(temp + 6, temp + 4, 10);
位的用法
移位運(yùn)算
y = x << 1;
y = x >> 1;
分別為左移右移,存在溢出現(xiàn)象。
按位邏輯運(yùn)算符
雙目運(yùn)算符。
&, |, ^分別為:按位與,或,異或。
求補(bǔ)(~):按位取反
結(jié)構(gòu)中的位字段
節(jié)省內(nèi)存空間,c中最小的數(shù)據(jù)類型為char,1字節(jié),存儲(chǔ)只需1-7位的數(shù)據(jù)時(shí)浪費(fèi)內(nèi)存。
位字段必須放在結(jié)構(gòu)定義的最前面。賦值時(shí)采用符號(hào)變量TRUE、FALSE等最好,超過(guò)范圍時(shí)結(jié)果不可預(yù)測(cè)。
- 將輸入轉(zhuǎn)換為二進(jìn)制時(shí),可以按位運(yùn)算或者不斷除以2求余。
第21天:編譯器的高級(jí)用法
多文件編譯
之前知乎抄的vscode配置
https://www.cnblogs.com/esllovesn/p/10012653.html這個(gè)是簡(jiǎn)化版也是更方便版的,建議直接看這個(gè)。
https://www.zhihu.com/question/30315894這個(gè)是知乎原文,比較復(fù)雜。
進(jìn)行少量的多文件編譯,C語(yǔ)言直接用gcc 源文件1.c 源文件2.c 頭文件1.h這樣就好,C++用g++。默認(rèn)生成a.exe,加-o可指定輸出文件名,其余選項(xiàng)百度gcc使用教程。如果需要多次編譯可以寫一個(gè)批處理。
- 進(jìn)行大量的多文件編譯,請(qǐng)學(xué)習(xí)如何寫makefile或使用cmake。然后把tasks的命令改成調(diào)用make等。如果你想使用別人的庫(kù),比如ffmpeg,可能需要在命令中指定-I、-l(小寫的L)、-L。具體參數(shù)閱讀那個(gè)庫(kù)的文檔。還可能需要把路徑添加到c_cpp_properties.json和compile_flags.txt里來(lái)配置Intellisense。這些情況下可以考慮單獨(dú)建一個(gè)工作區(qū),不要和單文件編譯的共用。其實(shí)不新建工程(Project)、只是單文件就能調(diào)試,是不利于以后使用和理解大型IDE的。不過(guò)初學(xué)也不用掌握那么多,不要覺得建工程很麻煩、不建工程就能編譯很強(qiáng)就是了。總之這些和VSC無(wú)關(guān),用其它IDE或是手動(dòng)編譯也會(huì)遇到差不多的問(wèn)題,也有點(diǎn)復(fù)雜。
入門水平,不太懂后面的意思,使用第一種少量多文件編譯的方法。
.c文件–>預(yù)處理–>.i文件–>編譯–>.s文件–>匯編–>.o文件–>鏈接–>.exe文件
gcc -E (E大寫)預(yù)處理(preprocessing)。不會(huì)檢查語(yǔ)法錯(cuò)誤。win10需要使用重新定向生成.i文件gcc -E test.c > test.i或者指定文件名gcc -E test.c -o test.i,否則只會(huì)輸出到stdout。
gcc -S(S大寫)編譯(compiling)。將.i文件編譯成.s文件,即使不指定文件名也會(huì)生成與.i文件同名的.s文件,即gcc -S test.i -o test.s與gcc -S test.i相同。并且檢查語(yǔ)法錯(cuò)誤是在編譯過(guò)程進(jìn)行的。
gcc -c(c小寫)匯編(assembling),將.s文件轉(zhuǎn)換成機(jī)器能執(zhí)行的機(jī)器代碼.o文件,同樣即使不指定文件名也會(huì)生成與.s文件同名的.o文件,gcc -c test.s -o test.o與gcc -c test.s相同。
gcc x.o或gcc x.o -o x.exe鏈接(linking)將機(jī)器碼.o文件連接成可執(zhí)行文件.exe。
模塊化編程
通用指導(dǎo)原則:
- 輔助模塊(secondary module)應(yīng)包含通用的函數(shù),常見的做法是,對(duì)每一類函數(shù),創(chuàng)建一個(gè)輔助模塊。例如:鍵盤函數(shù)放在keyboard.c中,屏幕顯示函數(shù)放在screen.c中等。
- 主模塊應(yīng)包含main()和其他程序特有的函數(shù)。
- 每個(gè) 輔助模塊都有一個(gè)頭文件,名稱應(yīng)與相應(yīng)的模塊名相同,擴(kuò)展名為.h。應(yīng)包含(1).輔助模塊中的函數(shù)的原型。(2).定義模塊使用的宏和符號(hào)常量的#define編譯指令。(3).模塊使用的結(jié)構(gòu)和外部變量的定義。
- 為防止頭文件被包含多次,可以使用預(yù)處理器來(lái)實(shí)現(xiàn)有條件的編譯。
外部變量
外部變量在其所屬的整個(gè)源代碼文件中可見,但在其他模塊中不會(huì)自動(dòng)可見。需要使用關(guān)鍵字extern來(lái)聲明。
主模塊中float interest_rate;輔助模塊extern float interest_rate;
.o文件
如果源代碼文件已被編譯為.o文件,則應(yīng)僅當(dāng)修改該文件時(shí)才重新編譯,否則應(yīng)盡量使用.o文件,gcc database.c keyboard.c
生成工具
應(yīng)該就是上面的makefile和cmake。可以檢查源代碼和目標(biāo)文件的時(shí)間和日期郵戳,并根據(jù)定義的依存性,指示編譯器只重新編譯那些依賴于被修改的文件的文件。避免了不必要的編譯,最大化效率。
適合于大型工程。
預(yù)處理器
gcc -E進(jìn)行預(yù)處理,根據(jù)源代碼的指令(預(yù)處理器編譯指令)對(duì)源代碼進(jìn)行修改。
#define
替換宏
#define text1 text2
預(yù)編譯器把源代碼文件中所有的text1替換為text2。如果text1位于雙引號(hào)中,則不替換。不會(huì)修改源代碼文件。
函數(shù)宏
函數(shù)宏的優(yōu)點(diǎn):對(duì)參數(shù)類型不敏感,可以將任何數(shù)值類型的變量傳遞給接受數(shù)值參數(shù)的函數(shù)宏。
#define HALFOF(value) ((value) / 2)
#define LARGER(x, y) ((x) > (y) ? (x) : (y))
宏定義時(shí),宏名后必須緊跟左圓括號(hào),不能有空白。并且替換字符串中每個(gè)參數(shù)都要有圓括號(hào)括起。例如:
#define SQUARE(x) (x) * (x)而不是#define SQUARE (x) x*x
替換字符串中,如果宏參數(shù)前面有#字符串化運(yùn)算符,展開宏時(shí),該參數(shù)將被轉(zhuǎn)換成被引號(hào)括起來(lái)的字符串。例如:
#define OUT(x) printf(#x)
OUT(Hello);被展開為printf("Hello");
#運(yùn)算符在轉(zhuǎn)換時(shí)會(huì)考慮特殊字符,需要轉(zhuǎn)義時(shí)加\。
OUT("HELLO");---->printf("\"HELLO\"");
##拼接運(yùn)算符,#define CHOP(x) func ## x,將兩個(gè)字符串拼接,不會(huì)加引號(hào),也不對(duì)轉(zhuǎn)義字符做處理,主要用于修改源代碼。salad = CHOP(3) (q, w);—>func3 (q, w);使用##決定了調(diào)用哪個(gè)函數(shù)。
宏和函數(shù)
宏速度快,函數(shù)代碼長(zhǎng)度短。
查看宏展開
gcc -E *.c -o *.i
#include
不可以包含一組,但可以嵌套,被包含的文件可以#include,ANSI最多15層。
尖括號(hào)括起#incldue <stdio.h>在標(biāo)準(zhǔn)目錄查找。雙引號(hào)括起#include "myfile.h"在源代碼文件中查找。也可以在雙引號(hào)中指定路徑。
#if、#elif、#else和#endif
有條件控制編譯,無(wú)需用花括號(hào),似乎用處很大 ,例如:
#if ENGLAND == 1 # include "england.h" #elif FRANCE == 1 # include "france.h" #elif ITALY == 1 #include "italy.h" #else #include "usa.h" #endif還可以幫助調(diào)試:
#if DEBUG == 1
debugging code here
#endif
defined(),檢查是否被定義。用來(lái)避免包含多次頭文件。
#if defined(prog_h) /* already included */ #else #define prog_h /* header file information goes here */ #endifundef(),撤銷定義。
預(yù)定義的宏
__DATE__、__TIME__、__LINE__、__FILE__
預(yù)處理時(shí),__DATE__、__TIME__為編譯時(shí)的日期和時(shí)間。__LINE__被替換為行號(hào),__FILE__替換為文件名,后兩個(gè)最適合于調(diào)試程序或處理錯(cuò)誤。
printf(" Program %s: (%d) Error opening file ", __FILE__, __LINE__);
使用命令行參數(shù)
main(int argc, char *argv[]) // char **argv也行 {/*...*/ }argc表示有多少個(gè)命令行參數(shù),argv的長(zhǎng)度為argc,argv[0]指向包括路徑信息的文件名,argv[1]指向程序名后的第一個(gè)參數(shù)。也可以用其他名,傳統(tǒng)這兩個(gè)。
e:>program smith "little pony"
參數(shù)含有空格,則雙引號(hào)括起。
- 一定要檢驗(yàn)用戶輸入的命令行參數(shù)
總結(jié)
- 模塊化編程
- 預(yù)處理編譯指令,#define #include #if #elif #else #endif #undef
- 函數(shù)宏
- 命令行參數(shù)。main(int argc, char **argv[])
總結(jié)
以上是生活随笔為你收集整理的21天学通c语言总结(3)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 五大板块(2)—— 指针
- 下一篇: 五大板块(3)—— 结构体