很久以前,我在一个论坛上偶然发现了一个有趣的问题,我想知道答案.

考虑以下C函数:

f1.C

#include <stdbool.h>

bool f1()
{
    int var1 = 1000;
    int var2 = 2000;
    int var3 = var1 + var2;
    return (var3 == 0) ? true : false;
}

var3 == 3000开始,这应该始终返回false.main函数如下所示:

主要的C

#include <stdio.h>
#include <stdbool.h>

int main()
{
    printf( f1() == true ? "true\n" : "false\n");
    if( f1() )
    {
        printf("executed\n");
    }
    return 0;
}

因为f1()应该总是返回false,所以人们希望程序只在屏幕上打印一个false.但编译并运行后,executed也会显示:

$ gcc 主要的C f1.C -o test
$ ./test
false
executed

为什么?这段代码是否有某种未定义的行为?

注:我是用gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2编译的.

推荐答案

正如其他答案所指出的,问题是使用gcc时没有设置编译器选项.如果您这样做,它默认为所谓的"gnu90",这是旧的、从1990年撤回的C90标准的非标准实现.

在旧的C90标准中,C语言有一个重大缺陷:如果在使用函数之前没有声明原型,它将缺省为int func ()(其中( )表示"接受任何参数").这改变了函数func的调用约定,但不改变实际的函数定义.由于boolint的大小不同,因此在调用函数时,您的代码将调用未定义的行为.

1999年,随着C99标准的发布,这种危险的胡说八道行为得到了纠正.隐式函数声明被禁止.

不幸的是,GCC升级到了版本5.x、 默认情况下,x仍然使用旧的C标准.你可能没有理由想把代码编译成标准C以外的任何代码.所以你必须明确地告诉GCC,它应该把你的代码编译成现代C代码,而不是25年前的非标准GNU垃圾代码.

通过始终将您的程序编译为:

gcc -std=c11 -pedantic-errors -Wall -Wextra
  • -std=c11告诉它根据(当前)C标准(非正式地称为C11)进行半心半意的编译.
  • -pedantic-errors告诉它要全心全意地做上述工作,当你编写了违反C标准的错误代码时,要给出编译器错误.
  • -Wall意味着给我一些额外的警告,这可能是好的.
  • -Wextra意味着给我一些额外的警告,可能会更好.

C++相关问答推荐

不同到达时间的轮询实现

如何将不同长度的位转换成字节数组?

通过管道将一个子系统的标准输出发送到另一个子系统的标准输出

Rust FFI--如何用给出返回引用的迭代器包装C风格的迭代器?

轮询libusb_pollfd struct 列表的正确方式是什么?

S的这种管道实施有什么问题吗?

在libwget中启用Cookie会导致分段故障

接受任何参数的函数指针是否与接受不同参数的函数兼容

C整型和_泛型.哪些类型是兼容的?

类型定义 struct 与简单的类型定义 struct

在吉陀罗中,_2_1_和CONCAT11是什么意思?

从整型转换为浮点型可能会改变其值.

可以对两种 struct 类型中的任何一种进行操作的C函数

在我的函数中实现va_arg的问题

GCC认为这是一个VLA是对的吗?

gdb - 你能找到持有内部 glibc 锁的线程吗?

既然我们在 if 中将 int 的值更改为 10,为什么在第二个 fork 后,子进程及其创建的子进程都会打印 33 ?

如何为avr atmega32微控制器构建C代码,通过光电二极管捕获光强度并通过串行通信传输数据

子进程不会修改父进程中的统计信息

比 * 更快的乘法