5

In perl I can create 1D arrays and then create 2D array from them, following way:

@a1=(a,b,c)
@a2=(d,e,f)
@a3=(g,h,i)
@m23_v1=(\@a1,\@a2,\@a3)

Here is another way (assuming that @a1, @a2 and @a3 are the same as in previous example):

@m23_v2=([@a1],[@a2],[@a3])

The difference between those two ways is that when backslashes are used then changing $a[0][0] will also change $a1[0]. On the other hand when brackets are used then the value is copied so changing $a[0][0] will not change $a1[0]. Bellow are memory addresses of variables which should clarify what I mean:

print \$a1[0]
SCALAR(0x2006c0a0)

print \$m23_v1[0][0]
SCALAR(0x2006c0a0)

print \$m23_v2[0][0]
SCALAR(0x2030a7e8)

How to achieve the same but in C? I've tried following code:

# include <stdio.h>
int main(){
  int a1[3] = {1,2,3};
  int a2[3] = {4,5,6};
  int m23[2][3] = {a1, a2};
  printf("%d\n", a1[0]);
  printf("%d\n", m23[0][0]);
}

But it gives me following compilation warnings:

2d.c: In function ‘main’:
2d.c:4:3: warning: initialization makes integer from pointer without a cast [enabled by default]
2d.c:4:3: warning: (near initialization for ‘m23[0][0]’) [enabled by default]
2d.c:4:3: warning: initialization makes integer from pointer without a cast [enabled by default]
2d.c:4:3: warning: (near initialization for ‘m23[0][1]’) [enabled by default]
2d.c:5:3: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]

After execution the C code returns following:

1
-1077371888

Questions:

  1. Why I get the compilation warnings and how to modify the code to get rid of them?
  2. If given C is equivalent to backslashed perl version then what is the equivalent for brackets version (and vice versa)?
  3. Why I get -1077371888 instead of 1?
1
  • Thank you all guys. I've upvoted all answer because everybody brings some interesting points to note. I can only accept one so I've chosen @GrzegorzSzpetkowski because it well explains what is going on under the hood. Commented Jul 27, 2015 at 14:22

6 Answers 6

3

You can use array of pointers to get an equivalent of backslahed version (i.e. @m23_v1):

#include <stdio.h>

int main(void)
{
    int a1[3] = {1,2,3};
    int a2[3] = {4,5,6};

    int *m23[2] = {a1, a2};

    printf("%d\n", a1[0]);
    printf("%d\n", m23[0][0]);

    return 0;
}

In your code:

int m23[2][3] = {a1, a2};

initializer expects to be filled with integers. Basically you create two-dimensional array with two integers: a1, a2. The remaining elements are initialized with zeros. To illustrate it, this looks something like:

int m23[2][3] = {0xaaee33, 0xaaee55, 0, 0, 0, 0};

which is effectively the same as:

int m23[2][3] = {{0xaaee33, 0xaaee55, 0}, {0, 0, 0}}; // two rows, three columns

However, a1 is not an integer. It's name of array, that is implicitely converted to int (after it is converted into pointer, that points to array's first element). In other words you are implicitely converting addresses of a1 and a2 into two integers. In fact, such operation is illegal in C and such code should not even compile with -pedantic-errors flag (or equivalent).

what is the equivalent for brackets version (and vice versa)?

The equivalent of braced version is define an multidimensional array of specific size and then copy each element of a1 and a2 arrays into it:

#include <stdio.h>
#include <string.h>

int main(void)
{
    int a1[3] = {1,2,3};
    int a2[3] = {4,5,6};

    int m23[2][3];
    memcpy(m23 + 0, a1, sizeof(a1));
    memcpy(m23 + 1, a2, sizeof(a2));

    printf("%d\n", a1[0]);
    printf("%d\n", m23[0][0]);

    return 0;
}
Sign up to request clarification or add additional context in comments.

5 Comments

Does m23[2][3] looks like you've explained {0xaaee33, 0xaaee55, 0, 0, 0, 0}; or it is rather {{0xaaee33, 0xaaee55, 0}, {0, 0, 0}};? Also when the a1 is implicitly converted to int then m23[0][0] should return a1 zeroth element. Or the question could also stand: how is a1 implicitly converted to int?
@WakanTanka: I clarified my answer a bit. For your second question, in this case m23[0][0] does return an address of a1, not its first element.
Thank you, your code gives me following warning: 2d.c:8:3: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default] can you assists?
@WakanTanka: I believe that you forgot to include <stdio.h> header, that cointains proper prototype for printf.
Yes, this was the case. Thank you
3

You get the warning because when you initialize the m23 array, the arrays a1 and a2 decays to pointers, making you have an array not of arrays but of pointers.And that's also how you solve your problem, by declaring m23 as an array of pointers:

int *m23[2] = { a1, a2 };

1 Comment

So this is equivalent of perl's backslash version (because pointers are used), am I right? How to create also braces version?
1

Well you can't initialize array with use of other array. You can do it like that:

int m[2][3] = {{1,2,3},{4,5,6}};

