C專家編程 筆記

來源:互聯網
上載者:User

C語言中的符號重載

C語言非常的簡潔, 以至於不願意用太多的符號, 這樣有很多符號在不同的地方有不同的含義

這樣會讓使用者很困惑, 這是c的語言特性, 也是設計上的一些失誤

static   
在函數內部,表示該變數的值在各個調用間一直保持延續性;
對於函數,表示該函數只在本檔案中可見

extern

用於變數,表示該變數在其它地方定義;
用於函數定義, 表示全域可見(屬於冗餘的)

void    
用於參數列表中,表示該函數參數為空白,如int main(void);
用於傳回值,表示該函數返回void,即不返回任何值,相當與pascal中的過程;
在指標聲明中,表示通用指標

*
乘法運算子;
用於指標前,表示對指標所指內容的間接引用;
用於聲明中表示指標,如int *pi,表示指向整形的指標,char *strcpy(...)表示函數的傳回值為指標

&     
位與運算子;
取地址操作符

()
函數定義中,包圍函數形式參數表,如int main(int argc, char **argv);
調用一個函數,如srand();
改變運算式的運算順序,如 a * ( b + c );
類型轉換, 如 (int*)x;
定義帶參數的宏,如 #define MAX(a, b) ((a) > (b)) ? (a) : (b)
包圍sizeof操作符的運算元(如果是類型名),如 sizeof(int)

 

數組和指標的本質區別

C編程新手最常聽到的一句話是'數組和指標是相同的', 不幸的是, 這是一種危險的說法, 他並不正確.

最典型的一個錯誤是

file 1:
int mango[100];
file 2:
extern int *mango;

這樣的聲明是錯的, 必須聲明為extern int  mango[];

關於聲明的意義在上篇講解extern時已經詳細說明過

 

我們首先要區分"地址y"和"地址y的內容"之間的區別, 這是一個相對微妙之處, 因為大多數程式設計語言中我們用同一個符號來表示兩樣東西, 並由編譯器根據上下文環境來判斷它的具體含義.

int X, Y;

X = Y;

這邊我們就來深入討論一下這個指派陳述式, 看上去X和Y是同一種東西, 都是int型變數

但對於編譯器而言, 一個指派陳述式兩邊的東西是不一樣的 (以彙編的角度思考)

左值  =  右值;

左值是地址, 右值是資料, 是有本質區別的

所以上面的X是X代表的地址, Y是Y代表的地址裡面的內容

所以對於編譯器而言,

X=2, 這個很直接, 把2放到X代表的地址上

X=Y, 需要先從Y代表的地址上讀出資料, 再放到X代表的地址上

需要注意, 左值是在編譯時間就可以明確知道的, 因為編譯器會為每個變數分配一個地址

而右值往往只有在運行時才可知的

 

那麼下面詳細看看為什麼大家會誤認為兩者相同

int a[10], *p, *q;
p = a ;
q = p+1 ;

q = a+1;

int i;

i = a[0];

i = (*a);

i = (*p);

如上的代碼, 你發現可以象操作指標一樣來運算元組變數名a, 貌似a和指標p時等價的

其實非也, a其實代表數組的首地址, 就是個常量, 進行q = a+1, 類似於X = 2+1

而p是指標變數, p=a後, p代表的地址裡存放了a,即數組首地址

那麼q = p+1, 需要從p代表的地址中讀出a, 並放到q代表的地址中去, 類似於X = Y+1

同樣對於*操作, 是取地址上的內容, *a直接取a地址上的內容, 而*p是要先從p代表的地址取出地址資料, 再取該地址資料上的內容

而且兩者還有一個區別是時間上的差別

對於q = a+1, 因為a代表常量, 在編譯時間就可以算出q的值

而對於q = p+1, p是變數, 必須到運行時才能知道q的值

 

兩者是有本質區別的, a代表常量, 有人把它稱為'常量指標', 是不能賦值或改變的指標, 這絕對的是誤導

a就不是指標, 根本就不能作為左值, 你有見把2作為左值的嗎

此處可以認為a等同於&p

之所以大家會混淆, 完全是因為c語言的設計不規範導致的, 相同的語句卻有著完全不同的操作

 

此處個人覺得是數組的設計欠缺妥當

int i, * p;

對於一般的變數, 無論是i還是p, 作為左值表示變數名所代表的地址, 作為右值表示變數名所代表的地址上存放的資料. 其實i和p從這個層面上來講, 完全沒有什麼分別, 只不過p中存放的資料是地址而不是其他.

 

但是對於int a[];

就不一樣了, 因為a只是作為一個資料集合的開始位置, a無法作為左值(設計者認為這樣的不恰當). 同時a作為右值時, 也無法取a地址上的資料, 因為它只是個開始位置, 你不知道取多少. 所以設計者打破了普通變數的規則, 把a當作一個地址常量來看待, 即當a作為右值時, 直接取a代表的地址作為右值, 而不會象通常變數一樣去取地址上的資料作為右值.

問題是, 根據c的文法, 其在使用時和指標又十分相似, 這樣的設計是不嚴謹的. 其實如果這樣設計會穩妥些,

將a[]作為一個整體, 不能單獨對a做任何操作, 如需取地址則用&(a[])

不過這樣應該不符合c語言的簡單美學...c語言是靈活, 簡單的語言, 但是很多設計太隨意, 不夠嚴謹, 所以導致很多地方不符合邏輯, 很難理解

 

再論數組

本書和很多講解數組和指標的文章過於複雜, 我個人覺得那是把簡單的問題複雜化了.

我在上面應該已經把數組和指標的關係講清楚了, 下面補充幾點

1. 書中指出數組和指標在作為函數參數時是一樣的(equivalent), 這個說法來自"C Programming Language, 2nd Ed, Kernighan & Ritchie"

如下,

char my_array[10];
char * my_ptr;
...
i = strlen( my_array );
j = strlen( my_ptr );

在c語言中, 作為函數的參數都是傳值的, 但是數組卻是個例外, 確實對於大數組, 傳值明顯是相當低效的

所以當你自己把數組作為函數參數的時候, 編譯器其實是產生一個指向數組首地址的指標, 並把這個指標作為參數傳入函數.

其實如果想把整個數組傳值傳入函數, 象前面說的, 只要把數組封裝在結構中, 即可, 不過不推薦這樣, 甚至在把結構體對象當作參數的時候, 要check結構體內是否有數組, 如果有數組, 最好把地址作為參數.

所以在作為函數參數時, 數組首地址會被轉化為指標作為參數, 這個只是編譯器出於方便統一這樣處理.

在這點上說兩者equivalent, 我個人覺得是不妥當的

 

2. 書中說"運算式中的數組名就是指標", 這個說法是相當危險的, 會把剛有些明白的程式員再次繞暈

int a[100];

int *p;

p = a+1;

書中的意思是, 這個操作中, 編譯器出於處理的方便, 會先把a封裝成一個指標, 然後賦值給p, 所以可以把a看作是指標, 這個說法很不負責

編譯器可以出於處理方便的需要把a封裝成指標, 但是數組名a絕對不是指標, 如果a是指標, 就應該可以作為左值進行賦值, a = p

實際上是不行的, a就是地址常量

 

3. 數組中的[]符號就代表地址位移

在編譯的時候, a[i] 會被改寫成 *(a+i)

對於+是沒有前後順序的

所以你這樣寫, i[a], 也是可以的, 因為在編譯時間, 也是被改寫成 *(i+a)

所以雖然看著很bt, 但是其實效果是一樣的, 這就是[]的真正意義.

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.