tags:

views:

324

answers:

4

I saw both :

const char*  arr = {"foo", "bar"};

and

const char*  arr[] = {"foo", "bar"};

What is the correct and generally standard way?

What is the difference between two?

what is the difference between

   const char**arr = {"foo", "bar"};

and

    const char* arr[] = {"foo", "bar"};

and

   const char* * const arr = {"foo", "bar"};

and

   const char* const * const arr = {"foo", "bar"};

Sorry for so many questions, just trying to understand it better...

+7  A: 

This is not correct as it doesn't have the correct level of indirection.

const char* arr = {"foo", "bar"};

This is not correct as it's missing an =. It looks a bit like a function definition.

const char* arr[] {"foo", "bar"};

This is the usual correct form.

const char* arr[] = { "foo", "bar" };

Edit

You can't initialize a pointer from an aggregate initializer (i.e. { ..., ..., ... } ). You can do both

const char* str1 = "A string";

and

const char str2[] = "Another string";

but this is different.

A string literal has type 'array of n char' so can be converted to a pointer whereas an initializer list isn't actually an array, it's just a way to initialize arrays.

Charles Bailey
In older code you will also see: const char **arr = { "foo", "bar", NULL }; which is just just fine.
dmckee
If you see that in older code, I'd suggest that it needs fixing. It initializes `arr` (a const char **) with a const char* pointing at "foo" and discards the excess initalizers. I get "warning: initialization from incompatible pointer type" and "warning: excess elements in scalar initializer"
Charles Bailey
This is one area where C and C++ differ; in C, string literal has type "array N of char", not "array N of const char".
John Bode
@John Bode: Technically true, but even in C, attempting to modify the members of the array results in undefined behaviour so it's I think that it's a little academic that they aren't actually const. It does avoid needing the C++ special rule that allows a string literal to be assigned to a non-const char*.
Charles Bailey
*Mea cupla*. I can't seem to get gcc to accept that syntax with any set of flags. NOt sure what I am remembering at this point...
dmckee
That C++ special rule is only there in order to allow C code to compile, and the C rule is only there because string literals were invented before "const" was. So it's not that C's approach "avoids the need" for it. It's that legacy support in C++ for something that's legacy support even in C, means you have to do something. They decided a special conversion was better than the type of the literal being wrong to start with.
Steve Jessop
@onebyone: I understand the historical perspective but C is a living language too. When they made attempting to change the members of static char arrays undefined behaviour they effectively made them 'const' as far as portable code is concerned. I'm not quite sure what the various merits (it all seems a bit academic) of having a array of non-const chars that you aren't allowed to change, vs. having the conversion from const char array to pointer to non-const and but still not being allowed to change the underlying array members.
Charles Bailey
Yes, either way you need a stupid special rule, in order to ensure that perfectly reasonable code from 1978 still compiles, along with all the stupid code written more recently, that the author didn't realise was stupid, because the stupid compiler didn't reject it ;-) -Wwrite-strings is the only non-stupid way forward, really.
Steve Jessop
A: 

Typically, I use dynamic memory if possible here. I find it is simpler to think about pointers than fighting with the array type.

//Allocate pointers for the strings
char **c = (char**)malloc(maxnumofstrs*sizeof(int));
//allocate memory for each string
for(int i = 0; i < maxnumofstrs; i++)
{
   c[i] = (char*)malloc(maxwidth);
}
Paul Nathan
+1  A: 

There are several approaches. The simplest is to declare an array of arrays of char, like so:

char strs[N][M + 1]; // M is max length of each string
...
strcpy(strs[i], "foo");

All the memory for the strings is statically allocated, but the size of each string in the array is fixed, and you have to size for your longest possible string, which may result in some internal fragmentation. All strings are writable.

Another approach is to declare an array of pointers to char:

char *strs[N];
...
strs[i] = malloc(strlen("bar") + 1);
if (strs[i]) 
  strcpy(strs[i], "bar");

This way you can allocate as much or as little memory as each string in the array needs, and you can resize your strings if necessary. You can also just point to string literals, but remember that literals may not be writable; i.e., you may not be able to do something like:

strs[j] = "foo";
strs[j][0] = 'b'; // may not be allowed on string literal

You can dynamically allocate the whole smash:

char **strs = malloc(sizeof *strs * N);
for (i = 0; i < N; i++)
  strs[i] = malloc(SIZE + 1);

This approach allows you to not only resize each string as necessary, but to resize the number of strings. Note that in this case, strs is not an array type, even though it is being treated as an array.

Note that best practices with respect to malloc() differ between C and C++. In C, it's considered bad practice to cast the result of malloc(). For one thing, it's unnecessary, as void pointers are implicitly cast to other object pointer types. For another, it will supress a diagnostic if you forget to #include stdlib.h or otherwise don't have a prototype for malloc() in scope. Remember that if C doesn't see a prototype for a function before it is referenced, it will assume that the function returns int, which cannot be implicitly converted to a pointer type.

John Bode
A: 

With respect to constness...

const char* constValue = "foo";
constValue = "bar";
constValue[0] = 'x'; // will not work

char* const constPtr = "foo";
constPtr = "bar"; // will not work
constPtr[0] = 'x';

const char* const arr[] = { "foo", "bar", 0 }; // all const

'const char* const' is is often the best solution for something fully constant. One more optimization would be to also make this static if it is declared in a local scope. The 0 ptr is useful for a sentinel value.

Bill Hoag