C語言隱式聲明與GCC內建函數
什麼是C語言的隱式聲明
在C語言中,函數在調用前不一定非要聲明。如果沒有聲明,那麼編譯器會自動按照一種隱式聲明的規則,爲調用函數的C代碼產生彙編代碼。下面是一個例子:
#include <stdio.h>
int main() {
double x = no_declare_func();
return 0;
}
$ gcc -c implicitDeclare2.c
$ gcc implicitDeclare2.o -o implicitDeclare2
implicitDeclare2.o: In function `main':
implicitDeclare2.c:(.text+0xe): undefined reference to `no_declare_func'
collect2: error: ld returned 1 exit status
可以看到上面的代碼沒有定義no_declare_func,但是gcc在編譯的時候並不會報錯,因爲C語言規定,對於沒有聲明的函數,自動使用隱式聲明,也就變成如下代碼(函數的隱式聲明默認爲int型):
#include <stdio.h>
int no_declare_func();
int main() {
double x = no_declare_func();
return 0;
}
在鏈接的時候會報錯,因爲鏈接過程中沒有找到函數no_declare_func的定義。
目前C++是沒有隱式聲明這一說,遇到這種沒有函數聲明的情況,會直接報error。
GCC內建函數
內建函數(built-in),即一個系統或者工具提供的默認就能用的函數,我的理解是默認情況下,gcc在鏈接的時候如果沒有找到定義,就會自動去鏈接庫中對應名稱的函數,gcc的內建函數大多是爲了對代碼進行優化。
gcc內建函數相關的選項如下:
-fbuiltin:
gcc編譯默認選項,默認通過名字來識別內建函數-fno-builtin:
禁用GCC編譯器內建函數。如果在gcc編譯時用了-fno-builtin選項,則除非利用前綴__builtin_進行引用,否則不識別所有內建函數。例如,爲了獲得內建strcpy函數,應該調用__builtin_strcpy()而不是名爲 strcpy() 的函數。-fno-builtin-xxx:
gcc編譯時想特定的不使用某些函數的內建函數,例如,想使用除sqrt()之外的所有內建函數,則可以添加此選項-fno-builtin-sqrt
隱式聲明帶來的災難
不要用隱式聲明!!!不要用隱式聲明!!!不要用隱式聲明!!!
- 首先,隱式聲明是默認int型,如果沒有對應同名的內建函數,編譯不報錯,鏈接報錯,這種情況上面已經介紹
- 有同名的內建函數,且類型相同,gcc編譯鏈接沒問題
- 有同名的內建函數,類型不同,鏈接報warning“隱式聲明與內建函數不兼容”,但是以內建函數的函數原型爲準。gcc編譯器在編譯時能夠自動在常用庫頭文件(內建函數)中查找與隱式聲明同名的函數,如果發現兩者並不相同,則會按照內建函數的聲明原型去生成調用代碼。
case 2:
#include <stdio.h>
int main(){
int x = abs(-1,2,3,4,5);
printf("%d \n", x);
return 0;
}
其中,abs的隱式聲明函數原型爲:
int abs(int);
abs()內建函數原型爲:
int abs(int);
gcc編譯鏈接均沒有報錯,執行結果如下:
$ gcc -c implicitDeclare.c
$ gcc implicitDeclare.o -o implicitDeclare
$ ./implicitDeclare
1
可見,gcc的內建函數機制並不關心函數的參數,只是關心函數的返回值。雖然這個例子的運行結果都是正確的,但是這種正確是“碰巧”的,因爲額外的函數參數並沒有影響到結果。這種偶然正確是程序中要避免的。
case 3:
#include <stdio.h>
int main(){
double x = sqrt(1);
printf("%lf \n", x);
return 0;
}
其中,sqrt()的隱式聲明函數原型爲:
int sqrt(int);
sqrt()的內建函數原型爲:
double sqrt(double);
編譯報警告,鏈接按照內建函數原型鏈接:
$ gcc -c implicitDeclare.c
implicitDeclare.c: In function ‘main’:
implicitDeclare.c:4:16: warning: incompatible implicit declaration of built-in function ‘sqrt’ [enabled by default]
double x = sqrt(1);
^
$ gcc implicitDeclare.o -o implicitDeclare
但是仍然可以執行,結果如下:
$ ./implicitDeclare
1.000000