天天看点

隐式声明与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 
 
           

继续阅读