用g++进行奇怪的零初始化
Strange zero initialization with g++
在使用g++ 4.4.3进行int型初始化时,我遇到了下面代码的一个奇怪行为。
int main()
{
int x(int());
int y = int();
cout << x << " " << y << endl;
}
的结果是:
1 0
"y"的值如预期的那样是0,但x的值奇怪地是"1"!
在VS2008中产生以下链接错误(函数声明,但没有定义):
unresolved external symbol "int __cdecl x(int (__cdecl*)(void))"
谁能解释g++的这种奇怪行为? int x(int());
解析为函数声明
声明一个名为x
的函数,返回一个int
,接受一个参数,该函数的类型为返回一个int
,不接受任何参数。
为了补充GMan在这里的答案(x
是一个函数定义),为什么1
.
输出为1
的原因是,在调用std::cout << x
的地方,函数衰减为指向该函数的指针(语言不允许将函数作为参数传递给其他函数,因此对于数组,执行隐式转换为指向的指针)。现在,没有接受函数指针的ostream
的重载,编译器尝试选择到任何可用的重载的转换。此时,它发现最佳的转换序列是到bool
,并打印1
(指针不为0)。
您可以通过更改行为来检查这一点,您可以使用std::cout << std::boolalpha << x
,这将打印true
而不是1
。此外,值得注意的是,VS在这一点上是正确的,因为表达式std::cout << x
需要获取x
的地址,那么该函数是使用的,如果该函数没有定义,则程序是病态的。您可以通过提供定义再次检查:
int f() {}
int main() {
int x(int()); // 1
x( &f ); // 2
}
int x( int(*)() ) { // 3
std::cout << "In x" << std::endl;
}
在x
(1)的定义和参数f
(2)的调用中,我手动执行了从function
到pointer-to-function
的转换——请注意,1中的声明和3 中的定义是相同的签名,并且x( &f )
中的&
将由编译器执行,如果你不这样做。
添加更多的父元素:
int main()
{
int x((int()));
int y = int();
cout << x << " " << y << endl;
}
现在x是整型而不是函数
正如其他人所说,x
是一个函数声明。由于没有为函数指针类型定义预定义的ostream插入器,g++似乎使用隐式bool转换(用于检查函数指针是否为NULL)来找到一种输出方法。
x
从来没有定义,因此它不能完成链接。我怀疑在这种情况下,g++足够聪明,可以看到函数从未被调用,因此不需要担心链接。
您可以尝试在代码中添加函数int x(int(*)()) { return 0xdeadbeef; }
的虚拟定义,然后看看MSVC如何处理它。
c++将第一个解释为函数声明。
This
int x(int());
实际上是一个函数声明,输入是以下签名的函数
int fn(void)
传递给std::cout <<
的函数指针被转换为bool(1),因为它是非null指针。
- 是否可以初始化不可复制类型的成员变量(或基类)
- C++使用整数的压缩数组初始化对象
- C++初始化基类
- 多成员Constexpr结构初始化
- 复制列表初始化的隐式转换的等级是多少
- 内联映射初始化的动态atexit析构函数崩溃
- 如何在C++中初始化嵌套类中的2个memeber
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 没有用于初始化C++中的变量模板的匹配构造函数
- 在未初始化映射的情况下,将值插入到映射的映射中
- C++成员初始化
- 为什么在C++中首先初始化成员类
- 同时具有"聚合初始化"和"模板推导"
- 初始化具有非默认构造函数的std::数组项的更好方法
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 在C和C++中初始化结构中的数组
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 在函数内部的声明中初始化数组,并在外部使用它
- 继承:构造函数,初始化C++11中基类的类C数组成员