終於把這本被稱為C語言聖經的書看完了。書的封面如下:
摘抄筆記如下:
page 16
EOF值為-1,所以char類型是無法存放該值的,需要用int類型。聯想到我們常寫的
while(scanf("%d",&a)!=EOF)其實scanf是一個int類型的函數,在類型匹配的時候傳回值為1,在類型不符的時候傳回值為0,在沒有資料讀入的時候傳回值為-1,這和EOF是一致的。
page53
x=f()+g(),該語句在編譯的時候其實不一定是從左至右執行的,具體情況視環境而定
printf("%d %d",++n,power(n,2))這樣的寫法就不是很正確
page84
局部變數問題,就算在if裡面也算是局部變數,例如下面就是正確的
int x;int y;f(double x){ double y;}
page90
宏替換,如果在替換文本中參數名以#作為首碼,那麼它們將被由實際變元替換的參數擴充成帶引號的字串。
#define dprint(expr) printf(#expr"=%g\n",expr)
使用時dprint(x/y)
宏被替換為printf("x/y" "=%g\n",x/y) 也就是printf("x/y=%g\n",x/y)
##是一種串連實際變元的手段,替換文本參數用此串連,##與前後的空白符被刪除
#define paste(front,back) front## back
paste(name,1) 建立name1
page115
main(int argc,char *agrv[])
main中的這兩個參數在使用命令列的情況下使用,第一個參數的值為程式執行時命令列中變元的數目,第二個變元為一個指向字串數組的指標,其中每個字串對應一個變元。
例echo hello,world 將列印出hello,world
#include<stdio.h>main(int argc,char *argv[]){while(--agrc>0)printf("%s%s",argv[i],(i<argc-1)?" ":"");printf("\n"); return 0;}
page138
千萬不要以為一個結構的大小等於它各成員大小的和,因為對不同對象有不同對齊需求,所以結構中會出現洞。例如
struct {char c;int i;}
需要八個位元組而不是五個位元組,sizeof[注意sizeof是運算子不是函數]運算子返回結構大小的正確值
page147
union中所有的元素都位於同一地區,保證地區大小能夠容納擁有最大位元組類型的元素,從某種意義上來說,聯合也是一個結構,只不過是其所有成員相對於基地址的位移量為0。
補充:聯合的作用
C中的union和struct的區別,為什麼要發明union?
union和struct的區別在於:
1、對於一個union類型的變數,所有成員變數共用一塊記憶體,該記憶體的大小有這些成員變數中長度最大的一個來決定
2、union分配的記憶體是連續的,這一點非常重要。
更具體的看這裡,還有測試代碼呢(附件1):
那為什麼要發明union呢?這是由於union的特性決定的,特別是上述的第二點,因為struct中成員變數雖然記憶體都是獨立的,但是struct不能保證分配的記憶體是連續的。
舉個例子:CPU的ax寄存器分為ah和al兩部分,我們現在要給其賦值,就可以採用union:
union _ax_register { int i; // 4Bytes, union將按此分配一個4位元組的連續記憶體 unsigned short ah; // 2 Bytes unsigned short al; // 2 Bytes } ax_register;
現在,給ax_register.i賦值,其高16bit就會賦給ah,低16bit就會賦給al,是不是很方便呢?
再舉個例子,IP地址,我們使用union,就可以直接給他賦一個int值,就能給IP的四個段同時賦值,如果用struct,就麻煩多嘍,如下:
union _ip_address { int i; // spaceholder unsigned char ip_first_num; unsigned char ip_second_num; unsigned char ip_third_num; unsigned char ip_fourth_num; } ip_address;
這樣,給ip_address.i 賦值,就自動將其8bit 的賦給了四個IP欄位。是不是很方便?更方便的是,現在要比較兩個IP地址的大小,只要比較這個int值就OK了。原因就在於它們共用同一片記憶體。union的壞處是原先的資料很容易被後寫入的資料抹去,所以不宜用來進行資料處理。一般用來儲存資料就行了。
page147
位欄位,個人感覺是用來儲存某個資料的資訊的,這裡把某個資料的是否是關鍵字,是否是靜態變數,是否是全域變數進列欄位壓縮儲存。即程式中每個標識符都有與其關聯的特定資訊,類似於電腦網路中的掩碼。
struct {unsigned int is_keyword:1;unsigned int is_extern :1;unsigned int is_static:1;}flags;
這裡定義一個變數flags,只包含三個一位欄位。flags.is_exteern=1;可以直接這樣進行賦值
第七章輸入輸出 覺得沒有太大用,粗略瀏覽
總結:大一學過C語言,大二學過C++,所以這本書對我來說可能沒有別人說的那麼神奇,但是我還是學到了一點東西。第七章大致看了一點,第八章和後面的附錄沒看,習題也沒做幾道,希望以後有時間再讀。
最後說一個問題,今天在實驗室討論的很激烈:
#include <stdio.h>int main(){const int a=4;int *b=(int *)(&a);printf("%d\n",&a);printf("%d\n",b);*b=10;printf("%d\n",a);printf("%d\n",*b);return 0;}
說明如下:a是const類型,b是指向a地址的指標,首先輸出地址,然後把b的值給改變了,再輸入a和*b的值。如果按照常理推斷,*b和a表示的是同一個記憶體,所以改變*b也會改變a,但是結果呢?
輸出如下:
可以看到a的值並沒有改變,這與const類型有關,去掉const後輸出是按照常理推斷的10和10。
究竟為什麼呢?
按照反組譯碼後,發現編譯器發現a是const後直接把4傳給了printf根本沒有從記憶體位址中去取值,所以導致輸出不一樣。看來又學到點東西了,討論是一種協助人進步的方式。