重讀C Primer Plus,記錄遺漏的、未掌握的、不清楚的知識點html
一、fgets函數在讀取文件內容時會將換行符讀入,但gets不會,fputs函數在寫入文件時不會追加一個換行符,但puts會,應該對應配合使用。數組
二、不一樣操做系統下,以文本方式打開文件,幾乎沒有區別,但因爲不一樣操做系統文件結尾的的標識符不一樣,以二進制方式打開時,可能會將結尾標識符錯誤輸出。安全
三、對於大型文件,有兩個特殊的函數提供支持:數據結構
1 int fgetpos(FILE * restrict stream, fpos_t * restrict pos); 2 int fsetpos(FILE * stream, const fpos_t *pos);
其中,fpos_t是經過其餘類型定義的文件定位類型,在使用上述函數時,fsetpos中的pos必須是經過fgetpos函數得到的。當兩個函數執行成功時,會返回0。函數
四、其餘標準IO函數flex
1 size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE* restrict fp); 2 size_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE* restrict fp); 3 // 是否到達文件結尾 4 int feof(FILE* fp); 5 // 是否發生讀寫錯誤 6 int ferror(FILE* fp); 7 // 將字符迴流進緩衝區 8 int ungetc(int c, FILE* fp) 9 // 馬上將緩衝區內容寫入文件 10 int fflush(FILE* fp) 11 // 替換緩衝區 12 int setvbuf(FILE* restrict fp, char * restrict buf, int mode, size_t size)
固然,上述的一些函數在目前的VS Studio中會被認爲是不安全的函數,已通過時。spa
五、C99標準下支持對結構體初始化時的任意字段賦值:操作系統
1 struct book gift = {.value=25.99, .author="Harry Potter", .title="Yoo"}; 2 // 此時 0.25 會被賦給定義結構體時author後的那個成員,即使那個成員已經被初始化過。 3 struct book gift = {.value=25.99, .author="Harry Potter", 0.25};
六、對於結構體數組,數組名不是其首個元素的地址,須要引用首個元素再取地址。翻譯
七、在結構中通常使用字符數組,而不使用字符指針,結構中的字符指針沒法很好的初始化地址,這樣會有使用上的風險,因此結構中的字符指針最好只指向那些字符串常量或者是指向由malloc分配的內存。指針
八、C99標準對結構也支持複合文字,同時複合文字的結構也能夠做爲函數參數,也能夠取地址,也和普通變量有相同的生存週期,聲明方式以下:
1 (struct book) {"The Idiot", "Fyodor Dostoyevsky", 6.99}
九、C99支持一種伸縮型數組成員,這個成員必須是結構中最後一個成員,並且不是惟一一個成員,就像聲明普通數組同樣,但括號內爲空,這個成員不會在聲明後當即存在,實際上,C99但願使用malloc爲這樣含有伸縮型成員的數組分配空間。
1 struct flex{ 2 int count; 3 double avreage; 4 double scores[]; // 伸縮型成員 5 } 6 struct flex * pf; 7 pf = malloc(sizeof(struct flex) + 5*sizeof(double)) 8 pf->count = 5; 9 pf->scores[2] = 2.99;
十、對於C中的枚舉類型,某些屬性不能順延至C++,例如C容許對枚舉作++運算,但C++不容許。
1 enum spectrum {red, yellow, green, blue}; 2 spectrum color; 3 for(color = red; color != blue; color++);
十一、在C中,對於同一做用域下的標記和變量名可使用同一個名字,由於對於標記(枚舉、結構,聯合),他們使用的名字空間與普通變量不一樣,但C++中不能夠,例:
1 struct complex{double x, double y}; 2 int complex; // 在C中不會引發衝突,但C++中則不容許
十二、對於函數指針執行函數時,會出現兩種語法,ANSI C把他們視爲等價的。
1 void ToUpper(char *); 2 void (*pf) (char*); 3 char str[] = "hello"; 4 pf = ToUpper; 5 (*pf)(str); // 語法1 6 pf(str); // 語法2
1三、爲何一個字節能夠表示的有符號整數的範圍是-128~+127?
看這裏:https://www.cnblogs.com/Dylan7/p/12649972.html
1四、計算機中小數是如何表示的?(一部分表示指數,一部分表示小數,有精確度問題)
1五、對位進行操做的第二種方法就是位字段(從沒用過,細節能夠用到時再研究),位字段比如一個結構體,但其中的成員,表明的是某幾位上的值,好處是避免了經過複雜的位運算去控制某些位上的值,聲明例如:
1 struct box 2 { 3 unsigned int opaque :1 // 總體結構的對齊補齊依據無符號整型 4 unsigned int fill_color :3 // 數字表明須要幾位來表示這個字段 5 unsigned int :4 // 能夠跳過一些位 6 unsigned int show_border :1 // 但一個字段不能橫跨兩個無符號整型的邊界 7 } 8 struct box b; 9 b.fill_color = 7; // 不能夠超過字段所佔用的位可表示的上限
1六、程序翻譯的第一步,在預處理前,編譯器會對代碼作一些翻譯,將代碼中出現的字符映射到源字符集(用來處理多字節字符和使C外觀更加國際化的三元字符擴展),接着查找反斜槓後緊跟換行符的實例,將其轉換爲一行,而後將文本劃分爲預處理的語言符號序列以及空白字符及註釋序列(將用一個空格代替一個註釋),最後進入預處理階段,尋找每個預處理指令。
1七、 幾個宏定義
1 #define F(x) #x // #將語言符號字符串化 2 #define F(x) F##x // ##將兩個語言符號組成一個語言符號 3 #define F(x,...) printf("x", __VA_ARGS__) // ...和__VA_ARGS__,可變參數(必須爲最後一個參數)
1八、#if 指令後面跟常量整數表達式,能夠與 #elif 配合使用,例如:
1 #if 1 == SYS 2 ... 3 #elif 2 == SYS 4 ... 5 #endif
同時,還有如下新的實現方式,defined 是一個預處理運算符,若是參數使用#define定義過,defined返回1,不然返回0。
1 #if defined(INMPC) 2 ... 3 #elif defined(VAX) 4 ... 5 #endif
1九、#line 用於重置__LINE__,__FILE__宏所報告的行數
#error 指令使預處理器能夠發出一條錯誤信息
1 #line 10000 2 #line 10 cool.c" 3 #if __STD_VERSION__ != 199901L 4 #error Not C99 5 #endif
20、C99 提供了_Pragma預處理器運算符,能夠將字符串轉換成常規的編譯指示
1 _Pragma("c99 on") 等價於 2 #pragma c99 on
2一、內聯函數不會在調試器中顯示,例如使用gdb調試時,有些內聯函數沒法被手動執行,同時內聯函數具備內部連接屬性,因此在多文件程序中,使用其餘文件的內聯函數時,要單獨聲明一次,而且在嘗試獲 取內聯函數的地址時,編譯器都會產生非內聯函數,也就是說可能產生外部定義的函數。
2三、在main()函數結束時,會隱式地調用exit()函數,同時,能夠經過atexit()函數,向exit()註冊在程序容許結束時執行的函數,ANSI C保證能夠設置至少32個函數,按照先設置後執行的順序執行,atexit()函數接受一個返回值爲void,參數也爲void的函數指針做爲惟一參數。
2四、memcpy()與memmove()兩個函數的區別在於聲明上,以及memcpy()會假定兩個內存區沒有重疊。
1 void *memcpy(void * restrict s1, const void * restrict s2, size_t n); 2 void *memmove(void *s1, void *s2, size_t n);
2五、可變參數的相關內容包含在stdarg.h頭文件中,使用起來比較複雜,包括初始化可變參數列表,遍歷列表,清理列表,拷貝列表等一系列操做,須要時再研究。
2六、 這章沒什麼新奇內容,但它告訴咱們,用C能夠實現不少複雜的數據結構。
2020年4月16日,星期五,晚23點09分,首次完整讀完這本書,共勉。
學如逆水行舟,不進則退;心似平原放馬,易縱難收。