2

I am new to C and I am trying to experiment with multi-dimensional arrays. I have the following example where I am trying to initialize a multi-dimensional array:

char matrix[5][10];  

matrix[0] = {'0','1','2','3','4','5','6','7','8','9'};
matrix[1] = {'a','b','c','d','e','f','g','h','i','j'};
matrix[2] = {'A','B','C','D','E','F','G','H','I','J'};
matrix[3] = {'9','8','7','6','5','4','3','2','1','0'};
matrix[4] = {'J','I','H','G','F','E','D','C','B','A'};

At first glance it would appear that this type of declaration would be valid since a multi-dimensional array is an array of arrays; however, this example fails to properly compile and I am not entirely certain as to why.

I am aware that I am able to initialize a multi-dimensional array using the following notation:

char matrix2[5][10] =
{
    {'0','1','2','3','4','5','6','7','8','9'},
    {'a','b','c','d','e','f','g','h','i','j'},
    {'A','B','C','D','E','F','G','H','I','J'},
    {'9','8','7','6','5','4','3','2','1','0'},
    {'J','I','H','G','F','E','D','C','B','A'},
};

However, what if I do not know the contents of the array at declaration time and would like to populate this array with data at a later point. I could initialize each individual element as follows:

matrix[0][0] = '0';
matrix[0][1] = '1';
matrix[0][2] =  '2';
etc....

I am wondering if it somehow possible to declare each array using my original approach:

matrix[0] = {'0','1','2','3','4','5','6','7','8','9'};
matrix[1] = {'a','b','c','d','e','f','g','h','i','j'};
etc...

I tried to use strcpy as follows:

strcpy(matrix[0], "012345678");
strcpy(matrix[1], "abcdefghi");

It appears that this might work if the multi-dimensional array was an array of null-terminated strings, but what would be an equivalent to a multi-dimensional array of integers or other data structures. Any help is appreciated. Thank you.

Adam Bak
  • 1,234
  • 12
  • 15

1 Answers1

7

There is a difference between an initialiser and an assignment. They both are introduced by =, but they are syntactically and semantically different.

For an initializer, C can deduce the type of the right side of the = by the left side type. For an assignment, the type must be know with the right side and be compatible with the left side (lvalue) type. Most imporatnt here: the compiler has no type information from the lvalue when evaluating the right side (it does not even started the assignment expression at that moment).

An initialiser must only be used with a definition (you seem to confuse this with declaration - pay heed, they are well-defined terms with different meaning). SO once you have complete the definition, you cannout use an _initialiser anymore.

Since C99, you can use a compound literal. This is much like a string literal ("Hello world"), but you have to tell the compiler its exact type (the string literal's type is given by the "). Additionally, C does not allow arrays to be simply assigned like scalars or structs. Thus you have to memcpy instead of the assignment:

memcpy(matrix[0], (char [10]){ 1,2,3,4, ... }, 10));

Note the (char ...} part which is the compound literal. This is not a cast, but tells the compiler which type the object in the curly braces has (here: array of 10 chars). A cast OTOH is a prefix expression and changess the type of the following expression. Note that you can use a _string literal here instead, but that does obviously not work for other array element types. Also note that for string literals you cannot define their length other than providing as many characters. So if you forget two characters, you will invoke undefined behaviour for reading outside array boundaries. (Two because of the implicit trailing '\0' character).

For the dimensions, you better use symbolic constants, (i.e. macros in C) like #define ROWS 10 to avoid magic numbers in your code - you have to use these values multiple times.

For an array of pointers, you can use the easier way:

matrix[0] = (char [10]){ 1,2,3,4,...};

Warning: This does not allow to change entries like the first version does, because literals must not be written to!

Extra: This version also works for structs (not just pointers to them), because C does allow assignments for those. To me, this looks like one of the irregularities of C one has to live with.

Community
  • 1
  • 1
too honest for this site
  • 11,417
  • 3
  • 27
  • 49
  • How does it differ from a cast? – clearlight Oct 04 '15 at 16:59
  • @nerdistcolony: It is not a cast, but belongs to the braces. The contents between the braces is not cast to the given type, but tells the compiler the type of the parentethised(!;-) construct. A cast is an expression. – too honest for this site Oct 04 '15 at 17:01
  • Thanks for the insight. I'm going to have to read up on how C treats initialization and assignment statements. – Adam Bak Oct 04 '15 at 17:04
  • @BLUEPIXY: I edited. Thanks again for the correction. I normally use this with structs only, so I forgot in the first place. – too honest for this site Oct 04 '15 at 17:10
  • @Olaf - I tried an example with int pointers and I can't figure out how to make it work. Can you show how I would initialize an array of pointers using that technique? I tried 'casting' as (int **), (int *), (int *[]) ... nothing worked: http://ideone.com/b6R5cy, also http://ideone.com/b6R5cy – clearlight Oct 04 '15 at 17:18
  • @nerdistcolony: I wonder a bit: You have the same problem, or is that an alternative account you asked with? – too honest for this site Oct 04 '15 at 17:24
  • @nerdistcolony: There is on need to cast. And you should **never** cast just to make the code compile unless you really know **why** you have to and are aware about the implications. Use it as I wrote, just change the type to `int`. – too honest for this site Oct 04 '15 at 17:26
  • @nerdistcolony: `int *ar2[3];` `ar2[0] = (int [2]){ 1, 2};` – too honest for this site Oct 04 '15 at 17:31
  • @Olaf - I created a new account after seeing BLUEPIXY's comment. I first tried it on the command line of my Mac running cc from the latest version of XCode. Same kind of errors. – clearlight Oct 04 '15 at 17:43
  • @Olaf - I will have to play with this. Why can't I cast the {} contents as (int *[2})? or pass &a, &b, ...? I guess what you're showing is a way I can assign literal addresses to an array... (elements 0 and 1 of a 3 element array?) – clearlight Oct 04 '15 at 17:45
  • Please understand that this is no debugging service. However, the two lines I posted in my last comment **do** compile. Note that you (as I stated) need C99 at least. VisualC is **not** compliant, clang and gcc **are**, but you might have to enforce this standard in gcc. Not sure about clang (it will not harm, however). If you still have problems, post a new question with **specific** problem statement and [mcve]. – too honest for this site Oct 04 '15 at 18:05
  • @GRC: Not, it does not! Please read my answer carefully. You cannot assign an array in C, but have to use `memcpy` as I proposed. – too honest for this site Oct 04 '15 at 18:22
  • @Olaf give an example in which it does not give an error :) –  Oct 04 '15 at 19:37
  • `int ar[2][2]; memcpy(ar[0], (int [2]){ 1,2}, 2 * sizeof(int));` – too honest for this site Oct 04 '15 at 20:06
  • @Olaf that is not a line giving me error it is this one `matrix[0] = (char [10]){ 1,2,3,4,...};` –  Oct 04 '15 at 20:08
  • @GRC: Sorry, but I really have no idea how I can make you clear: I already stated in my answer that this is not possible in C, because you cannot assign arrays. Problem is that the right hand side decays to a pointer to the first element _before_ the assignment expression is processed (this is the reason so many people think an array is the same as a pointer), so the assignment never sees the array actually, but only the pointer. You **have to** use `memcpy`. – too honest for this site Oct 04 '15 at 20:30