C double character pointer declaration and initialization
I always though that declaring
char *c = "line";
was the same as
char c[] = "line";
and so I did
char **choices = { 开发者_开发知识库"New Game", "Continue Game", "Exit" };
Which gives me an incompatible pointer type, where
char *choices[] = { "New Game", "Continue Game", "Exit" };
doesn't. Any help on understanding this?
char *c = "line";
is not the same as
char c[] = "line";
it's really the same as
static const char hidden_C0[] = "line";
char *c = (char *)hidden_C0;
except that the variable hidden_C0
is not directly accessible. But you'll see it if you dump out generated assembly language (it will usually have a name that isn't a valid C identifier, like .LC0
). And in your array-of-string-constants example, the same thing is going on:
char *choices[] = { "New Game", "Continue Game", "Exit" };
becomes
const char hidden_C0[] = "New Game";
const char hidden_C1[] = "Continue Game";
const char hidden_C2[] = "Exit";
char *choices[] = { (char *)hidden_C0, (char *)hidden_C1, (char *)hidden_C2 };
Now, this is a special case behavior that is available only for string constants. You cannot write
int *numbers = { 1, 2, 3 };
you must write
int numbers[] = { 1, 2, 3 };
and that's why you can't write
char **choices = { "a", "b", "c" };
either.
(Your confusion is a special case of the common misconception that arrays are "the same as" pointers in C. They are not. Arrays are arrays. Variables with array types suffer type decay to a pointer type when they are used (in almost every context), but not when they are defined.)
Well, they're not the same. It's just easier for most people to think of them as being the same so everyone starts to think that way until they run into a problem like the above :-)
I was going to write something long and winded, but then I figured... Someone else must have done this already. And they have. This is a pretty good explanation:
http://www.lysator.liu.se/c/c-faq/c-2.html
The easiest way to think about it is that when you do something like:
char *foo = "something";
You're really doing something like:
char randomblob[] = "something"; char *foo = randomblob;
Now... that's not really an accurate picture (though I'm not a compiler expert). It at least lets you think about things in a slightly more correct fashion.
So, back to your problem, if I understand things right (which is never guaranteed), you can't do your example line #3 in C. You're right that someone could write a compiler that would do the right thing here, but gcc doesn't. The 4th example, however, does the "right thing" and gives you "an array of pointers that are each pointing to a const char array themselves".
I once ran across a web page that would translate a complex C type into English. That was probably in the early 90s though but I bet if you google enough it would give you a more accurate wording description than the one I just whipped up.
It's ok, just write
char **choices = (char *[]){ "New Game", "Continue Game", "Exit" };
However, choices
can be used only for linear addressing. For example:
printf ("%s", &(*choices)[0]);
outputs: New Game
printf ("%s", &(*choices)[1]);
outputs: ew Game
printf ("%s", &(*choices)[9]);
outputs: Continue Game
So it's not a joke, it's a valid initialization. Just another kind of usage.
You can also find a very close example here, explaining Compound Literals notion.
Online C standard (draft n1256):
6.7.8 Initialization
...
11 The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.
...
16 Otherwise, the initializer for an object that has aggregate or union type shall be a brace enclosed list of initializers for the elements or named members.
Emphasis added.
char **
is a scalar type, not an aggregate, and so is not compatible with the initializer {"New Game", "Continue Game", "Exit"}
. By contrast, char *[]
is an aggregate (array) type.
Similarly, you couldn't write something like
int *foo = {1, 2, 3};
because int *
isn't an array type.
Your understanding of
char *c = "line";
and
char c[] = "line";
is slightly off; they are not the same. The first form copies the address of the string literal to the pointer value c
. The second form copies the contents of the array expression "line"
to the buffer designated by c
.
精彩评论