1 什么是C语言的隐式函数声明

在C语言中,函数在调用前不一定非要声明。如果没有声明,那么编译器会自己主动依照一种隐式声明的规则,为调用函数的C代码产生汇编代码。以下是一个样例:

int main(int argc, char** argv)
{double x = any_name_function();return 0;
}

单纯的编译上述源代码。并没有不论什么报错,仅仅是在链接阶段由于找不到名为any_name_function的函数体而报错。

[smstong@centos192 test]$ gcc -c main.c
[smstong@centos192 test]$ gcc main.o
main.o: In function `main':
main.c:(.text+0x15): undefined reference to `any_name_function'
collect2: ld 返回 1

之所以编译不会报错,是由于C语言规定,对于没有声明的函数,自己主动使用隐式声明。

相当于变成了例如以下代码:

int any_name_function();
int main(int argc, char** argv)
{double x = any_name_function();return 0;
}

2 带来的问题

2.1 隐式声明函数名称恰好在链接库中存在,但返回非int类型

前面给出的样例。并不会造成太大影响。由于在链接阶段非常easy发现存在的问题。

然而以下这个样例则会造成莫名的执行时错误。

#include <stdio.h>
int main(int argc, char** argv)
{double x = sqrt(1);printf("%lf", x);return 0;
}

gcc编译链接

[smstong@centos192 test]$ gcc -c main.c
main.c: 在函数‘main’中:
main.c:6: 警告:隐式声明与内建函数‘sqrt’不兼容
[smstong@centos192 test]$ gcc main.o

执行结果

1.000000

编译时会给出警告,提示隐式声明与内建函数’sqrt’不兼容。gcc编译器在编译时可以自己主动在经常使用库头文件(内建函数)中查找与隐式声明同名的函数,如果发现两者并不同样。则会依照内建函数的声明原型去生成调用代码。这往往也是程序猿预期的想法。
上面的样例中隐式声明的函数原型为:

int sqrt(int);

而相应的同名内建函数原型为:

double sqrt(double);

终于编译器依照内建函数原型进行了编译。达到了预期效果。

然而gcc编译器的这样的行为并非C语言的规范,并非全部的编译器实现都有这样的功能。

同样的源代码在VC++2015下编译执行的结果却是:

VC++编译

warning C4013: “sqrt”没有定义;如果外部返回 int

执行结果

2884223.000000

显然。VC++编译器没有没有所谓的“内建函数”,仅仅是简单的依照隐式声明的原型,生成调用sqrt函数的代码。由于返回类型和參数类型的不同。导致错误的函数调用方式。产生莫名奇异的执行时错误。

对着这样的情况,由于返回类型的不同,两种编译器都可以给出警告信息,至少能引起程序猿的注意。而以下这样的情况,则更加隐蔽。

2.2 隐式声明函数名称恰好在链接库中存在。且返回int类型

測试代码例如以下:

#include <stdio.h>int main(int argc, char** argv)
{int x = abs(-1);printf("%d", x);return 0;
}

此时。由于隐式声明的函数原型与gcc的内建函数原型全然同样。所以gcc不会给出不论什么警告,结果也是正确的。
而VC++则仍然会给出警告:warning C4013: “abs”没有定义。如果外部返回 int。

不管怎样,隐式声明的函数原型与库函数全然同样,所以链接执行都是没有问题的。

以下,略微修改一下代码:

#include <stdio.h>int main(int argc, char** argv)
{int x = abs(-1,2,3,4);printf("%d", x);return 0;
}

gcc下编译链接没有不论什么报错。

gcc编译链接

[smstong@centos192 test]$ gcc -c main.c
[smstong@centos192 test]$ gcc main.o

可见。gcc的内建函数机制并不关心函数的參数。仅仅是关心函数的返回值。

vc++编译链接

warning C4013: “abs”没有定义;如果外部返回 int

尽管这个样例的执行结果都是正确的,可是这样的正确是“碰巧”的,由于额外的函数參数并没有影响到结果。这样的偶然正确是程序中要避免的。

3 编程中注意事项

C语言的隐式函数声明。给程序猿带来了各种困惑,给程序的稳定性带来了非常坏的影响。不知道当初C语言设计者是怎样考虑这个问题的?

* 为了避免这样的影响,强烈建议程序猿重视编译器给出的关于隐式声明的警告,及时通过包括必要的头文件来消除这样的警告。*

对于gcc来说。前面给出的那个abs(-1,2,3,4)的特殊样例。编译器根本不会产生不论什么警告,仅仅能靠程序猿熟悉自己调用的每个库函数了。

为了避免这样的问题,在C语言的C99版本号中,不管怎样都会给出警告。如gcc使用C99编译上述代码。

gcc -std=c99编译

[smstong@centos192 test]$ gcc -c main.c -std=c99
main.c: 在函数‘main’中:
main.c:5: 警告:隐式声明函数‘abs’

而C++则更严格,直接抛弃了隐式函数声明,对于未声明函数的调用,将直接无法通过编译。

g++编译

[smstong@centos192 test]$ g++ main.c
main.c: In function ‘int main(int, char**)’:
main.c:5: 错误:‘abs’在此作用域中尚未声明

