33

I'm trying to assign a compound literal to a variable, but it seems not to work, see:

  int *p[] = (int *[]) {{1,2,3},{4,5,6}};

I got a error in gcc.

but if I write only this:

  int p[] = (int []) {1,2,3,4,5,6};

Then it's okay.

But is not what I want.

I don't understand why the error occurrs, because if I initialize it like a array, or use it with a pointer of arrays of chars, its okay, see:

  int *p[] = (int *[]) {{1,2,3},{4,5,6}}; //I got a error
  int p[][3] = {{1,2,3},{4,5,6}}; //it's okay
  char *p[] = (char *[]) {"one", "two"...}; // it's okay!

Note I don't understand why I got an error in the first one, and please I can't, or I don't want to write like the second form because it's needs to be a compound literals, and I don't want to say how big is the array to the compiler. I want something like the second one, but for int values.

Thanks in advance.

Lanorkin
  • 7,310
  • 2
  • 42
  • 60
drigoSkalWalker
  • 2,742
  • 9
  • 32
  • 45

5 Answers5

32

First, the casts are redundant in all of your examples and can be removed. Secondly, you are using the syntax for initializing a multidimensional array, and that requires the second dimension the be defined in order to allocate a sequential block of memory. Instead, try one of the two approaches below:

  • Multidimensional array:

    int p[][3] = {{1,2,3},{4,5,6}};
    
  • Array of pointers to one dimensional arrays:

    int p1[] = {1,2,3};
    int p2[] = {4,5,6};
    int *p[] = {p1,p2};
    

The latter method has the advantage of allowing for sub-arrays of varying length. Whereas, the former method ensures that the memory is laid out contiguously.

Another approach that I highly recommend that you do NOT use is to encode the integers in string literals. This is a non-portable hack. Also, the data in string literals is supposed to be constant. Do your arrays need to be mutable?

int *p[] = (int *[]) {
    "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00",
    "\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"
};

That example might work on a 32-bit little-endian machine, but I'm typing this from an iPad and cannot verify it at the moment. Again, please don't use that; I feel dirty for even bringing it up.

The casting method you discovered also appears to work with a pointer to a pointer. That can be indexed like a multidimensional array as well.

int **p = (int *[]) { (int[]) {1,2,3}, (int[]) {4,5,6} };
yhw42
  • 3,334
  • 2
  • 27
  • 24
Judge Maygarden
  • 26,961
  • 9
  • 82
  • 99
  • Yes, it's redundant in my example, but it's would good for a function parameter, and how can I avoid the dimension with pointer? like the second example... thanks for your answer. – drigoSkalWalker Mar 31 '11 at 06:39
  • @drigoSkalWalker - you have to tell C the last array size, since the C will not try and guess on its own. It is important for pointer arithmetics etc. – Eli Iser Mar 31 '11 at 06:43
  • Thanks guy, Are there some way to write the your first example like the last one? Because I can't say to compiler how big is my array, and it needs to be a array inside another....I begin to think that it is not possible...Thanks anyway. – drigoSkalWalker Mar 31 '11 at 06:46
  • You could encode the integers in string literals, but that would not be portable. – Judge Maygarden Mar 31 '11 at 06:54
  • Hi, this hack is nicer, see, I found an approach to get what I want... If I write this in this way : int **p = (int *[]) {{1,2,3},(int []){4,5,6}}; it works, it will help me a lot, but see, writing (int []) in every array is no so perfect because I will need to use macros to obfuscate it...maybe another solution for my problem? – drigoSkalWalker Mar 31 '11 at 07:17
  • The correct is : int **p = (int *[]) {(int []){1,2,3},(int []){4,5,6}}; – drigoSkalWalker Mar 31 '11 at 07:19
  • Yeah, that seems to work. It is definitely better than using string literals. – Judge Maygarden Mar 31 '11 at 07:31
  • 10
    the type in `()` is not a cast -- it is a part of the compound literal syntax. – u0b34a0f6ae Dec 08 '11 at 14:06
  • @u0b34a0f6ae Yes. That's what I was thinking too. I didn't trust myself atm because I'm so dead tired but I thought this cannot possibly be correct what the answer started out as. It's not a cast but indeed part of the compound literal syntax. Thanks for confirming this. Actually if I wasn't so tired I would say that this answer has the wrong idea (at least for the first part). – Pryftan Apr 30 '20 at 16:01
  • On second thought: it must be that the answer is trying to show the differences. Still your point is valid. But to Judge: compound literals isn't casting. As the other commentator points out the () is part of the compound literal syntax. – Pryftan Apr 30 '20 at 16:17
