Reputation: 14129
I just had a nasty bug in C++. So I had list of registers and values, that are wrapped in a struct and then those structs are initialized in an array. But then I accidentally typed ()
instead of {}
. Here is some test code:
#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;
}
I am more familiar with C and to my surprise this still compiled with 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
This code would obviously fail to compile with a C compiler, what's the difference in C++ that makes a C++ compiler (although grudgingly) accept this code?
Upvotes: 5
Views: 301
Reputation: 27633
To answer your question, I will first answer: Why does this fail to compile in C? Well, it fails to compile due to:
initializer element is not constant
For good measure, let's drop the {}
s from C:
struct reg_val faulty_array[] = {
{0x5001, 0xff},
{0x5580, 0x01},
0x5580, 0x02, //<- THIS LINE IS THE PROBLEM
0x5589, 0x00, //<- AND THIS LINE
};
Now the program outputs:
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
This is perfectly allowed by the C standard (and C++). C (and C++) flatten braces to initialize elements of structures (this will come back). Your code fails in C because objects with static storage duration must initialized with constant expressions or with aggregate initializers containing constant expressions. C does not treat (0x5580, 0x02)
as a constant expression.
This (unhappily) compiles in C++ because C++ treats the comma operator between two constant expressions as a constant expression, so your code is more like:
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},
};
Upvotes: 4
Reputation: 283624
What makes you think it would fail to compile in C?
C++: http://ideone.com/KLPh4 C: http://ideone.com/VYUbL
Pay attention to your warnings. I can't stress this enough. Warnings are there to help you catch mistakes like this.
Well, the error message in C makes the difference perfectly clear: C requires the initializers to be constants, not arbitrary expressions. It makes no sense to me why those aren't considered constants, since this compiles fine in C:
Upvotes: 4
Reputation: 224904
C++ has a comma operator that evaluates both of its operands and returns the value of its right operand, ignoring its left. You can see it more clearly here.
And C too, apparently =) (Thanks, @BenVoigt)
Upvotes: 3