【问题标题】:Strange zero initialization with g++使用 g++ 进行奇怪的零初始化
【发布时间】:2011-04-28 07:33:57
【问题描述】:

我在使用 g++ 4.4.3 对整数进行初始化时遇到了以下代码的奇怪行为。

  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++ 的这种奇怪行为?

【问题讨论】:

  • @Skavcho,好问题。 x 被解释为函数声明。你可以试试int x(*new int());。它会给你预期的结果为 0 0。
  • @iammilind:是的,但它会泄漏。
  • @Benoit:是的,每次调用main()...
  • @Ben Jackson:不要这么简单地贬低 Benoit 的回答。这段代码 sn-p 只是一个代码示例,旨在拥有尽可能小的执行程序。但是,问题的答案将在实际代码中使用。所以是的重要的是不要泄露。
  • 正如@Jan 所说 - 这可以完成工作: int x((int()));

标签: c++ gcc g++


【解决方案1】:

int x(int()); 被解析为函数声明。

它声明了一个名为 x 的函数,返回一个 int 并接受一个参数,该参数具有返回 int 且不接受任何参数的函数类型。

这被称为最令人头疼的解析。

【讨论】:

  • 为什么“它的价值”(不是引号)是 1 ?它不应该是某个功能的地址吗? (+1,顺便说一句)
  • 最麻烦的解析?不喜欢误解X&lt;Y&lt;Z&gt;&gt;
  • @Ben - 我同意 GMan,因为 X&lt;Y&lt;Z&gt;&gt; 非常明显并且(已经)众所周知,而这个函数声明并不是那么“熟悉”
  • @Ben,其他人:most vexing parse 由 Scott Meyers 于 1992 年首次发表。 (所有的 C++ 解析都有点麻烦,但也只能有一个伊凡雷帝)
【解决方案2】:

补充 GMan 的回答 herex 是一个函数定义)关于为什么 1

输出为1的原因是在调用std::cout &lt;&lt; x的地方,函数衰减为指向函数的指针(语言不允许你将函数作为参数传递给其他函数,所以与数组一样,执行到 pointer-to 的隐式转换)。现在,没有采用函数指针的ostream 的重载,编译器尝试选择转换为任何可用的重载。此时它发现最佳 转换序列是到bool 并打印1(指针不为0)。

您可以通过更改行为来检查这一点,您可以使用std::cout &lt;&lt; std::boolalpha &lt;&lt; x,这将打印true 而不是1。另外,有趣的是,VS 是正确的,因为表达式std::cout &lt;&lt; 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) 的调用中手动执行了从functionpointer-to-function 的转换——请注意,1 中的声明和3中的定义相同的签名,如果你不这样做,x( &amp;f )中的&amp;将由编译器执行。

【讨论】:

    【解决方案3】:

    只需添加更多括号:

    int main()
    
        {
    
            int x((int()));
    
            int y = int();
    
            cout << x << "  " << y <<  endl;
        }
    

    现在 x 是一个 int,而不是一个函数。

    【讨论】:

      【解决方案4】:

      正如其他人所说,x 是一个函数声明。由于没有为函数指针类型定义预定义的 ostream 插入器,g++ 似乎使用隐式 bool 转换(用于检查函数指针是否为 NULL)来寻找输出它的方法。

      另一方面,Visual C++ 抱怨声明的函数x 从未定义,因此它无法完成链接。我怀疑在这种情况下 g++ 足够聪明,可以看到该函数从未被调用,因此不必担心链接。

      您可以尝试将函数 int x(int(*)()) { return 0xdeadbeef; } 的虚拟定义添加到代码中,然后看看 MSVC 对它做了什么。

      【讨论】:

        【解决方案5】:

        C++ 将第一个解释为函数的声明。

        【讨论】:

          【解决方案6】:

          这个

          int x(int());
          

          实际上是一个函数声明,输入是一个遵循签名的函数

          int fn(void)
          

          传递给std::cout &lt;&lt; 的函数指针被转换为bool(1),因为它是一个非NULL 指针。

          【讨论】:

            猜你喜欢
            • 2021-12-06
            • 2016-10-07
            • 2011-02-22
            • 1970-01-01
            • 2016-03-30
            • 2017-05-18
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多