13

First understand that "Arrays are not pointers".

int p[] = (int []) {1,2,3,4,5,6};

In the above case p is an array of integers. Copying the elements {1,2,3,4,5,6} to p. Typecasting is not necessary here and both the rvalue and lvalue types match which is an integer array and so no error.

int *p[] = (int *[]) {{1,2,3},{4,5,6}};

"Note I don't understand why I got a error in the first one,.."

In the above case, p an array of integer pointers. But the {{1,2,3},{4,5,6}} is a two dimensional array ( i.e., [][] ) and cannot be type casted to array of pointers. You need to initialize as -

int p[][3] = { {1,2,3},{4,5,6} };
  // ^^ First index of array is optional because with each column having 3 elements
  // it is obvious that array has two rows which compiler can figure out.

But why did this statement compile ?

char *p[] = {"one", "two"...};

String literals are different from integer literals. In this case also, p is an array of character pointers. When actually said "one", it can either be copied to an array or point to its location considering it as read only.

char cpy[] = "one" ;
cpy[0] = 't' ;  // Not a problem

char *readOnly = "one" ;
readOnly[0] = 't' ;  // Error because of copy of it is not made but pointing
                     // to a read only location.

With string literals, either of the above case is possible. So, that is the reason the statement compiled. But -

char *p[] = {"one", "two"...}; // All the string literals are stored in 
                               // read only locations and at each of the array index 
                               // stores the starting index of each string literal.

I don't want to say how big is the array to the compiler.

Dynamically allocating the memory using malloc is the solution.

Hope it helps !

Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • You said that {{1,2,3},{4,5,6}} is a two dimensional array , and cannot be type casted to array of ponters, but I learned that any array is a pointer, and it is passed like a pointer to functions, thanks for your answer!!! – drigoSkalWalker Mar 31 '11 at 07:18
  • If array is a pointer, then try this - `void foo( int **twoDimensionalArray){}` and try to pass `p` as mentioned in your example which have the okey comment. – Mahesh Mar 31 '11 at 07:20
  • 3
    Just because it is possible for a 1D array doesn't mean that **array is a pointer**. – Mahesh Mar 31 '11 at 07:21
  • Sorry but I can't say how big is it to compiler, much less use malloc, the perfect approach is to use pointer instead, I found a solution writing : int **p = (int *[]) {(int []){1,2,3},(int []){4,5,6}}; but I need to cast to every array inside the main array, I'm finding a solution to do without it. – drigoSkalWalker Mar 31 '11 at 07:27
  • 1
    There are subtle differences between arrays and pointer, but I don't think it will matter in your case other than I itialization options. – Judge Maygarden Mar 31 '11 at 07:43
  • 2
    One of differences is that you can't change the address of array, but you can do that for pointer. For that reason you can call array `static address` and pointer - `dynamic address`. – Agnius Vasiliauskas Apr 05 '11 at 16:22
  • @drigoSkalWalker No. As Judge says there are differences. Whereas `int *p;` isn't allocated `int a[4];` is: `a[0] = 1;` is perfectly safe but `p[1] = 1` (or `*(p+1)`) is not. I hope I typed that wrong (can barely see atm!). Now passing `a` to a function it will act as a pointer to the first element but that's not the same thing. There are other ways this shows itself. – Pryftan Apr 30 '20 at 16:09
5

Since nobody's said it: If you want to have a pointer-to-2D-array, you can (probably) do something like

int (*p)[][3] = &(int[][3]) {{1,2,3},{4,5,6}};

EDIT: Or you can have a pointer to its first element via

int (*p)[3] = (int[][3]) {{1,2,3},{4,5,6}};

The reason why your example doesn't work is because {{1,2,3},{4,5,6}} is not a valid initializer for type int*[] (because {1,2,3} is not a valid initializer for int*). Note that it is not an int[2][3] — it's simply an invalid expression.

