Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

2D array variable pointer confusion

I have a basic doubt in 2D arrays (C Language). Consider a declaration of a 2D array as follows

int array[3][5];

Now when I do the following, the output of both th below printf's is the same:

printf("%u\n", array);
printf("%u\n", *(array));

Now when try to to the following:

printf("%u\n", array+1);
printf("%u\n", *(array)+1);

The outputs are different. I get that the 2nd printf refers to array[0][1] and the first one to array[1][0]. How does this work? array is a pointer to what?

Thanks in advance

like image 924
Kaushik Balasubramanain Avatar asked Dec 25 '11 14:12

Kaushik Balasubramanain


3 Answers

I'll try to give you a technically-correct explanation so you'll know what's going on. Not really complicated, but indeed counter-intuitive.

Intro:

In C there are "lvalues" which basically represent "assignable" objects that have a place somewhere in memory, and "rvalues" which represent, well, "conceptual" values (not required to be placed anywhere in particular).

For example if you define int a = 5;, then a is an lvalue of type int and value 5. Also it can be interpreted as (or rather, converted to) an rvalue of type int. Such an rvalue would still be known to be equal to 5, but it would no longer contain the information about a's location in memory.

Some expressions need lvalues (like the left hand-side of operator= because you have to assign to an object), and some need rvalues (like operator+ because you only need the integral values when you add, or the right side of operator=). If an expression needs an rvalue but you pass an lvalue, then it is converted to an rvalue.

Also, only rvalues are passed to functions in C (which means that C is strictly call-by-value, not call-by-reference).

Some examples:

int a = 1;
a; // a is an lvalue of type int and value 1
a = a+3; // here the `a` is converted to an rvalue of type int and value 1, then after the addition there's an assignment, on the lhs there's an lvalue `a` and an rvalue `4`

Conversion from an lvalue to an rvalue is usually trivial and unnoticable (it's like taking the number 5 from a shelf labeled a). Arrays are basically the exception here.

The big thing: There are no rvalues of array type in C. There are pointer lvalues and rvalues, integer lvalues and rvalues, structure lvalues and rvalues etc... But only lvalue arrays. When you try to convert an lvalue of array type to an rvalue, you no longer have an array, you have a pointer to the array's first member. That is the root of confusion about arrays in C (and C++).

Explanation:

  • array
  • *(array)
  • array+1
  • *(array)+1

array is an lvalue of type int[3][5] (array of 3 ints of 5 ints). When you try to pass it to a function, it receives a pointer of type int (*)[5] (pointer to array of 5 ints) because that's what's left after lvalue-to-rvalue conversion.

*(array) is tricker. First the lvalue-to-rvalue is performed resulting in an rvalue of type int(*)[5], then operator* takes that rvalue and returns an lvalue of type int[5], which then you attempt to pass to the function. Hence again it's converted to an rvalue, resulting in an int*.

array+1 causes the array to be converted to an rvalue of type int(*)[5] and that rvalue gets incremented by one, so (according to the rules of pointer arithmetics) the pointer moves 1 * sizeof(int[5]) bytes forwards.

*(array)+1: see 2 points before, but the final rvalue of type int* gets incremented, again by rules of pointer arithmetics, by 1 * sizeof(int).

No mystery here!

like image 193
Kos Avatar answered Oct 11 '22 00:10

Kos


2D arrays in C are confusing.
array and *array are both the same pointer, but are not the same type.
array is of type int[3][5] (which is an array, of size 5, of int[3] arrays).
*array is the first line of array, which is of type int[3].
array+1 means array plus one element. An element of array is int[3], so it's 12 bytes forward.
*array+1 means *array plus one element. An element of *array is int, so it's 4 bytes forward.

like image 36
ugoren Avatar answered Oct 10 '22 23:10

ugoren


Arrays are not pointers. Ignore any answer, book, or tutorial that tries to tell you otherwise.

An expression of array type, in most contexts, is converted (at compile time) into a pointer to the array's first element. The exceptions are:

  • The operand of sizeof (sizeof arr yields the size of the array, not the size of a pointer)
  • The operand of unary & (&arr yields the address of the array, not of its first element -- same memory location, different type). This is particularly relevant to your example.
  • A string literal in an initializer used to initialize an array object (char s[6] = "hello"; doesn't copy the address of the string literal, it copies its value)

A 2-dimensional array is nothing more or less than an array of arrays. There are other data structures that can be used with the same x[y][z] syntax, but they're not true 2-dimensional arrays. Yours is.

The [] indexing operator is defined in terms of pointer arithmetic. x[y] means *(x+y).

The behavior of your code follows from these rules.

Read section 6 of the comp.lang.c FAQ. It's the best explanation of this stuff I've seen.

And don't use "%u" to print pointer values; convert to void* and use "%p".

printf("%p\n", (void*)array);
printf("%p\n", (void*)*(array));
like image 29
Keith Thompson Avatar answered Oct 10 '22 23:10

Keith Thompson