Reputation: 8042
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:
Upvotes: 5
Views: 3203
Reputation: 4877
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.
Upvotes: 1
Reputation: 37924
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;
}
Upvotes: 3
Reputation: 63124
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*)
.
Upvotes: 1
Reputation: 873
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]);
}
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.
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.
Upvotes: 1
Reputation: 330
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);
Upvotes: 1
Reputation: 409176
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 };
Upvotes: 3