C++和C中括号的含义

5

我刚刚在 C++ 中遇到了一个麻烦的错误。我有一系列寄存器和值,它们被包装在一个结构体中,然后这些结构体被初始化为一个数组。但是,我不小心输入了 () 而不是 {}。以下是一些测试代码:

#include <stdio.h>

struct reg_val {
        unsigned reg;
        unsigned val;
};

struct reg_val faulty_array[] = { 
        {0x5001, 0xff},
        {0x5580, 0x01},
        (0x5580, 0x02), //<- THIS LINE IS THE PROBLEM
        (0x5589, 0x00), //<- AND THIS LINE
};

struct reg_val good_array[] = { 
        {0x5001, 0xff}, 
        {0x5580, 0x01}, 
        {0x5580, 0x02},
        {0x5589, 0x00},
};

int main()
{
        unsigned i;
        unsigned faulty_size = sizeof(faulty_array) / sizeof(struct reg_val);
        printf("Size of faulty array: %d\n", faulty_size);

        for (i = 0; i < faulty_size; ++i) {
                printf("faulty reg: %x  val: %x\n", faulty_array[i].reg,
                       faulty_array[i].val);
        }   

        unsigned good_size = sizeof(good_array) / sizeof(struct reg_val);
        printf("\nSize of good array: %d\n", good_size);
        for (i = 0; i < good_size; ++i) {
                printf("good reg: %x  val: %x\n", good_array[i].reg,
                       good_array[i].val);
        }   
        return 0;
}

我更熟悉C语言,令我惊讶的是,这段代码在使用g++编译时仍然可以通过:

$ g++ -Wall array.cc
array.cc:11: warning: left-hand operand of comma has no effect
array.cc:12: warning: left-hand operand of comma has no effect
array.cc:13: warning: missing braces around initializer for ‘reg_val’
$ ./a.out 
Size of faulty array: 3
faulty reg: 5001  val: ff
faulty reg: 5580  val: 1
faulty reg: 2  val: 0       <-- the first value gets discarded as mentioned in the compiler warning

Size of good array: 4
good reg: 5001  val: ff
good reg: 5580  val: 1
good reg: 5580  val: 2
good reg: 5589  val: 0

这段代码显然在C编译器上无法编译,那么是什么让C++编译器(虽然不情愿)接受这段代码呢?

3个回答

4
为了回答你的问题,我首先要解释一下:为什么这段代码在C语言中无法编译通过?它无法编译通过是因为:
initializer element is not constant

为了更好的效果,让我们从C语言中省略掉{}
struct reg_val faulty_array[] = { 
        {0x5001, 0xff},
        {0x5580, 0x01},
        0x5580, 0x02, //<- THIS LINE IS THE PROBLEM
        0x5589, 0x00, //<- AND THIS LINE
};

现在该程序输出的是:
Size of faulty array: 4
faulty reg: 5001  val: ff
faulty reg: 5580  val: 1
faulty reg: 5580  val: 2
faulty reg: 5589  val: 0

这在C标准(以及C++)中是完全允许的。C(和C++)将大括号展开以初始化结构体的元素(这将会回来)。你的代码在C中失败,因为具有静态存储期的对象必须用常量表达式或包含常量表达式的聚合初始化器进行初始化。C不将(0x5580, 0x02)视为常量表达式。
这个(不幸的是)在C++中编译是可以通过的,因为C++将两个常量表达式之间的逗号运算符视为常量表达式,所以你的代码更像:
struct reg_val faulty_array[] = { 
        {0x5001, 0xff},
        {0x5580, 0x01},
        0x02,
        0x00,
};

...which is, of course, allowed.

struct reg_val faulty_array[] = { 
        {0x5001, 0xff},
        {0x5580, 0x01},
        {0x02, 0x00},
};

啊,谢谢。我明白了。那很有道理。但是gcc似乎对我“拍扁大括号”不太满意。$ gcc -Wall array.c array.c:13: warning: missing braces around initializer - Lucas
使用gcc -Wall编译时,如果代码中出现if (c = something())这样的语句,编译器会提示你加上括号,因为它被当做真值使用。仅仅因为某些语句在ANSI C标准下是合法的,并不意味着它们不会产生警告。 - Travis Gockel
你是正确的,gcc -std=c89 -pedantic array.c编译没有警告。 - Lucas

4
你认为它在C中编译失败的原因是什么?
C++:http://ideone.com/KLPh4 C:http://ideone.com/VYUbL 请注意警告。我无法强调这一点。警告有助于您捕捉此类错误。
好的,在C中的错误消息使区别非常明显:C要求初始化程序为常量,而不是任意表达式。我不明白为什么它们不被认为是常量,因为在C中这样编译没有问题: http://ideone.com/p5I1d

4
在C语言中,它无法通过编译,因为“初始化元素不是常量”。换句话说:(expr, expr)不是一个常量表达式或聚合初始化。 - Travis Gockel
1
C标准中所谓的“常量”,在C++中被称为“字面值”。如果你有一个const int C = 5,那么C不是一个常量。 - Travis Gockel
1
@BenVoigt 需要一个“常量表达式”,只有整数常量的简单算术表达式仍然是常量表达式。逗号运算符的应用程序不是。现在,为什么(constant_expression, constant_expression)不是常量表达式,你得问委员会。 - Daniel Fischer
2
@bames53 是的,这是一个bug。6.6.(3)中写道:“常量表达式不得包含赋值、递增、递减、函数调用或逗号运算符,除非它们包含在不被评估的子表达式中”(n1570和C99)。 - Daniel Fischer
1
@bames53 噢,也许不是一个 bug,第 10 段说:“实现可能接受其他形式的常量表达式。” - Daniel Fischer
显示剩余5条评论

3

C++有一个逗号运算符,它评估其两个操作数并返回其右侧操作数的值,忽略其左侧。您可以在此处更清楚地看到它。

而C语言也是如此,显然 =) (感谢@BenVoigt)


2
问题是:“C++与C有什么区别?”答案不是“逗号运算符”。 - Ben Voigt
@BenVoigt:我不懂C语言,我只是在解释它为什么可以在C++中编译,并且我假设他尝试在C语言中编译它,但那个特定的部分失败了。抱歉。 - Ry-
我认为C++会在编译时尝试计算这些表达式,当它们具有常量操作数时,而C不会,这解释了C和C++之间的错误与警告之间的区别。 - antlersoft

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接