The reason why it works for strings is because "one" is a valid initializer for char[] and char[N] (for some N>3). As an expression, it's approximately equivalent to (const char[]){'o','n','e','\0'} except the compiler doesn't complain too much when it loses constness.

And yes, there's a big difference between an initializer and an expression. I'm pretty sure char s[] = (char[]){3,2,1,0}; is a compile error in C99 (and possibly C++ pre-0x). There are loads of other things too, but T foo = ...; is variable initialization, not assignment, even though they look similar. (They are especially different in C++, since the assignment operator is not called.)

And the reason for the confusion with pointers:

  • Type T[] is implicitly converted to type T* (a pointer to its first element) when necessary.
  • T arg1[] in a function argument list actually means T * arg1. You cannot pass an array to a function for Various Reasons. It is not possible. If you try, you are actually passing a pointer-to-array. (You can, however, pass a struct containing a fixed-size array to a function.)
  • They both can be dereferenced and subscripted with identical (I think) semantics.

EDIT: The observant might notice that my first example is roughly syntactically equivalent to int * p = &1;, which is invalid. This works in C99 because a compound literal inside a function "has automatic storage duration associated with the enclosing block" (ISO/IEC 9899:TC3).

tc.
  • 33,468
  • 5
  • 78
  • 96
  • 1
    Could you add some example where T[] is implicitly cvonverted to T(*)[]? You explanation is thorough! I think your anwer really see through the mist of compound literal and points out the true problem! – Allan Ruin May 28 '12 at 12:36
  • 1
    After some test, I think it should be `int (*p)[3] = (int[][3]) {{1,2,3},{4,5,6}};` – Allan Ruin May 28 '12 at 13:18
1

The one that you are using is array of int pointers. You should use pointer to array :

int (*p)[] = (int *) {{1,2,3}, {4,5,6}}

Look at this answer for more details.

Community
  • 1
  • 1
Karan
  • 12,724
  • 6
  • 40
  • 33
  • 1
    I don't think that even compiles. – tc. Apr 12 '11 at 00:23
  • 1
    @tc Check this answer http://stackoverflow.com/questions/859634/c-pointer-to-array-array-of-pointers-disambiguation and you should try it first before commenting. – Karan Apr 12 '11 at 04:02
  • I'm still pretty sure it does not compile. – tc. Apr 12 '11 at 04:33
  • 1
    Here is the code: #include void main() { int (*p)[] = (int *) {{1,2,3},{4,5,6}}; } – Karan Apr 12 '11 at 04:38
  • I was wrong; it appears to compile, but is equivalent to `int (*p)[] = (int*)1;` ("braces around scalar initializer", "initialization makes pointer from integer without a cast", "excess elements in scalar initializer", "initialization from incompatible pointer type", "return type from 'main' is not 'int'"). I stand by my downvote. – tc. Apr 12 '11 at 04:50
  • @Karan Maybe. But your example is undefined. In other words the compiler is free to do whatever it wants even if that's halt and catch fire (https://en.wikipedia.org/wiki/Halt_and_Catch_Fire_(computing) ). `main()` must return an int. This is not negotiable. Even if it appears to work with your compiler it's wrong. – Pryftan Apr 30 '20 at 16:14
1

It seems you are confusing pointers and array. They're not the same thing! An array is the list itself, while a pointer is just an address. Then, with pointer arithmetic you can pretend pointers are array, and with the fact that the name of an array is a pointer to the first element everything sums up in a mess. ;)

int *p[] = (int *[]) {{1,2,3},{4,5,6}};      //I got a error

Here, p is an array of pointers, so you are trying to assign the elements whose addresses are 1, 2, 3 to the first array and 4, 5, 6 to the second array. The seg fault happens because you can't access those memory locations.

int p[][3] = {{1,2,3},{4,5,6}};              //it's okay

This is ok because this is an array of arrays, so this time 1, 2, 3, 4, 5 and 6 aren't addresses but the elements themselves.

char *p[] = (char *[]) {"one", "two"...};    // it's okay!

This is ok because the string literals ("one", "two", ...) aren't really strings but pointers to those strings, so you're assigning to p[1] the address of the string literal "one".
BTW, this is the same as doing char abc[]; abc = "abc";. This won't compile, because you can't assign a pointer to an array, while char *def; def = "def"; solves the problem.

BlackBear
  • 22,411
  • 10
  • 48
  • 86