C語言的語法較其他語言來說比較復雜。這裡舉幾個我碰到過的例子,證之。
例子1
int a[10];
printf( "%d-%d", a, & a);
猜測:這裡的&會被編譯器忽略。a在內存中是不存在的,內存中的是a[0] a[1]....a[9],上述printf打印的a在編譯的時候就被替換成了a在內存中的地址。因為a是概念上的,而非內存中的,所以自然不存在地址,也就無法對它使用取址符。但是編譯器很聰明,會忽略&。
例子2
struct a{
void (*func)();
};
聲明了一個結構體,含有一個函數指針成員。這種語法不太好看,一般這麼寫:
typedef void (*lpFuncType)(); // 定義func類型
struct a{
lpFuncType func;
}; // 更像C語言的聲明語句
如果上面看懂了,再看看一個標准庫函數的聲明(signal.h文件中):
void ( *signal( int signal, void (* func) (int)) ) (int);
相信你可以看懂了。不過最好還是這麼寫,以便於別人閱讀你的程序:
typedef void (*funcType)(int);
funcType signal( int signal, funcType func);
例子3
下面的輸出是什麼呢?
#include<stdio.h>
int a[2][5] = { 0,1,2,3,4,
5,6,7,8,9};
void f( int ** b){
printf( "%d\n", b[1][3]);
}
int main(){
f(a);
return 0;
}
答案是會報段錯誤。解釋如下:
先看看一個更容易理解的版本,當然二者是等價的。
#include<stdio.h>
int a[2][5] = { 0,1,2,3,4,
5,6,7,8,9};
void f( (int*) b[]){
printf( "%d\n", (b[1])[3]);
}
int main(){
f(a);
return 0;
}
函數f的參數是一個一維數組,其中存放的是一個int指針。b[1]就是取出a中的第二個數字,所以是1,。因為這是指針,所以指向內存地址1處;又它是int指針,所以指向的是int數組,那麼其中第四個數的地址就是( 1 + 3 * sizeof( int)),也就是13。當然,這是一個非法地址,所以會報段錯誤,或者內存訪問違例。
那麼二維數組怎麼寫呢?
#include<stdio.h>
int a[2][5] = { 0,1,2,3,4,
5,6,7,8,9};
void f( int (*b)[5]){
printf( "%d\n", b[1][3]);
}
int main(){
f(a);
return 0;
}
這樣就可以正確訪問到8這個值了。可以這麼理解f的參數的類型:
void f( int[5] b[]);
當然,也只是理解而已。這個問題是復習編譯原理考試的時候遇到的,考了上面的源碼,答案給的也是錯誤的(因為不相信答案,所以自己在VS上試了一下)。正確的理解應當是從編譯器翻譯的角度來理解,也就是如何計算多維指針地址之類的規定。無論如何,為了避免出錯和給他人帶來誤解,實際中還是盡量避免復雜的語法吧 ^_^ 享受簡單。