implicit declaration(奇怪的C语言程序)

昨天浏览外文论坛帖子,发现一个有趣的问题。这个问题产生的原因是程序员编写C语言代码不规范造成的,这也是很多初学者容易犯的错——只关注核心功能,而不关注细节。 问题是这样的问题 小明...

昨天浏览外文论坛帖子,发现一个有趣的问题。这个问题产生的原因是程序员编写C语言代码不规范造成的,这也是很多初学者容易犯的错——只关注核心功能,而不关注细节。

奇怪的C语言程序,if条件明明为假,这个函数为何还会执行呢?

问题是这样的

问题

小明在源文件 f1.c 里定义了一个布尔函数,相关的C语言代码是下面这样的:

#include <stdbool.h>bool f1(){ int var1 = 1000; int var2 = 2000; int var3 = var1 + var2; return (var3 == 0) ? true : false;}奇怪的C语言程序,if条件明明为假,这个函数为何还会执行呢?

f1()函数的C语言代码

显然,函数 f1() 执行后,变量 var3 的值等于 3000,因此必定会返回 false。但是小明在 main.c 文件里编写 main() 函数调用 f1() 后,发现结果似乎有些奇怪,main() 函数的C语言代码如下:

#include <stdio.h>#include <stdbool.h>int main(){ printf( f1() == true ? "true\n" : "false\n"); if( f1() ) { printf("executed\n"); } return 0;}奇怪的C语言程序,if条件明明为假,这个函数为何还会执行呢?

main()函数的代码

按理说,既然 f1() 函数总是返回 false,上述 main() 函数被编译执行后,应该只会输出“false”才对,但是小明**的实际执行结果却是下面这样的:

$ gcc main.c f1.c -o test$ ./testfalseexecuted

这是怎么回事呢?小明使用的编译器版本为 gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2。

**

仔细观察小明的编译命令gcc main.c f1.c -o test,应该能够发现他并没有指定任何别的编译选项,因此 gcc 编译器默认实现的是 1990 年(很老了)的 C90 **。

奇怪的C语言程序,if条件明明为假,这个函数为何还会执行呢?

gcc 编译器默认实现的是 C90 **

C90 **在处理C语言代码时,有一个主要缺点就是:如果某段C语言代码调用函数时,程序员未能实现指定被调用函数原型,那么将使用默认的函数原型,即默认被调用函数的原型为:

int func();

也即默认被调用函数可以接收任意多的参数,并且返回值类型为 int。这样的默认规则在处理函数返回值时可能会有一个转换**(当被调用函数原型返回值不是 int 类型时),但是它并不会修改实际的函数实现。

现在小明遇到的问题就清楚了:f1() 函数原本的返回值类型为 bool 型,但是他在编写 main() 函数时,并未事先指定 f1() 函数的原型,因此编译器默认将其当作是 int 返回值类型了。

而 bool 型和 int 型的 size 并不一致,所以小明编写的C语言程序行为就属于“未定义”的了,出现什么样的结果都是不足为奇的。

奇怪的C语言程序,if条件明明为假,这个函数为何还会执行呢?

出现什么样的结果都是不足为奇的

能够看出,C90 **在遇到未知函数原型时,会默认将其当做 int func(); 原型的特性其实是一种危险的特性,因此,从 C99 **开始,这样的特性就被禁止了。

但是不幸的是,直到 5.x.x 版本的 gcc 默认属性仍然是较老的 C90 **,这可能是为了兼容之前的C语言代码。要解决小明遇到的问题也是简单的,只需要告诉 gcc 编译器希望使用的**就可以了。例如:

gcc -std=c11 -pedantic-errors -Wall -Wextra-std=c11 用于告诉编译器遵循 C11 **。-pedantic-errors 告诉编译器全心全意的编译C语言代码,一旦发现错误就给出错误提示。-Wall 可以尽可能的让编译器发现一些不规范的代码,并给出相应的**告提示。-Wextra 类似于 -Wall 选项,它能够让编译器发现更多不规范的代码。

使用上述命令编译小明的C语言代码,应该会发现此时编译器不再能够完成编译了,而是给出了错误提示:“implicit declaration of function ‘f1’”:

奇怪的C语言程序,if条件明明为假,这个函数为何还会执行呢?

错误提示

此时,编译器不再“猜测”f1() 函数的原型,而是强制我们在调用函数前指定其原型。因此对小明的C语言代码稍作修改,如下:

奇怪的C语言程序,if条件明明为假,这个函数为何还会执行呢?

强制我们在调用函数前指定其原型

此时再编译执行,发现一起符合预期了:

# gcc -std=c11 -pedantic-errors -Wall -Wextra main.c f1.c -o test# ./test false小结

很多C语言初学者觉得细节不重要,重要的是核心算法或者代码,这样的心态其实很危险,很容易写出难以捉摸的程序,本节就是一个例子。事实上,越是初学者就越应该严格遵守规范,这样才能尽可能的避免出现奇怪的,难以捉摸的结果,打击自己的学习信心。

奇怪的C语言程序,if条件明明为假,这个函数为何还会执行呢?

点个赞再走吧

欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式**,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。

未经许可,禁止转载。

  • 发表于 2022-12-01 14:34:43
  • 阅读 ( 212 )
  • 分类:科技

0 条评论

请先 登录 后评论
ki
ki

230 篇文章

你可能感兴趣的文章

相关问题