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
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!
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.
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:
sizeof
(sizeof arr
yields the size of the array, not the size of a pointer)&
(&arr
yields the address of the array, not of its first element -- same memory location, different type). This is particularly relevant to your example.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));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With