为啥声明“2i;”不会导致编译器错误?

Posted

技术标签:

【中文标题】为啥声明“2i;”不会导致编译器错误?【英文标题】:Why does the statement "2i;" NOT cause a compiler error?为什么声明“2i;”不会导致编译器错误? 【发布时间】:2016-05-13 01:03:10 【问题描述】:

我不小心写了2*i,而不是2i

int foo(int i)

    2i;
    return 2i;

我希望编译器能够捕捉到错误。但它没有。那么2i 是 C 语言中的有效语句吗?如果是这样,它有什么作用?困惑!

我使用 gcc 版本 5.3.0 编译,这是汇编输出:

    .file   "strange.c"
    .text
    .globl  foo
    .type   foo, @function
foo:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    %edi, -4(%rbp)
    nop
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   foo, .-foo
    .ident  "GCC: (GNU) 5.3.0"
    .section    .note.GNU-stack,"",@progbits

【问题讨论】:

那是什么编译器? 我不使用_Complex 号码。在阅读了一些标准和提供的链接之后,我认为@iharob 的回答是正确的。 @Olaf 西方的fastet傅里叶变换fftw使用_Complex类型。我在为我的论文编写数据处理软件时发现了这一点(实际上是物理学,而不是计算机科学或类似的科学)-(我必须应用低通滤波器,所以卷积所以快速傅里叶变换,所以fftw)。 @iharob:嗯,这比 ffte 快吗?如果不是,如果我住在西部,但在你的东部,我可以使用更快的吗? ;-) 说真的:感谢您提供的信息。看起来,C 标准甚至不支持与 C11 类似的简单表示法。 一个很好的例子“......不是'Eureka'而是'这很有趣......'”! 【参考方案1】:

这是一个gcc extension,而2i 是虚常数。所以你可以像这样写一个复数:

#include <complex.h>

_Complex x = 4 + 5i;

【讨论】:

哇,真是个惊喜!你能告诉我为什么即使我没有包含 complex.h 头文件它也能工作吗? @daltonfury42 标头用于_Complex 类型,2i 是一个常量(gcc 理解它)。添加std=c99std=c11 标志并结合-Wall,您将看到警告。此外,它确实不返回0,但由于返回类型应该是_Complex 和值0 + 2i,你不能用printf() 检查它。所以也许这只是真正的部分0 @daltonfury42:您也不必通过#include &lt;float.h&gt;(或math.h)获得对浮点常量的支持。 @daltonfury42 没错。头文件不会改变语言语法,它们只是声明变量、函数、类型等。 @daltonfury42 尽管有可能通过#pragma 控制对这种语法的识别,complex.h 可能会发出此问题。但他们没有这样做。【参考方案2】:

2i 是复数整数文字的gcc 扩展,是-1 平方根的两倍的纯虚数。 clang 也支持此扩展。

使用gcc 5.4.0 进行编译会产生发布的程序集输出,这有点令人惊讶:

在http://gcc.godbolt.org/# 上编译我收到来自gcc 5.3.0 的编译错误:http://gcc.godbolt.org/#:error: cannot convert '__complex__ int' to 'int' in return。 发布的函数foo 的汇编代码不正确:它不返回0。将复整数常量 2i 转换为 int 应该返回其实部 0

相反,使用clang 3.7,它会在没有警告的情况下编译并生成最佳代码,但当然不是您所期望的:

foo(int):                       # @foo(int)
    xorl    %eax, %eax
    retq

此语法可以以任何顺序与其他后缀组合。用clang -Weverything 编译下面的代码会给我适当的警告warning: imaginary constants are a GNU extension [-Wgnu-imaginary-constant]

#include <stdio.h>

int main() 
    /* complex integer literals */
    printf("sizeof(2i) = %zd\n", sizeof(2i));
    printf("sizeof(2ui) = %zd\n", sizeof(2ui));
    printf("sizeof(2li) = %zd\n", sizeof(2li));
    printf("sizeof(2lli) = %zd\n", sizeof(2lli));
    /* complex floating point literals */
    printf("sizeof(2.i) = %zd\n", sizeof(2.i));
    printf("sizeof(2.fi) = %zd\n", sizeof(2.fi));
    printf("sizeof(2e0fi) = %zd\n", sizeof(2e0fi));
    printf("sizeof(2e0i) = %zd\n", sizeof(2e0i));
    /* alternate order */
    printf("sizeof(2il) = %zd\n", sizeof(2il));
    printf("sizeof(2ill) = %zd\n", sizeof(2ill));
    printf("sizeof(2.if) = %zd\n", sizeof(2.if));

    return 0;

它在我的环境中产生这个输出:

sizeof(2i) = 8
sizeof(2ui) = 8
sizeof(2li) = 16
sizeof(2lli) = 16
sizeof(2.i) = 16
sizeof(2.fi) = 8
sizeof(2e0fi) = 8
sizeof(2e0i) = 16
sizeof(2il) = 16
sizeof(2ill) = 16
sizeof(2.if) = 8

用你的语法着色编辑器试试最后一个;-)

【讨论】:

嗯,这就是我在运行 Arch Linux 的 PC 上使用 GCC 5.3.0 时得到的结果。 Here 是我的 gcc 配置,如果你有兴趣的话。

以上是关于为啥声明“2i;”不会导致编译器错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥重新声明 std::cout 会导致分段错误?

在我将编译器设置设置为支持 c++11 后,我仍然收到错误“stoi”未声明。为啥? [复制]

为啥将 lambda 传递给受约束的类型模板参数会导致“不完整类型”编译器错误?

为啥在 MFC 应用程序中设置 DEBUG_NEW 会导致编译器错误

为啥在 BinaryReader 上调用 Dispose() 会导致编译错误?

为啥 Java 编译器不会为无法访问的 then 语句生成无法访问的语句错误?