This is because a1 and a2 is treated as pointer to int. You can this with array of pointers:

int* m[2] = {a1,a2);

4 Comments

Are you sure to use int* m[3] not int* m[2]?
My bad it should be int *m[2]
So far from your (and another) answers I understand that this cannot be done by arrays but can be done by pointers. But I thought that arrays and pointers are similar in many ways (arrays are constant pointers AFAIK) So is there any reason for this statement "you can't initialize array with use of other array" What I'm violating here? Am I incrementing any constant or what?
This is because language doesn't support this kind of initialization, as it doesn't support array copying. You initialize it with aggregate style or do it manually with for loop, or use array of pointers.
1
  1. Why I get the compilation warnings and how to modify the code to get rid of them?

That is because you assign a pointer (a1) where it expects a integer (m23 if of type int) without a proper casting.

This is one way to write it:

#include <stdio.h>
#define N 2
#define M 3
int main(){
    int a1[M] = {1,2,3};
    int a2[M] = {4,5,6};
    int* m23[N] = {a1, a2};
    printf("%d\n", a1[0]);
    printf("%d\n", m23[0][0]);
}
  1. If given C is equivalent to backslashed perl version then what is the equivalent for brackets version (and vice versa)?

I'm not an expert in Perl, but i think that there is not such easy way, as in just some types. As you can see in your output from Perl you are printing where the row and vector resides in memory. $a1 is the same with $m23_v1 as it gets the $a1 for the row, but in $m23_v2 you actually create another vector out from $a2, so memory location changes.

  1. Why I get -1077371888 instead of 1?

That is because you assigned to m23 the value of pointer a1, which is an address in the memory, and when printed like an integer ( printf("%d\n" ) it gets evaluates to int, and this is the result.

2 Comments

Yes, I understand how it works in perl. I'm wondering (among other questions) what is the equivalent in C. Or in other words: how to change m23[0][0] (after it was build using int* m23[N]) without changing a1[0] ? Is this even possible in C?
As you find out, doing int* m23[N] = {a1, a2}; and modifying m23[0][0] will modify a1. in order to keep them separated you need to manually build the m23, iterating over each element and assign it values. A way is like this: int* cols[N] = {a1, a2}; int m23[N][M]; int i, j; for (i = 0; i < N; ++i) { for (j = 0; j < M; ++j) { m23[i][j] = cols[i][j]; } } You can see it better if you copy/paste it in an editor and Enter after each ;
1

As other answers have already stated, you can make an array of pointers to gather both subarrays together. However they're indeed not copied, but merely referenced (i.e. like the Perl snippet with backslashes).

int *m23[] = {a1, a2};

In C, you can't assign arrays, and you can't directly initialize an array from another. You must copy it manually :

int a1[3] = {1,2,3};
int a2[3] = {4,5,6};

// Uninitialized array of sufficient size
int m23[2][3];

// Manual copy of both subarrays
memcpy(&m23[0], &a1, sizeof a1);
memcpy(&m23[1], &a2, sizeof a2);

Side note related to one of your comments : arrays are not pointers, constant or otherwise. But an array's name can decay to a pointer to its first element in most contexts.
Exceptions to array-to-pointer decay include the sizeof operator used above : its result here is 3 * sizeof(int), the actual size of the array, not sizeof(int*).

Comments

1

You have to take in account the way that C language use to store multidimensional arrays in memory.

C is a simple minded language and it consider that in an array having n dimensions just the last one defines how many real objects are present, the others are repetition of it. I.e. an array of int int arr[10][5]; means that we have a string of 5 integers that repeats itself 10 times in memory.

If we add more dimensions we have more repetitions in memory. I.e. int arr[4][10][5]; means that we have a string of 5 integers that repeats itself 10 times, totalizing 50 integers in a row, that repeats again 4 times. At the end we have a sequence of 200 integers contiguous in memory representing a 3 dimensional array of integers. Now one of the dark sides of C is that, missing boundaries control, this array can be read with any index we like which product is between 0 and 199. For this reason when you declare a multidimensional array, to allow compiler to correctly address elements, you should give all dimensions but the last.

EDIT: maybe the sentence above could be not very clear: with 'last' dimension I mean that close to the identifier. I.e.

void foo(int a[][2])      //Correct
....
void foo(int a[2][])      //Wrong!

Now with your problem you can use an union to fake the array mixing:

union
{
    struct
    {
      int a1[3];
      int a2[3];
    };
    int m23[2][3];
} stTst = {1,2,3,4,5,6};
  
printf("%d\n", stTst.a1[0]);
printf("%d\n", stTst.m23[0][0]);
printf("%d\n", stTst.a2[2]);
printf("%d\n", stTst.m23[1][2]);

In this case we use the C arrays memory layout to create two modimensional arrays and a mutidimensional one together.

Another solution is to copy each single array in the dimension of the multidimensional one.

2 Comments

Shouldn't it be int a1[2]; to match with int m23[2][3];?
@sdbbs Sorry I don't understand what you mean. Why it should be int a1[2]? this is an array of only 2 elements.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.