views:

272

answers:

5

In C specifically (i suppose this also applies to C++), what is the difference between

char str[4] = "abc";
char *cstr = {"abc"};

Problems arise when i try and pass my "abc" into a function that accepts char**

void f(char** s)
{
  fprintf(stderr, "%s", *s);
}

Doing the following yields a compiler error. If cast to char** (to make compiler happy) program seg faults.

f(&str);

However the following works fine

f(&cstr[0]);
+1  A: 

The braces are actually something of a false trail here.

str is an initialized array, not a pointer: there is no actual pointer variable holding the address of str in your example, so you cannot take its address, and thus the expression &str produces a compile error.

In contrast, cstr is a pointer variable which holds the address of a string constant, and the address of this pointer variable may be taken.

Jeffrey Hantin
You can take an address of an array. The result is a pointer to said array. In this case, the type of such pointer would be `char(*)[4]`.
Pavel Minaev
Yes Pavel, thats the compiler error that i get when using f(. something like "char** does not match char(*)[4]"
+8  A: 

The first line line defines an array of four bytes. These two are equivalent:

char str[4] = "abc";
char str[4] = {'a', 'b', 'c', 0};

The second line declares a pointer to a memory location, which contains the bytes 'a', 'b', 'c', and 0. These two are equivalent:

char *cstr = {"abc"};
char *cstr = "abc";

Your problem arises from mixing char[] and char*. If the function accepts a char**, you must create a char* to get the address of:

char str[4] = "abc";
char *cstr = str;
f(&cstr);
John Millikin
Pavel Minaev
A: 

You may want to try

char str[4] = {'a', 'b', 'c', 0};

or

char str[4];
sprintf(str, "abc");
7macaw
This isn't any different from `char str[4] = "abc"`. Using `sprintf` where `strcpy` would do is also a bad idea.
Pavel Minaev
+4  A: 

This is an example of how pointers and arrays are not equivalent in C. In particular: the rule that arrays decay to pointers is not applied recursively

This means that an array can be used as a pointer, but a pointer-to-array cannot be used as a pointer-to-pointer. This is what you are experiencing here. This is why the compiler complains about mismatched types when you don't cast &str explicitly to char**. That should be your first clue that something is wrong.

The reason that this causes a segfault is this: The way that an array automatically decays to a pointer is by turning into the address of its first element. A pointer to an array is likewise a pointer to the address of the array's first element. So a pointer-to-array and array-as-pointer are the same thing. In other words str, when passed as a pointer, has a value identical to &str. So if you try to make &str into a pointer-to-pointer, it doesn't work, since is just a (single-level) pointer.

For example,

void f(char** pp);
void g(char* p);

char[] str = "abcd"; // Lets say this is allocated at address 0x1234
g(str); // Value of p in g is 0x1234 (by automatic conversion of char[4] to char*)

char* p_str = &str; // Value of p_str is 0x1234
g(p_str); // Value of p in g is again 0x1234 

f(str);  // Illegal, no conversion of char[] to char** (obvious)
f(p_str); // Illegal, no conversion of char* to char** (obvious)
f(&str); // Illegal, no conversion of char*[4] to char** (less obvious)

f((char**)p_str); // Ok, now you're overriding the typecheck

But after that last call to f((char**)p_str), the value of pp in f is still going to be 0x1234 because you haven't modified the value of p_str, you've only suppressed the type-checker's complaint. This means that *pp is going to be 'a', not a pointer to the address that contains 'a'. And that's why you get a segfault when f tries to execute **pp.

Tyler McHenry
A: 

So, &x gets you pointer to x. But pointer to array is the same thing as array. This is reinforced by an exception to the lvalue expression rules, in the case of the & operator, when applied to arrays, the expression type is not converted to be "pointer to ...". (6.2.2.1, ANSI 9899-1990.)

This means that str and &str are the same thing when str is an actual array. But &p where p is really a pointer will get you a double-indirect pointer, i.e., **p.

This would be a lot more clear if the standard just said "& before array is ignored", and I think dmr's publications on C used to really say that. Instead, ANSI phrases it as a conversion rule, far from the definition of the & operator.

DigitalRoss