C Primer Plus(三)

重讀C Primer Plus ,查漏補缺

  重讀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; // 不能夠超過字段所佔用的位可表示的上限

  

C預處理器和C庫

  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分,首次完整讀完這本書,共勉。

  學如逆水行舟,不進則退;心似平原放馬,易縱難收。

相關文章
相關標籤/搜索