tags:

views:

108

answers:

1

Why does

static char *opcode_str[] = { "DATA"
                            , "DATA_REQUEST_ACK"
                            , "ACK_TIMER_EXPIRED"
                            , "ACK_UNEXPECTED_SEQ"
                            , "ACK_AS_REQUESTED"
                            } ;

work, but

static char **opcode_str = { "DATA"
                           , "DATA_REQUEST_ACK"
                           , "ACK_TIMER_EXPIRED"
                           , "ACK_UNEXPECTED_SEQ"
                           , "ACK_AS_REQUESTED"
                           } ;

fails with SEGV when opcode_str[0] is printf'd?

I think it's because the second listing has not allocated memory for the five element array of pointers, but I need a more comprehensive explanation.

All the best,

Chris.

+10  A: 

That's correct. You're essentially trying to assign an array to a pointer. GCC 4.4.1 warns about this by default:

opcode_str.c:4: warning: initialization from incompatible pointer type
opcode_str.c:5: warning: excess elements in scalar initializer

It repeats the excess elements warning 4 times, since you're essentially putting 5 pointers where only one will fit. You can use gcc -Werror to force all warnings to be errors.

You could do:

static char **opcode_str = malloc(sizeof(char *) * 5);
opcode_str[0] = "DATA";
opcode_str[1] = "DATA_REQUEST_ACK";
opcode_str[2] = "ACK_TIMER_EXPIRED";
opcode_str[3] = "ACK_UNEXPECTED_SEQ";
opcode_str[4] = "ACK_AS_REQUESTED";

But you've already found the best way to do it. As far as when the error occurs, once you invoke undefined behavior you really can't count on a particular time for problems to manifest.

But I think opcode_str holds a pointer to DATA. So (assuming 32-bit) it will try to interpret the first four bytes at opcode_str ('D','A','T','A') as as the four bytes of a char*.

Matthew Flaschen
Thanks. Why does it not fail at compile time, or when the pointer is initialised?
chrisdew
@chrisdew it's undefined behavior to violate a semantic rule in C. The rule you violate is *The initializer for a scalar shall be a single expression, optionally enclosed in braces.* You have got multiple expressions enclosed in braces, however.
Johannes Schaub - litb
That's it, I'd let the warning scroll right off my screen. Doh!I can now see that I set the char** pointer to the value of the char* pointer to "DATA". "DATA" was then evaluated as if these four/eight bytes held a pointer and this caused the segfault.
chrisdew
As to why it compiles, it would not if you had compiled with the -pedantic flag. This is a case of being liberal in what the compiler will accept, in practice this provides some room for compiling code from other implementations an god know where. It is just an embodiment of the rule of thumb, be liberal in what you accept and strict in what you emit.
Ukko