用g ++编译的奇怪代码


71

以下代码使用g ++ 4.8.1成功编译:

int main()
{
    int(*)();
}

它看起来像一个简单的函数指针声明:

int(*f)();

它不能与clang 3.4和vc ++ 2013一起编译。

是编译器错误还是标准的暗处之一?


使用g ++ 4.8.1可以正常编译的类似奇怪代码片段的列表(已更新):

  1. int(*)();

  2. int(*);

  3. int(*){};

  4. int(*());

这些奇怪代码片段的现场示例

更新1: @Ali在注释中添加了一些有趣的信息:

所有4种情况均使用clang 3.5干线(202594)给出了编译错误,并且使用gcc 4.9干线(20140302)进行了编译。的行为与相同-std=c++98 -pedantic,但int(*){};可以理解的除外。扩展的初始化程序列表仅适用于-std=c++11

更新2:正如@CantChooseUsernames他的回答中指出的那样,即使没有初始化,它们仍然可以正常编译,即使没有启用任何优化,g ++也不会为它们生成任何汇编(既没有初始化也没有初始化):

  1. int(*)() = 0;

  2. int(*) = 0;

  3. int(*){} = 0;

  4. int(*()) = 0;

带有初始化的实时示例

更新3:令我惊讶的是int(*)() = "Hello, world!";,它也int(*p)() = "Hello, world!";可以很好地编译(当然不编译)。

更新4:很棒,但是可以int(*){} = Hello, world!;编译。而下面的怪异之极的一段代码,也:int(*){}() = -+*/%&|^~.,:!?$()[]{};活生生的例子)。

更新5:正如@zwol评论中指出的

这个错误和许多相关的语法问题正在作为gcc错误68265进行跟踪。


3
int(*)();就像键入int;int*;...。也就是说,您开始声明变量类型,但不要命名它。
扎克·豪兰

12
fork(3)-3个人去ideone进行编译int;。:)
Luchian Grigore 2014年

1
@chris我想它与它的解析有关。似乎这是g ++中的错误,而clang和VS提供了适当的错误。
扎克·豪兰2014年

16
当前正在浏览eelis.net/C++/grammar.png,但在那里找不到
PlasmaHH 2014年

1
@PlasmaHH,这是一张令人赞叹的图片,感谢您的分享。
Natan Streppel

Answers:


15

根据C ++标准(第7节声明中的第6页)

6 init-declarator-list中的每个init-declarator都恰好包含一个declarator-id,它是该init-declarator声明的名称,因此是声明中声明的名称之一

因此,这只是一个编译器错误。

有效代码看起来像例如(除了您显示的函数指针声明),尽管我无法使用MS VC ++ 2010对其进行编译。

int(*p){};

似乎您用于测试的编译器允许声明而没有declarator-id。

还应考虑第8.1节“类型名称”的以下段落

1要显式地指定类型转换,并且必须指定类型名称作为 sizeof,alignof,new或typeid的参数。这可以通过type-id来完成,它在语法上是对该类型的变量或函数的声明,该声明忽略实体的名称。


可以不是初始化语句,而是可以由g ++编译的其他语句吗?
构造函数

我认为@Constructor是一个声明。
弗拉德(Flad),来自莫斯科

1
@BЈовић不,我刚好碰上他们。:-)我想了解它们是否确实存在错误。
构造函数

1
@VladfromMoscow我刚刚发现了另一段奇怪的代码(列表中的数字3,请参见问题的编辑)。您能说些什么?
构造函数

1
gcc扩展名,允许对评论以外的内容进行宣誓。
Yakk-Adam Nevraumont 2014年

7

我不确定这有多大帮助,但是我尝试了以下操作(clang 3.3,g ++ 4.8.1):

using P = int(*)();
using Q = int*;
P; // warning only
Q; // warning only
int(*)(); // error (but only in clang)
int*;     // error
int(*p)(); // ok
int *q;    // ok

另一方面,在g ++ 4.8.2和4.9.0中,一切都可以正常编译。不幸的是,我没有clang 3.4。

