16

I have created a 2D array, and tried to print certain values as shown below:

int a[2][2] = { {1, 2}, 
                {3, 4}};

printf("%d %d\n", *(a+1)[0], ((int *)a+1)[0]);

The output is:

3 2

I understand why 3 is the first output (a+1 points to the second row, and we print its 0th element.

My question is regarding the second output, i.e., 2. My guess is that due to typecasting a as int *, the 2D array is treated like a 1D array, and thus a+1 acts as pointer to the 2nd element, and so we get the output as 2.

Are my assumptions correct or is there some other logic behind this?
Also, originally what is the type of a when treated as pointer int (*)[2] or int **?

haccks
  • 104,019
  • 25
  • 176
  • 264
Kevin Richards
  • 570
  • 1
  • 6
  • 23
  • The cast has a higher propriety so your invoking undefined behavior when you try to dereference `a+1`. – user657267 Jul 16 '14 at 22:35
  • 4
    @user657267 there is no UB – M.M Jul 16 '14 at 22:58
  • 2
    Oops, arrays are flat of course. – user657267 Jul 16 '14 at 23:30
  • Note: `*(a+1)[3]` is not the same as `(*(a+1))[3]` (unless the second number is zero, which it is in your code) – user253751 Jul 17 '14 at 03:47
  • Also note that when the "array" is actually a `int**` (where the rows of the array have been `malloc`ed manually), although it can be accessed like an `array[x][y]`, casting it to a 1D array and accessing it will in the best case return the value of a *pointer* (that is, garbage from the callers' point of view) – Marco13 Jul 17 '14 at 09:35

4 Answers4

10

When you wrote expression

(int *)a

then logically the original array can be considered as a one-dimensional array the following way

int a[4] = { 1, 2, 3, 4 };

So expression a points to the first element equal to 1 of this imaginary array. Expression ( a + 1 ) points to the second element of the imaginary array equal to 2 and expression ( a + 1 )[0] returns reference to this element that is you get 2.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
9

Are my assumptions correct or is there some other logic behind this?

Yes.

*(a+1)[0] is equivalent to a[1][0].
((int *)a+1)[0] is equivalent to a[0][1].

Explanation:

a decays to pointer to first element of 2D array, i.e to the first row. *a dereferences that row which is an array of 2 int. Therefore *a can be treated as an array name of first row which further decay to pointer to its first element, i.e 1. *a + 1 will give the pointer to second element. Dereferencing *a + 1 will give 1. So:

((int *)a+1)[0] == *( ((int *)a+1 )+ 0) 
                == *( ((int *)a + 0) + 1) 
                == a[0][1]   

Note that a, *a, &a, &a[0] and &a[0][0] all have the same address value although they are of different types. After decay, a is of type int (*)[2]. Casting it to int * just makes the address value to type int * and the arithmetic (int *)a+1 gives the address of second element.

Also, originally what is the type of a when treated as pointer (int (*)[2] or int **?

It becomes of type pointer to array of 2 int, i.e int (*)[2]

chouaib
  • 2,763
  • 5
  • 20
  • 35
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 2
    This answer does not provide any explanation for why this is the case. The equivalence is clear from the numbers that are output, please include a reason for said equivalence. It also does not answer the question at the end of the post. – Mike Precup Jul 16 '14 at 22:40
  • 1
    @MikePrecup; sometimes you need to be patience. – haccks Jul 16 '14 at 22:44
  • 2
    In accordance to the site rules, I vote based on the current state of a post, not how it could potentially be. As such, I've changed my vote now that the post answers the question. – Mike Precup Jul 16 '14 at 22:45
  • 3
    @haccks It's like serving a pizza uncooked and without toppings to a customer, yelling "Order up!", and then getting sour when they point out that the pizza served is simply not adequate. That aside, why are you even providing answers with versions? 3 on the record and those are excluding the minor ones... – Utkan Gezer Jul 16 '14 at 23:00
  • @ThoAppelsin; *That aside, why are you even providing answers with versions?*: Isn't that a sarcasm ? At first I thought this pointer arithmetic is not that much difficult that OP could not understand that and saying that `((int *)a+1)[0]` is equivalent to `a[0][1]`, OP will get a hint. After that I decided to explain that. And I do not think this answer is given is versions. – haccks Jul 16 '14 at 23:10
  • 2
    @ThoAppelsin - Have you never improved on one of your answers? Jeez. Look at some of the best answers on SO, most of them have several edits. ***[This one for example, 15 edits](http://stackoverflow.com/a/1433387/645128)*** There is nothing wrong with an edit that improves on the answer. – ryyker Jul 17 '14 at 00:17
  • 3
    @ryyker I believe what I've said is not to be taken out of it's context. *haccks* said something about patience, right on his first comment; which I have I believe rightfully interpreted as *"Don't be so fast on judging my answer, it was not complete back then, I was still preparing it."*. Of course one can improve their answer, I also do; the problem here is that he apparently posted the first version with the plan ob his mind to provide an edit asap, which I never do. Refute me if you want, it is inappropriate to prematurely post an answer while being aware that it's not complete yet. – Utkan Gezer Jul 17 '14 at 00:36
  • 1
    @ThoAppelsin There is difference between pizza and answer. A five year old answer can help someone new in future (pizza after expiry date not eatable)! So one should improve answer if he can. In fact we should help others to improve their answer and write better version. – Grijesh Chauhan Jul 17 '14 at 10:04
  • @GrijeshChauhan You either intentionally or unfortunately not getting the point. The improvement he made did not hit him after he made the post, he already had an improvement on his head as he sent the post for the first time. You can find this out from the way he said *"patience"*. Fine if you came up with an improvement afterwards, but disaster if you are making it just to be the first one to post an answer, even if completely inadequate and incomplete, as in this case. His first answer really wasn't any better than posting `(reserved)` as an answer. – Utkan Gezer Jul 17 '14 at 13:04
  • @ThoAppelsin; I still do not understand why you are shouting that the answer was *incomplete* and *inadequate*. What's your problem ? – haccks Jul 17 '14 at 13:12
  • @haccks Grijesh replies to me, I reply to him? There's my reason to pop out. No need for you to pop out like that for nothing, I guess. – Utkan Gezer Jul 17 '14 at 13:16
  • @ThoAppelsin; *for nothing*: Seriously! This all about this post. – haccks Jul 17 '14 at 13:18
  • @haccks I've said that you've sent your first post prematurely with the plan on your mind of improving it immediately after. Do you have something to say about that, or all you're going to do is wonder *"Why is this guy pointing out that I've served my pizza uncooked?"*? – Utkan Gezer Jul 17 '14 at 13:29
  • @ThoAppelsin; *with the plan on your mind of improving it *: Most of the users, most of the time, do the same. – haccks Jul 17 '14 at 13:33
  • @haccks Not just *"with the plan on your mind of improving it..."*, but rather *"with the plan on your mind of improving it __immediately after__"*. If what you've said applies to this complete form of the quote as well, then all such users may feel free to take what I've said so far to themselves, too. I think it's reasonable to say that it's a terrible idea to write a paragraph of the answer on your head, post that, then edit in the remaining `n` paragraphs one-by-one. Take that one level further and make your first post as *"I'll be giving an answer in a minute"*, disgusting... – Utkan Gezer Jul 17 '14 at 14:07
3

A 2D-array is essentially a single-dimensional array with some additional compiler's knowledge.

When you cast a to int*, you remove this knowledge, and it's treated like a normal single-dimensional array (which in your case looks in memory like 1 2 3 4).

nullptr
  • 11,008
  • 1
  • 23
  • 18
0

The key thing to recognize here is that the a there holds the value of the address where the first row is located at. Since the whole array starts from the same location as that, the whole array also has the same address value; same for the very first element.

In C terms:

&a == &a[0];
&a == &a[0][0];
&a[0] == &a[0][0];
// all of these hold true, evaluate into 1
// cast them if you want, looks ugly, but whatever...
&a == (int (*)[2][2]) &a[0];
&a == (int (*)[2][2]) &a[0][0];
&a[0] == (int (*)[2]) &a[0][0];

For this reason, when you cast the a to int *, it simply becomes 1-to-1 equivalent to &a[0][0] both by the means of type and the value. If you were to apply those operations to &a[0][0]:

(&a[0][0] + 1)[0];
(a[0] + 1)[0];
*(a[0] + 1);
a[0][1];

As for the type of a when treated as a pointer, although I am not certain, should be int (*)[2].

Utkan Gezer
  • 3,009
  • 2
  • 16
  • 29