【问题标题】:Different results between gcc and clang when compiling a rather simple c++11 program编译一个相当简单的c ++ 11程序时gcc和clang之间的不同结果
【发布时间】:2013-12-25 12:29:00
【问题描述】:

我试图了解 gcc 与 clang 在这个简单的 C++11 程序的输出中暴露的不同行为是否是由于 clang 中的错误(Xcode 5.0.2,OS X 10.8.5)。代码如下:

#include <iostream>

int main() {


    int matrix[][3]{{1,2,3}, {4,5,6}, {7,8,9}};
    auto dyn_matrix = new int[3][3]{{1,2,3}, {4,5,6}, {7,8,9}};

    std::cout << matrix[0][1] << std::endl;
    std::cout << dyn_matrix[0][1] << std::endl;

    return 0;   
}

如图所示,我正在尝试使用统一初始化来初始化大小为3x3 的匿名(或命名)多维数组。从 MacPorts 使用 gcc 4.7 编译时,会获得预期的输出:

$g++-mp-4.7 -std=c++11 dyn_matrix.cpp -o dyn_matrix 
$ ./dyn_matrix
2
2
$

相反,如果使用 clang,则输出为:

$ clang++ -std=c++11 -stdlib=libc++ dyn_matrix.cpp -o dyn_matrix_clang
$ ./dyn_matrix_clang 
2
4
$  

在这种情况下,结果(显然)是错误的。 clang --version 报道:

Apple LLVM version 5.0 (clang-500.2.75) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin12.5.0
Thread model: posix

该怪谁?我,gcc 还是 clang?

2013 年 12 月 11 日更新:该错误应该已在 r196995 中修复。不幸的是,我们仍然不知道 Apple 更新 Xcode 附带的 clang 版本需要多长时间。

2013 年 12 月 9 日更新:我提交了一份关于 LLVM bugzilla 平台的错误报告。它确实被认为是一个错误,目前正在审查一个补丁,请参阅http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20131209/095099.html

谢谢。

【问题讨论】:

  • Visual Studio 2013 使用 2 2 获取另一个统计信息。
  • +1 提出了一个有趣的好问题。确认 OS X 10.7.5 上的 clang++ v4.2 也会生成输出“2 4”的代码。我的直觉告诉我这是错误的。
  • 嗯。我当前安装的 clang 版本在此代码上崩溃(clang 版本 3.4(主干 193991))。是时候更新了,看看是否更好......
  • 我刚刚尝试使用clang 3.4 (trunk 187030),我可以确认它在尝试编译该代码时会崩溃。
  • 这里有一个更完整的现场演示:coliru.stacked-crooked.com/a/4465ae5a4677dc77。 @Dave matrixint [3][3]dyn_matrixint (*)[3],所以 sizeof 无济于事

标签: c++ gcc c++11 clang


【解决方案1】:

更新:感谢 Faisal Vali 和 Richard Smith,此错误已在 Clang ToT 中得到纠正;查看提交引入的test file


根据 §8.5.1 [dcl.init.aggr] 看来 Clang 是错误的:

11/ 可以在 initializer-list 中省略大括号,如下所示。如果 initializer-list 以左大括号开头,则 initializer-clauses 的后续逗号分隔列表初始化子聚合的成员; initializer-clauses 比成员多是错误的。但是,如果子聚合的 initializer-list 不以左大括号开头,则仅从列表中获取足够的 initializer-clauses 来初始化子聚合;剩下的任何 initializer-clauses 都被留下来初始化当前子聚合所属的聚合的下一个成员。 [ 例子:

float y[4][3] = {
    { 1, 3, 5 },
    { 2, 4, 6 },
    { 3, 5, 7 },
};

是一个完全支撑的初始化:1、3和5初始化数组y[0]的第一行,即y[0][0]y[0][1]y[0][2]。同样,接下来的两行初始化y[1]y[2]。初始化程序提前结束,因此y[3]s 元素被初始化,就好像用float() 形式的表达式显式初始化一样,即用0.0 初始化。在以下示例中,省略了 initializer-list 中的大括号;但是 initializer-list 与上面示例的完全支撑的 initializer-list 具有相同的效果,

float y[4][3] = {
    1, 3, 5, 2, 4, 6, 3, 5, 7
};

y 的初始化程序以左大括号开头,但y[0] 的初始化程序没有,因此使用了列表中的三个元素。同样,接下来的三个依次为y[1]y[2]。 —结束示例 ]

我认为这适用于 §5.3.4 [expr.new]

15/ 创建 T 类型对象的 new-expression 将该对象初始化如下:

  • 如果省略 new-initializer,则对象为默认初始化(§8.5);如果不执行初始化,则该对象具有不确定的值。
  • 否则,new-initializer 会根据§8.5 的初始化规则进行解释,以进行直接初始化。

【讨论】:

  • +1 你第二句引文中的最后一句话基本上就是这么说的。初始化器的解释相同(并且由于它们在代码 sn-p 中相同,因此输出也必须相同)。
  • @Damon:我希望如此,但是过去的经验表明,对于标准中的每一个绝对值,至少有一个例外隐藏在不相关的段落中......甚至不计算这仅适用于{ ... } 确实是一个 new-initializer :/
  • 我提交了一份关于 LLVM bugzilla 平台的错误报告。它确实被认为是一个错误,目前正在审查一个补丁,请参阅lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20131209/…。我将编辑原始问题以反映更改。
  • 起初我没有注意到您已经报告了该错误已得到修复。无论如何,主要问题已被更改以反映更新。谢谢。
猜你喜欢
  • 1970-01-01
  • 2017-07-08
  • 2014-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-25
  • 2016-01-08
相关资源
最近更新 更多