大致来说,声明[iso第7节]按顺序包括以下部分:

  1. 可选的前缀符(例如staticvirtual
  2. 基类型(例如const doublevector<int>
  3. 说明符(例如n*pa[7]f(int)
  4. 可选后缀功能说明符(例如constnoexcept
  5. 可选的初始化或功能体(例如= {1,2,3}{ return 0; }

现在,一个声明符大致由一个名称和一些可选的声明符运算符[iso 8/4]组成。

前缀运算符,例如:

  • * (指针)
  • *const (常量指针)
  • & (左值参考)
  • && (右值参考)
  • auto (函数返回类型,尾随时)

后缀运算符,例如:

  • [] (数组)
  • () (功能)
  • -> (函数尾随返回类型)

上面的运算符旨在反映它们在表达式中的使用。后缀运算符的绑定比前缀更紧密,并且括号可用于更改其顺序:int *f()是返回指针的函数int,而int (*f)()返回指针的函数int

也许我是错的,但是我认为这些运算符如果没有名称就不能出现在声明中。因此,当我们编写时int *q;,则int是基类型,并且*q是由前缀运算符*和name组成的声明符q。但是int *;不能单独出现。

另一方面,当我们定义时using Q = int*;,声明Q;本身就很好,因为Q它是基本类型。当然,由于我们未声明任何内容,因此根据编译器选项,我们可能会收到错误或警告,但这是一个不同的错误。

以上只是我的理解。标准(例如N3337)说的是[iso 8.3 / 1]:

每个声明符仅包含一个声明符id;它命名声明的标识符。除某些特殊功能的声明(12.3 [用户定义的转换],12.4 [析构函数],13.5 [重载的运算符])以及模板专门化的声明外,声明符中出现的不合格ID应该是一个简单的标识符。或部分专业(14.7)。

(方括号中的注释是我的)。所以我知道int(*)();应该是无效的,我不能说为什么它在clang和g ++的不同版本中具有不同的行为。


谢谢。您能说些有关奇怪代码段的更新列表吗?
建设者

恐怕不多。除了它们都可以在我拥有的所有g ++版本中进行编译外,而不能在clang中进行编译。同样,如果您添加名称或首先定义类型别名,它们将进行编译(当然,它{}是一个初始化程序,而不是该类型的一部分)。
iavr 2014年

我认为,如果添加名称,它们显然会成为有效的C ++结构。
建设者

3
我刚刚发现它也可以int(*){}() = -+*/%&|^~.,:!?$()[]{};编译!你怎么看待这件事?请参阅新问题更新中的实时示例。
构造函数

@Constructor越来越好!我以为作弊只是为了游戏……现在看来编译器也可以拥有作弊!太糟糕了,我不能再投票:-)
iavr 2014年

6

您可以使用:http : //gcc.godbolt.org/查看程序集。

int main()
{
    int(*)() = 0;
    return 0;
}

产生:

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    popq    %rbp
    ret

这等效于: int main() {return 0;} 因此,即使不进行优化,gcc也不会为其生成程序集。它应该给出警告还是错误?我不知道,但它不关心或对未命名的func指针不做任何事情。

然而:

int main()
{
    int (*p)() = 0;
    return 0;
}

没有优化将生成:

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movq    $0, -8(%rbp)
    movl    $0, %eax
    popq    %rbp
    ret

它在堆栈上分配8个字节。


好主意!谢谢。那又如何int(*)() = 0;呢?
构造函数

也没有组装。它不会分配给未命名的func ptr。如果您尝试分配1,则同样的事情。
布兰登2014年

2
这很奇怪,但也可以int(*)() = "Hello, world!";编译。虽然int(*p)() = "Hello, world!";没有。
建设者

我刚刚发现它也可以int(*){}() = -+*/%&|^~.,:!?$()[]{};编译!你怎么看待这件事?请参阅新问题更新中的实时示例。
构造函数

大声笑?这看起来像一个完全搞砸了解析器的问题,如果我见过一个..这应该毫无疑问给出一个错误。C不接受任何这方面的东西:ideone.com/GaxlGI
布兰登
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.