与g ++编译的奇怪代码
下面的代码用g ++ 4.8.1编译成功:
int main() { int(*)(); }
它看起来像一个函数指针的简单声明:
int(*f)();
它不能用clang 3.4和vc ++ 2013进行编译。
这是一个编译器错误还是标准的一个黑暗的地方?
用g ++编译的类似奇怪代码片断列表4.8.1(已更新):
-
int(*)();
-
int(*);
-
int(*){};
-
int(*());
这些奇怪的代码片段的生动的例子 。
更新1: @Ali在评论中增加了一些有趣的信息:
所有4种情况都给出了一个编译错误,使用3.5中继(202594),并使用gcc 4.9中继(20140302)进行编译。 行为与
-std=c++98 -pedantic
,除了int(*){};
这是可以理解的; 扩展的初始化程序列表仅在-std=c++11
可用-std=c++11
。
更新2:由于@CantChooseUsernames在他的答案中指出,即使在初始化时它们仍然可以很好地编译,而且即使没有任何启用的优化,g ++也不会为它们生成汇编(既不带有也不带有初始化)
-
int(*)() = 0;
-
int(*) = 0;
-
int(*){} = 0;
-
int(*()) = 0;
现场示例初始化 。
更新3:我真的很惊讶,发现int(*)() = "Hello, world!";
编译也很好(而int(*p)() = "Hello, world!";
当然不会编译)。
更新4:这是太棒了,但int(*){} = Hello, world!;
编译好。 下面这段代码也是非常奇怪的: int(*){}() = -+*/%&|^~.,:!?$()[]{};
( 现场示例 )。
更新5:正如@zwol在他的评论中指出的那样
这个和一些相关的语法问题正在作为gcc bug 68265被跟踪。
根据C ++标准(第7节声明的第6页)
6 init-declarator-list 中的每个init-declarator只包含一个declarator-id ,它是由该init-declarator声明的名称,因此声明中声明的名称之一
所以这只是一个编译器错误。
有效的代码可能看起来像(除了你所显示的函数指针声明),虽然我不能用我的MS VC ++ 2010编译它。
int(*p){};
看来你用来testing的编译器允许没有声明符的声明。
还要考虑8.1节types名称中的以下段落
1要明确指定types转换,并作为sizeof,alignof,new或typeid的参数,应指定types的名称。 这可以通过type-id来完成,它在语法上是一个variables或函数的声明,省略了实体的名字。
我不知道这有多大的帮助,但我尝试了以下(铿锵3.3,克++ 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中,所有东西都编译得很好。 3.4,不幸的是,我没有叮当声。
非常粗略地说 ,一个声明[iso部分7]按顺序由以下部分组成:
- 可选的前缀说明符(如
static
,virtual
) - 基types(例如
const double
,vector<int>
) - 声明符(例如
n
,*p
,a[7]
,f(int)
) - 可选的后缀函数说明符(如
const
,noexcept
) - 可选的初始值或函数体(例如
= {1,2,3}
或{ return 0; }
现在,一个声明器大致由一个名称和可选的一些声明符运算符组成[iso 8/4]。
前缀运算符,例如:
-
*
(指针) -
*const
(常量指针) -
&
(左值参考) -
&&
(右值引用) -
auto
(函数返回types,尾随时)
后缀运算符,例如:
-
[]
(数组) -
()
(function) -
->
(函数追踪返回types)
上面的操作符被devise来反映它们在expression式中的使用。 Postfix运算符的绑定比前缀更紧密,括号可以用来改变它们的顺序: int *f()
是一个返回指向int
的函数,而int (*f)()
是指向返回int
的函数的指针。
也许我错了,但我认为这些运营商不能在没有名字的声明中。 所以当我们写int *q;
,那么int
是基types, *q
是由前缀运算符*
后缀名称q
组成的声明符。 但是int *;
不能单独出现。
另一方面,当我们定义using Q = int*;
,然后申报Q;
由于Q
是基本types,所以本身是很好的。 当然,因为我们没有声明任何东西,所以根据编译器选项我们可能会得到一个错误或警告,但是这是一个不同的错误。
以上只是我的理解。 标准(如N3337)所说的是[iso 8.3 / 1]:
每个声明符都包含一个声明符id ; 它命名声明的标识符。 在声明 符 id中出现的非限定 id应该是一个简单的标识符,除了某些特殊函数(12.3 [ 用户定义的转换 ],12.4 [析构函数],13.5 [重载操作符])的声明以及用于声明模板专业化或部分专业化(14.7)。
(方括号中的注释是我的)。 所以我明白int(*)();
应该是无效的,我不能说为什么它在叮当声和不同版本的g ++中有不同的行为。
你可以使用这个: 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个字节。