Pass a 2d vector by reference

157 Views Asked by At

I read quite a few replies on this topic but still I can't get it. Please understand I'm in a learning process.

I have been told that a vector is nothing else than its first element address so a 2D vector.

So, why do I lose the ability to use index if I pass a 2D vector by a pointer.

int vec[10][10] = { 1, 2, 3, 4 };
int *v = vec;

why I can't say

int a = v[1][1]  

as I would do with

int a = vec[1][1] 

if v, that is a pointer to an int, point exactly where vec, that is a pointer to an int, point, aren't they exactly the same thing, the same object?

I read about

int (*v)[N] = vec;

where N is the number of columns. But this appears as a super tricky thing for a beginner.

Why is this?

EDIT:

My brains came up this explanation for int (*vec)[N] meaning a pointer to a vector of 10 int. Here it is.

Giving I understood that in a 2D array, a pointer to the first element is a pointer to an array of N elements, this is the explanation I gave myself.

If lhs of int *var = &int have to be read right to left, becoming "var is a pointer to int" then I would expect that int [N] * var = &2darray would be, right to left, "var is a pointer to an array of N int elements".

But this is syntactically incorrect to the compiler so I have to do the trick int (* vec) [N] = 2darray that, right to left, keeping in mind parenthesis priority become:

Parenthesis first, right to left: vec is a pointer.

Then the rest, that now is, right to left: int [N].

The final result is vec is a pointer to an array of N of int.

Have this even remotely any sense?

6

There are 6 best solutions below

0
Vlad from Moscow On

The compiler should issue a message that in this declaration

int* v = vec;

there is used a pointer of incompatible pointer type to initialize the pointer v of the type int *.

The array vec used as an initializer is implicitly converted to a pointer to its first element. The type of elements of this two dimensional array is int[10]. So a pointer to elements of the array will have the type int ( * )[10].

You may not use an expression like that v[1][1] in your code snippet becuase the sub-expression v[1] has the type int. That is the sub-expression v[1] produces a scalar object that is not an array. So such an expression v[1][1] is just invalid.

If you will write

int vec[10][10] = {1,2,3,4};
int ( *v )[10] = vec;

then the sub-expression v[1] yields an object of the type int[10] that is an array. So you may apply one more the subscript operator to the obtained array like v[1][1].

Pay attention to that if you have a one dimensional array like for example

int a[n];

then elements of this array have the type int. And the array used in expressions with rare exceptions (as for example using in sizeof operator) is converted to a pointer to its first element of the type int *.

On the other hand, if you have a two-dimensional array like

int a[m][n];

then elements of the array are one-dimensional arrays of the type int[n] and a pointer to elements of the array has the type int ( * )[n].

0
gulpr On
  1. Arrays in C language are not vectors.
  2. Multi dimensional arrays in C are arrays of arrays
int x[2][3] 

Defines two elements array of arrays of 3 int elements.

  1. Arrays in C decay to pointer to its first element when they are used as lvalues (ie in expressions)
  • x from p.2 decays to pointer to array of 3 int elements.
  • x[0]' decays to pointer to int`
  1. You need to use pointers to arrays (it is nothing special). It is a pointer which has a type of array.
int (*p)[3];

defines p as pointer to array of 3 int elements.

1
Luis Colorado On

why I can't say

int a = v[1][1] 

Because v is a pointer to int, you can only access an element, using a single access [] operator, not two. Had you declared it correctly, then you will be able to do:

int (*v)[10] = vec;

then you can use correctly v[1][1] or even v[9][9], as now v is a pointer to an array of ten arrays of 10 integers.

The problem here is that, when you say just vec the C language interprets it as a pointer to the first element (with a type of pointer to the accessed data) which is also an array, so it decays (again) into a pointer to the first element, this is a pointer to int.

Even, you can use a pointer to the full thing:

int (*v)[10][10] = &vec;

but the, you have to use the address of the 2D array. That's valid too.

Note:

The parenthesis are necessary, as v must be a pointer to an array, and not an array of pointers (which it should be if you have not used them)

1
Lundin On

A lot of the C language design revolves around arrays decaying to a pointer to the first element whenever used in most expressions. The [] operator was designed around such an assumption - it actually cannot use an array as operand, it always uses a pointer. See Do pointers support "array style indexing"?

And that pointer parameter handed to the [] operator results in pointer arithmetic on the de-referenced type. Given int* ptr; then ptr[n] performs pointer arithmetic on items of type int.

So for a plain array int vec[10] we can write int* ptr = vec; and that's fine since vec decays into a pointer to its first element here.

int vec[10][10]; ... int* v = vec; is however invalid C. The first element of int [10][10] is a int [10]. So then vec decays, it decays into a pointer to an int [10], which is written as int (*)[10]. This type is not compatible with int* so the code posted violates assignment constraints and the compiler must issue a diagnostic message here.

In case when we are typing vec[n], vec decays to a int(*)[10] and we therefore get pointer arithmetic on items of int [10] type.

Now regarding the pointer to array syntax, a pointer to the array int vec[10][10] would be declared as int (*ptr)[10][10]. When we already have a pointer rather than array, there is no such thing as decay, so ptr[n] would actually give us the nth array item of type int [10][10]... which is not helpful, we do not have an array of int [10][10] items. To actually get to a int [10] item which is more likely what we want, we would first have to de-reference the pointer: (*ptr)[n]. Rather cumbersome syntax.

Therefore the convenient trick is to declare the pointer one dimension short: int (*ptr)[10]. This corresponds directly to an array int vec[n][10]. We can use ptr[i][j] to access the int item in the 2D array. Or we can do vec[i][j] in which case vec decays and we also get an int item in the 2D array.

2
chqrlie On

As noted in the other answers, int *v = vec; is a type mismatch and should generate a diagnostic. Compiling with advanced warnings and making these warnings errors is a good practice to try and avoid mistakes that lead to undefined behavior or other bugs.

Use gcc -Wall -Wextra -Werror or use the same flags for clang.

As stated in your question, the correct definition is:

int (*v)[10] = vec;

To decipher such definitions, you should study and use the spiral rule.

0
emerald On
char c[10], *pc = c;
int i[10], *pi = i, ii[10][10], (*pii)[10] = ii;

char takes 1 byte and int takes 4 bytes in memory.

pc ++;
pi ++;

pc now is pointing to c[1], pi now is pointing to p[1]. There's nothing complicated here, if you look a little closer, the ++ operator for these two statements do different job under the hood. pc is a pointer to char and the value of pc is increased by 1 in order to point to the next char, however, pi is a pointer to int, thus is increased by 4 in order to point to the next int. You can see these by printing them.

printf("before pc: %x, after pc: %x", pc - 1, pc);
printf("before pi: %x, after pi: %x", pi - 1, pi); // the printed values will show you what I am saying

And it's time to look at pii which is a pointer to int[10].

pii ++;  // internally, the value of pii is increased by 40, which is `10 * sizeof(int)`
pii[0] // ii[1][0]
pii[3] // ii[1][3]

pii += 2  // value of pii is increased by 80
pii[0] // ii[3][0]