天天看點

隐式聲明與GCC内建函數C語言隐式聲明與GCC内建函數

C語言隐式聲明與GCC内建函數

  • C語言隐式聲明與GCC内建函數
    • 什麼是C語言的隐式聲明
    • GCC内建函數
    • 隐式聲明帶來的災難

什麼是C語言的隐式聲明

  在C語言中,函數在調用前不一定非要聲明。如果沒有聲明,那麼編譯器會自動按照一種隐式聲明的規則,為調用函數的C代碼産生彙編代碼。下面是一個例子:

#include <stdio.h>

int main() {
    double x = no_declare_func();
    return ;
}
           
$ 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 ;
}
           

  在連結的時候會報錯,因為連結過程中沒有找到函數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

隐式聲明帶來的災難

  不要用隐式聲明!!!不要用隐式聲明!!!不要用隐式聲明!!!

  1. 首先,隐式聲明是預設int型,如果沒有對應同名的内建函數,編譯不報錯,連結報錯,這種情況上面已經介紹
  2. 有同名的内建函數,且類型相同,gcc編譯連結沒問題
  3. 有同名的内建函數,類型不同,連結報warning“隐式聲明與内建函數不相容”,但是以内建函數的函數原型為準。gcc編譯器在編譯時能夠自動在常用庫頭檔案(内建函數)中查找與隐式聲明同名的函數,如果發現兩者并不相同,則會按照内建函數的聲明原型去生成調用代碼。

case 2:

#include <stdio.h>

int main(){
    int x = abs(-,,,,);
    printf("%d \n", x);
    return ;
}
           

  其中,abs的隐式聲明函數原型為:

int abs(int);
           

  abs()内建函數原型為:

int abs(int);
           

  gcc編譯連結均沒有報錯,執行結果如下:

$ gcc -c implicitDeclare.c
$ gcc implicitDeclare.o -o implicitDeclare
$ ./implicitDeclare 
 
           

  可見,gcc的内建函數機制并不關心函數的參數,隻是關心函數的傳回值。雖然這個例子的運作結果都是正确的,但是這種正确是“碰巧”的,因為額外的函數參數并沒有影響到結果。這種偶然正确是程式中要避免的。

case 3:

#include <stdio.h>

int main(){
    double x = sqrt();
    printf("%lf \n", x);
    return ;
}
           

  其中,sqrt()的隐式聲明函數原型為:

int sqrt(int);
           

  sqrt()的内建函數原型為:

double sqrt(double);
           

編譯報警告,連結按照内建函數原型連結:

$ gcc -c implicitDeclare.c
implicitDeclare.c: In function ‘main’:
implicitDeclare.c::: warning: incompatible implicit declaration of built-in function ‘sqrt’ [enabled by default]
     double x = sqrt();
                ^
$ gcc implicitDeclare.o -o implicitDeclare
           

但是仍然可以執行,結果如下:

$ ./implicitDeclare 
 
           

繼續閱讀