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类型

前面给出的例子,并不会造成太大影响,因为在链接阶段很容易发现存在的问题。然而下面这个例子则会造成莫名的运行时错误。

#include

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

int main(int argc, char** argv)

{

int x = abs(-1);

printf("%d", x);

return 0;

}

此时,由于隐式声明的函数原型与gcc的内建函数原型完全相同,所以gcc不会给出任何警告,结果也是正确的。

而VC++则仍然会给出警告:warning C4013: “abs”未定义;假设外部返回 int。

无论如何,隐式声明的函数原型与库函数完全相同,所以链接运行都是没有问题的。

下面,稍微改动一下代码:

#include

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更严格,更严谨。

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

  1. c++ 代码_C代码对汇编代码中声明的全局符号的访问方式详解

    首先声明一点:符号的值是地址,该地址上存储的值才是变量,不明白的话看另一篇文章: "静态链接与动态链接的宏观概述及微观详解". (1)对非数组符号的访问,编译器生成访存指令,要想寻 ...

  2. c语言 字符串数组末尾,C语言中,利用宏定义一个字符串和字符数组定义的字符串其末尾都会自动添加\0字符...

    利用宏定义一个字符串#define MYPHONE "AT+CMGS" 字符数组的方式来定义一个字符串unsigned char SMS[]="HELLO, WELCOM ...

  3. c语言if文里null,C语言中if (p==NULL)的是与非

    博客园cnblogs不知为何最近开始渐有C语言开发重启的迹象,不少人开始写一些C语言的教程. 其中看到一段有趣的留言,提到这个写法:if (p == NULL), 有人说这是不好的~,经典不提倡的~, ...

  4. c语言中引用头使用什么指令,在源文件(.c)和头文件(.h)中声明和定义的区别——C语言...

    最近在看多文件编程的时候遇到的一个问题,本来以为理解了声明和定义的区别(然而并没有····),也算是重新认识了一次声明和定义,下面上代码 声明和定义:有分配空间的叫定义,没分配空间的叫声明 定义:表示 ...

  5. c语言函数内判断嵌套常见错误,c语言不等于(c语言多个if函数怎么嵌套)

    其实我想问的是!=和==!有区别吗?我测试了一下,好像一样啊... 初学. 不等于符号就是 != != 是逻辑运算符,这个符号一般不适用于字符串的比较 != a为变量,用if语句检测a(不等于,大于, ...

  6. 在c语言中出现的int错误类型,【揭秘】C语言类型转换时发生了什么?

    原标题:[揭秘]C语言类型转换时发生了什么? ID:技术让梦想更伟大 作者:李肖遥 在C语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统,我们常用的算术类型包括两种类型:整数类型和浮 ...

  7. 乐创杯c语言大赛形式,【连载】(学了这么多年C语言,你真的了解static关键词吗)乐创DIY C语言讲义​——4.4节...

    文 | Edward 4.4 变量的补充 前面内容中,我们已经讨论了变量的定义,但是由于函数的概念还没有引入,因而这是不全面的,本节开始,我们再来补充一些变量的其他内容,这就相当于对变量这个概念的重新 ...

  8. 10万字C语言入门手册,历时三个月,详细的C语言教程终于出炉了,给你全新C语言入门体验

    C语言是一种非常流行的编程语言,它是许多现代编程语言的基础. 什么是计算机? 计算机是一种用于存储.处理.和输出数据的电子设备.它通过程序控制和数学算法来实现数据处理.计算机通过输入设备(如键盘.鼠标 ...

  9. c语言中赋值的时候顿号的作用,简单总结C语言中的运算符优先级

    简单总结C语言中的运算符优先级 C语言中有很多运算符,除了四则运算,还有位运算.比较运算.逻辑运算.赋值运算等等,令人眼花缭乱的同时,优先级也让人头疼.通常建议在写代码的时候不用省略括号,但是并不是所 ...

最新文章

  1. 基于python的数据分析方法五种_利用Python进行数据分析 第5章 pandas入门(1)
  2. 深度学习在图像超分辨率重建中的应用
  3. js中使用进行字符串传参
  4. GlassFish新纪元
  5. 很多字段的数据要插入另一张表_一文看懂数据库设计之逻辑设计,值得收藏
  6. iOS NSOperation 非并发执行
  7. 解决git clone报错SSL certificate problem
  8. 制作根文件系统时的错误解决
  9. 微信小程序启动、更新机制
  10. 关于python中自己写的模块之前相互调用函数
  11. ios build fdk-aac
  12. 均衡发展学校计算机室解说词,迎接省均衡发展学校解说词
  13. qq批量登录软件_QQ账号永久冻结
  14. java 200以内的素数_java200以内的素数每行每列7个代码怎么写?
  15. 一文了解无线网桥-小白笔记
  16. 写给立志进入网络行业的朋友的一些忠告
  17. python删除重复单词_Python - 过滤重复的单词
  18. ECharts 使用xAxis.boundaryGap使x轴两边不留空白
  19. 爬虫练习--豆瓣英美剧爬虫
  20. 请记得,我不是个好人

热门文章

  1. word中使用通配符批量将参考文献设置为上角标
  2. 通达信 python插件选股_期货市场教程第七版_使用python在通达信里面选股
  3. wegame饥荒一直登录中_腾讯WeGame新功能:手机串流,畅玩PC大作
  4. webpack打包文件过大,如何解决
  5. access/_access函数
  6. MySQL连接报1045错误
  7. 57- 打印钻石图形
  8. Bert时代的创新:Bert在NLP各领域的应用进展 | 技术头条
  9. 会员积分储值充值管理系统asp源码基于微信公众号
  10. linux虚拟机获取ip地址