当前位置: 首页 > 知识库问答 >
问题:

这个C函数应该总是返回false,但它没有

宋成天
2023-03-14

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

请考虑以下 C 函数:

#include <stdbool.h>

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

这应该总是返回< code>false,因为< code>var3 == 3000。< code>main函数如下所示:

#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 打印到屏幕上。但是在编译并运行它之后,还会显示执行:

$ gcc main.c f1.c -o test
$ ./test
false
executed

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

注意:我用gcc(Ubuntu 4.9.2-10ubuntu13)4.9.2编译它。


共有3个答案

秦炜
2023-03-14

我认为看看伦丁出色的回答中提到的尺寸不匹配实际上发生在哪里很有趣。

如果使用 --save-temps 进行编译,则将获得可以查看的程序集文件。下面是 f1() 执行 == 0 比较并返回其值的部分:

cmpl    $0, -4(%rbp)
sete    %al

现在在main()中:

call    f1
testl   %eax, %eax
je  .L2

添加显式函数声明会将main()更改为:

call    f1
testb   %al, %al
je  .L2

这就是我们想要的。

尹何平
2023-03-14

您在main. c中没有为f1()声明原型,因此它被隐式定义为int f1(),这意味着它是一个接受未知数量的参数并返回int的函数。

如果int和bool的大小不同,则会导致未定义的行为。例如,在我的机器上,int是4个字节,而bool是1个字节。因为函数被定义为返回bool,所以当它返回时,会在堆栈上放一个字节。然而,由于它隐式声明从main返回int。c、 调用函数将尝试从堆栈中读取4个字节。

gcc中的默认编译器选项不会告诉您它正在执行此操作。但是,如果使用-Wall-Wextra进行编译,则会得到以下结果:

main.c: In function ‘main’:
main.c:6: warning: implicit declaration of function ‘f1’

要解决此问题,请在 main.c 中的主要之前添加 f1 的声明

bool f1(void);

请注意,参数列表被显式设置为void,这告诉编译器函数不接受参数,而不是空参数列表,这意味着参数数量未知。f1.c中的f1定义也应更改以反映这一点。

南宫奇思
2023-03-14

正如在其他答案中所指出的,问题在于您使用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标准的错误代码时给编译器带来错误
  • -墙意味着给我一些额外的警告,这可能会很好
  • -Wextra的意思是给我一些其他可能很好的警告
 类似资料:
  • 在WordPress主题中,当一个联系人表单完成时,一条消息会写在页面上,但不会写在我想要的位置。但是当在PHP代码中更改消息的位置时,联系人表单中的段落(写在文章中)不再显示。事实上,我认为方法返回false,因此不会调用方法。 以下是原始代码: 我希望它是这样的: 如你所见,我只更改了确认消息的位置。但是在第二种方式中,具有的条件似乎不是真的,因为我在文章中写的文本没有显示出来。 有人能帮我解

  • 问题内容: 我已经写了这段简单的代码: 在我的情况当属。谁能建议/建议出什么问题了? 问题答案: 错误检查和处理是程序员的朋友。检查初始化和执行cURL函数的返回值。并在失败的情况下包含更多信息: *在 手动状态: 成功返回cURL句柄,错误返回 FALSE 。 我观察到该函数在您使用其参数且无法解析域时会返回。如果未使用该参数,则该函数 可能 永远不会返回。但是,请务必始终进行检查,因为该手册并

  • 出于某种原因,在下面的递归函数中, 永远不递增p,也就是说字符串s永远不是一个回文,尽管在我的程序中,s确实是一个回文,次数相当少。但是在下面一行中它仍然返回false 是因为功能吗?s采用的一些值为:aaa、aba、AAAA、abbb、bab 我已确保s中没有前后空格 P、 S:我已经检查过了,x==n 示例输入: 输出0。回文:aaaaaa、abaaba、aaaaaa

  • 而且 不是应该都返回吗?它不是基元变量,在第二个代码中,即使在添加零之后,它也会打印。我知道装箱(对于从-128到127的整数),但是为什么装箱在第二段代码中起作用而不是在第一段代码中起作用呢?

  • 我想检查模型是否已更改isDirty方法,但总是返回false。 这是我的代码:

  • 为什么运行应用程序时返回false? 我使用它来控制日志,如下所示: