Wakan Tanka
Wakan Tanka

Reputation: 8042

Create 2D array from existing 1D arrays in C?

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?

Upvotes: 5

Views: 3203

Answers (6)

Frankie_C
Frankie_C

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

Grzegorz Szpetkowski
Grzegorz Szpetkowski

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

Quentin
Quentin

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

foobarna
foobarna

Reputation: 873

  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.

Upvotes: 1

gandgandi
gandgandi

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

Some programmer dude
Some programmer dude

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

Related Questions