tags:

views:

740

answers:

7

I am trying to create an array of strings in C. If I use this code:

char (*a[2])[14];
a[0]="blah";
a[1]="hmm";

gcc gives me "warning: assignment from incompatible pointer type". What is the correct way to do this?

edit: I am curious why this should give a compiler warning since if I do printf(a[1]);, it correctly prints "hmm".

+6  A: 

Ack! Constant strings:

const char *strings[] = {"one","two","three"};

If I remember correctly.

Oh, and you want to use strcpy for assignment, not the = operator. strcpy_s is safer.

char arr[MAX_NUMBER_STRINGS][MAX_STRING_SIZE]; 
strcpy(arr[0], "blah");
Mark
Is that C99? I don't believe it's possible in ANSI C.
Noldorin
Oh... maybe I'm mistaken; not sure what version it is.
Mark
It's possible in both C89 and C99. It also doesn't matter whether it's with const or without it, although the former is preferred.
avakar
T.E.D.
I think it prevents buffer overflows. I just remember my compiler complaining about it and told me to use that instead.
Mark
strcpy_s is a Microsoft function. It should probably be avoided because it is not in standard C.
Simon
strcpy_s and other "safe functions" are standardized as ISO/IEC TR 24731 (it's an ISO published standard and as such isn't available online for free; the most recent draft is http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1225.pdf)
Pavel Minaev
+2  A: 

The string literals are const char *s.

And your use of parenthesis is odd. You probably mean

const char *a[2] = {"blah", "hmm"};

which declares an array of two pointers to constant characters, and initializes them to point at two hardcoded string constants.

dmckee
A: 

In ANSI C:

char* strings[3];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "baz";
Noldorin
that's definately not right dude. you created a 3-character string, and you're assigning "foo\0" to the first character.
Mark
@Mark: Ok, it's a typo! I missed out the `*`. Take it easy on me. :P
Noldorin
posted the same thing, SO rejected my submission.that's a 3 char string, useful for storing words like "is", "if", "it", "of", etc. not too good :(
KevinDTimm
@kevindtimm: See my update.
Noldorin
@Mark: Might be nice to remove the down-vote too please. :)
Noldorin
I never did down-vote you actually.
Mark
@Mark: Ah, fair enough then. :)
Noldorin
**Please** put the `*` by the identifier. It's not part of the type. (Beginners get bit by this a lot.)
Zifre
Zifre - agreed. This 'works', but it's not 'right'.
KevinDTimm
@Zifre: I wholly disagree. It very much is part of the type - a "char pointer" in this case. What would you say anyway... it's part of the variable name? I have seen many a competent programmer use this style.
Noldorin
In fact, if you know C#, you should be aware that Microsoft agrees with this, and explicitly *disallows* putting the star after the space.
Noldorin
To add another point: it can never be confused with the dereference operator as well. Convinced yet? Well, probably not, but I've made my argument.
Noldorin
Too bad we're not talking about C#.....
KevinDTimm
A: 

Your code is creating an array of function pointers. Try

char* a[size];

or

char a[size1][size2];

instead.

See wikibooks to arrays and pointers

Dario
+8  A: 

If you don't want to change the strings the you could simply do

const char *a[2];
a[0]="blah";
a[1]="hmm";

When you do it like this you will allocate an array of two pointers to chars. These pointers will then be set to the addresses of the static strings "blah" and "hmm".

If you do want to be able to change the actual string content, the you have to do something like

char a[2][14];
strcpy(a[0], "blah");
strcpy(a[1], "hmm");

This will allocate two consecutive arrays of 14 chars each, after which the content of the static strings will be copied into them.

Mikael Auno
Agreed. But are the '\n' intended ?
RaphaelSP
No, they weren't. Removed.
Mikael Auno
+1  A: 

Here are some of your options:

char a1[][14] = { "blah", "hmm" };
char* a2[] = { "blah", "hmm" };
char (*a3[])[] = { &"blah", &"hmm" };  // only since you brought up the syntax -

printf(a1[0]); // prints blah
printf(a2[0]); // prints blah
printf(*a3[0]); // prints blah

The advantage of a2 is that you can then do the following with string literals

a2[0] = "hmm";
a2[1] = "blah";

And for a3 you may do the following:

a3[0] = &"hmm";
a3[1] = &"blah";

For a1 you will have to use strcpy even when assigning string literals. The reason is that a2, and a3 are arrays of pointers and you can make their elements (i.e. pointers) point to any storage, whereas a1 is an array of 'array of chars' and so each element is an array that "owns" its own storage (which means it gets destroyed when it goes out of scope) - you can only copy stuff into its storage.

This also brings us to the disadvantage of using a2 and a3 - since they point to static storage (where string literals are stored) the contents of which cannot be reliably changed (viz. undefined behavior), if you want to assign non-string literals to the elements of a2 or a3 - you will first have to dynamically allocate enough memory and then have their elements point to this memory, and then copy the characters into it - and then you have to be sure to deallocate the memory when done.

Bah - I miss C++ already ;)

p.s. Let me know if you need examples.

Faisal Vali
+1  A: 

There are several ways to create an array of strings in C. If all the strings are going to be the same length (or at least have the same maximum length), you simply declare a 2-d array of char and assign as necessary:

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1];
...
strcpy(strs[0], aString); // where aString is either an array or pointer to char
strcpy(strs[1], "foo");

You can add a list of initializers as well:

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1] = {"foo", "bar", "bletch", ...};

This assumes the size and number of strings in the initializer match up with your array dimensions. In this case, the contents of each string literal (which is itself a zero-terminated array of char) are copied to the memory allocated to strs. The problem with this approach is the possibility of internal fragmentation; if you have 99 strings that are 5 characters or less, but 1 string that's 20 characters long, 99 strings are going to have at least 15 unused characters; that's a waste of space.

Instead of using a 2-d array of char, you can store a 1-d array of pointers to char:

char *strs[NUMBER_OF_STRINGS];

Note that in this case, you've only allocated memory to hold the pointers to the strings; the memory for the strings themselves must be allocated elsewhere (either as static arrays or by using malloc() or calloc()). You can use the initializer list like the earlier example:

char *strs[NUMBER_OF_STRINGS] = {"foo", "bar", "bletch", ...};

although instead of copying the contents of the string constants, you're simply storing the pointers to them. Note that string constants may not be writable; you can reassign the pointer, like so

strs[i] = "bar";
strs[i] = "foo";

but you may not be able to change the string's contents; i.e.,

strs[i] = "bar";
strcpy(strs[i], "foo");

may not be allowed.

You can use malloc() to dynamically allocate the buffer for each string and copy to that buffer:

strs[i] = malloc(strlen("foo") + 1);
strcpy(strs[i], "foo");

BTW,

char (*a[2])[14];

declares a as a 2-element array of pointers to 14-element arrays of char.

John Bode