vc++编译(作为C++)

error C3861: “abs”: 找不到标识符

在函数强类型这一点上。C++确实比C更严格,更严谨。

转载于:https://www.cnblogs.com/clnchanpin/p/7189554.html

万恶之源:C语言中的隐式函数声明相关推荐

  1. 什么是C语言中的隐式函数声明?

    「1.什么是C语言的隐式函数声明」 在C语言中,函数在调用前不一定非要声明.如果没有声明,那么编译器会自动按照一种隐式声明的规则,为调用函数的C代码产生汇编代码.下面是一个例子: int main(i ...

  2. 在c语言中函数的隐函,C语言中的隐式函数声明

    1 什么是C语言的隐式函数声明 在C语言中,函数在调用前不一定非要声明.如果没有声明,那么编译器会自动按照一种隐式声明的规则,为调用函数的C代码产生汇编代码.下面是一个例子: int main(int ...

  3. C语言中的隐式声明是什么,有什么危害?warning: implicit declaration of function ‘xxx’

    今天编译一个代码,提示CUSTOM_LAYER里面有一个C文件函数gettimeofday()隐式声明,用man手册查了一下,需要包含头文件#include <sys/time.h> 那么 ...

  4. c语言 隐式声明,关于C#:隐式函数声明和链接

    最近,我了解了C语言中的隐式函数声明.主要思想很明确,但在这种情况下,我对理解链接过程有些麻烦. 考虑以下代码(文件a.c): #include int main() { double someVal ...

  5. C语言一定要有函数声明吗,1 什么是C语言的隐式函数声明在C语言中,函数在调用前不一定非要声明。如果没有声明,那么编译器会自动按照一种隐式声明的规则,为调用函数的C代码产生汇编代码。下...

    1 什么是C语言的隐式函数声明 在C语言中,函数在调用前不一定非要声明.如果没有声明,那么编译器会自动按照一种隐式声明的规则,为调用函数的C代码产生汇编代码.下面是一个例子: int main(int ...

  6. C 编译器隐式函数声明(Implicit function declarations in C)

    C 编译器隐式函数声明(Implicit function declarations in C) 引子 C 编译器隐式函数申明 警示 引子 最近在帮同事定位一个 bug,简化如下:两个 c 文件,a. ...

  7. scala中的隐式转换、隐式参数和隐式类

    scala中的隐式转换.隐式参数和隐式类 @(SCALA)[scala] scala中的隐式转换隐式参数和隐式类 一隐式转换 1示例 2隐式转换的条件 二隐式参数 1示例 三隐式类 1示例 隐式转换是 ...

  8. c语言中有裁剪字符串的函数吗,C语言中的字符串截取函数

    /*======================================================== 子数整数 源程序名 num.??? (pas,c,cpp) 可执行文件名 num. ...

  9. c语言sub函数是什么,C语言中的字符串截取函数

    C语言中的字符串截取函数及应用 /*======================================================== 子数整数 源程序名 num.??? (pas,c, ...

最新文章

  1. TSNE算法是什么?该算法有什么缺陷?
  2. JavaScript——易班优课YOOC课群在线测试自动答题解决方案(四)答案显示
  3. Linux Shell常用技巧(一)
  4. linux ucontext 类型,协程:posix::ucontext用户级线程实现原理分析 | WalkerTalking
  5. code vs1517 求一次函数解析式(数论 纯数学知识)
  6. vue实现侧边折叠菜单栏手风琴效果
  7. html div float center,跨浏览器实现float:center
  8. C#设计模式之17-中介者模式
  9. NAnt 与 MS Build
  10. 5-32 说反话-加强版
  11. pid控制从入门到精通pdf_《PID整定指导》白皮书——PID领域的葵花宝典来袭!
  12. 单片机和微型计算机异同,微机与单片机有什么主要的区别?
  13. 还在使用集合类完成这些功能?不妨来看看 Guava 集合类!!!
  14. 利用MapInfo将MapInfo格式数据转换成shp格式
  15. rust进水器怎么用_RO反渗透净水器的正确使用方式 你都Get了吗?
  16. JavaScript学习指南 (来自转载)
  17. 微信小程序开发入门(API)
  18. 基于Android的人脸门禁系统
  19. java正则表达式控制半角字符串输入
  20. [财务][数据化分析][财务背景知识][财务三张基础表][资产负债表][利润表][现金流量表]看懂财务三张表,以后看表再也不求人了...

热门文章

  1. oracle第二章数据的运用,第二章:oracle_sql语句之限制(where子句)和排列数据(order by子句)...
  2. leetcode @38报数-js
  3. 5-1rquests模拟登陆知乎之httpcode
  4. 搭建“双11”大型网站架构必须掌握的 5 个核心知识
  5. zabbix设置邮件报警
  6. Kettle和ETL的基本构成
  7. 从零开始成为一名开源程序员,其实只需要九步!
  8. [HNOI2008]玩具装箱toy
  9. Redis附加功能之键过期功能
  10. Acey.ExcelX组件如何保证稳定性?