2

I know that for a one-dimensional array in C, you can use the name of the array as an address:

int k;
double x[6], sum=0;
...
/*  Sum the values in the array x.  */
for (k=0; k<=5; k++)
    sum += *(x+k);

My question is, how come this doesn't work for a two-dimensional array?

int g[2][4]={{5,2,2,3},{1,2,3,4}};
int g_count=8, k, sum=0;

for (k=0; k<=g_count-1; k++)
    sum += *(g+k);

I've noticed that while &g[0][0] and g hold the same value, &g[0][0]+1 and g+1 do not. Can someone please explain why? Thanks.

b_pcakes
  • 2,452
  • 3
  • 28
  • 45

6 Answers6

2

The size of (*g) while declared as g[M][N] is the N multiplied by the size of the element. For a simple reason, that the increment of the first index will "jump" over N elements in the memory. So *(g+k) is equivalent to &g[k][0]. Note, that dereferencing g or (g+k) is returning an address of the subarray.

Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61
2

Question 1

My question is, how come this doesn't work for a two-dimensional array?

Answer

for (k=0; k<=g_count-1; k++)
    sum += *(g+k);

is wrong on two counts.

  1. *(g+k) does not evaluate to an int.
  2. *(g+k) accesses the array out of bounds when k > 1.

When g is declared as:

int g[2][4];

g decays to a pointer of type int (*)[4]. It does not decay to a pointer of type int*.

What type does g+k evaluate to? It also evaluates to a pointer of type int (*)[4].

Where does it point to? It will be helpful to see that with a diagram.

|<---     memory used by g   -->|
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

address of g
|
v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

address of g[0] address of g[1]
|               |
v               v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

When used in an expression, g decays to the address of g[0].

The expression g evaluates to the address of g[0].
The expression g+1 evaluates to the address of g[1].

g               g+1             g+2
|               |               |
v               v               v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

As you can see, g+k points to a location beyond the valid limits when k > 1. Derefercencing g+k will lead to undefined behavior when k > 1.

You could use:

int* ptr = &g[0][0];  // Pointer to the first int in the 2D array.
for (k=0; k<=g_count-1; k++)
    sum += *(ptr+k);
         // ^^^ ptr, not g

That will be valid use of the array and the pointers.

Question 2

I've noticed that while &g[0][0] and g hold the same value, &g[0][0]+1 and g+1 do not. Can someone please explain why?

Answer

Given the array, we have:

g               g+1
|               |
v               v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

&g[0][0]
|   &g[0][0]+1
|   |
v   v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

From the diagram, you can see why expressions g and &g[0][0] evaluate to the same address. However, the type of the expressions are not the same. The expression g evaluates to int (*)[4] while &g[0][0] evaluates to int*.

Since the type of the expression g is int (*)[4], the offset between g and g+1 is the same as the size of 4 ints.

Since the type of the expression &g[0][0] is int*, the offset between &g[0][0] and &g[0][0]+1 is the same as the size of one int.

That explains why g+1 does not evaluate to the same address as &g[0][0]+1 even though g and &g[0][0] evaluate to the same address.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

In order to understand how this works you have to think about what you're creating. When you are creating an array of arrays, you are actually creating an array of pointers. i.e.

       0 1 2 3 4
0[] -> [][][][][]
1[] -> [][][][][]
2[] -> [][][][][]
3[] -> [][][][][]
4[] -> [][][][][]

Here, grabbing the 0th value in the first array is the same as grabbing [0][0]. However, grabbing the 1st value in the first array is not the same as grabbing [0][1], it is the same as grabbing [1][0].

Update

I've modified the diagram to show how the first array points to a set of separate arrays to form a 2D array, to better illustrate my point.

Second Update

After some discussion it appears that this is actually not correct. The first array is not an array of pointers to arrays, as was previously stated. The first array contains elements of type "array" (which is not the same as type "pointer") and is therefore not equivalent to an array of pointers to arrays. A more appropriate diagram for this would be something like:

    0         1         2         3         4
0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 
[][][][][][][][][][][][][][][][][][][][][][][][][]

Where the top row shows the index into the first array and the bottom row shows the index into the second array. Now, looking at the original question, it's just a matter of offset into that array. The first pair (&g[0][0] and g) both are offset into the array 0. The second pair (&g[0][0]+1 and g+1) have different offsets into the array and therefore provide different values. See @R Sahu's answer for further explanation.

dudeman
  • 1,106
  • 8
  • 19
  • 2
    "When you are creating an array of arrays, you are actually creating an array of pointers" Hum, no – Ilya Jan 06 '16 at 21:00
  • @MikeAtNobel - You should re-consider your answer, then reconsider your response to IIya. Both are wrong. One shows lack of discretion. – ryyker Jan 06 '16 at 21:09
  • @ryyker: I don't see how. Since when is an array of arrays not an array of pointers? And which shows lack of discretion? – dudeman Jan 06 '16 at 21:11
  • In C, what can be thought of as a 2D array, can be created as a single block of memory, accessible via a single pointer (and jumping by increments equal to `sizeof(type)` to access the _elements_). What you say _can_ be implemented, and would facilitate accessing intermediate locations within a block of memory, but is not required. – ryyker Jan 06 '16 at 21:13
  • 1
    Since always. You will not find element which is an address within the memory area occupied by array `g`. Try it. – Eugene Sh. Jan 06 '16 at 21:13
  • @ryyker: I understand a 2D array can be created using a single block of memory, but that requires doing the calculations to find the correct locations yourself. When the array is created using the [][] syntax, the system doesn't create a single block of memory, necessarily (i.e. if the array were built on the heap). – dudeman Jan 06 '16 at 21:17
  • If an array is created using the `[]`operator, it was not created on the heap. Memory on the heap is created for a memory location depicted by a pointer such as: `int *a = malloc(10*sizeof(int));` – ryyker Jan 06 '16 at 21:21
  • memory for `int a[2][2]`={0}` will look like `|0|0|0|0|` in memory. The very first `|` is the address for that block. (by the way, each segment in that diagram is `sizeof(int)` bytes.). – ryyker Jan 06 '16 at 21:25
  • @ryyker and Eugene: I've updated my answer to better illustrate my point. The first array you see is the array containing pointers. Those pointers point to a set of arrays containing the values. – dudeman Jan 06 '16 at 21:25
  • Your answer is still containing incorrect information where you are saying about array of pointers. The fact that dereferencing an array name is giving the address, doesn't mean the array actually containing the address. It is just calculated based on the array's start address. – Eugene Sh. Jan 06 '16 at 21:28
  • @MikeAtNobel Your understanding of C arrays is fundamentally incorrect. The commenters are correct. An array of arrays is not an array of pointers to arrays. I suggest you research the topic in more depth. – 2501 Jan 06 '16 at 21:30
  • @2501: I'd definitely check out any source you had. – dudeman Jan 06 '16 at 21:32
  • https://stackoverflow.com/questions/27030694/what-is-the-difference-between-char-s-and-char-s?lq=1 But don't stop there. – 2501 Jan 06 '16 at 21:37
  • Check _[C99 para. 6.5.2.1 Array subscripting AND 6.3.2.3 Pointers](http://www.open-std.org/jtc1/sc22/wg14/www/standards.html)_ – ryyker Jan 06 '16 at 21:45
  • 1
    @2501, Eugene, ryyker: Thanks for the links. You guys were definitely right. I think I understand my confusion now. I was always taught that arrays and pointers were the same thing, but now I know better. I think I'll leave my answer here anyways, though, for others who tread the same path. – dudeman Jan 06 '16 at 21:53
  • @MikeAtNobel - No Problem, thanks for hanging in there. Better than just keeping it here, edit it to be correct. If you do that, and send me a comment, I will come back an upclick if you add anything helpful to the conversation. By the way, here is one of my _[favorite illustrations on 2D arrays and pointers in C](http://stackoverflow.com/a/32910600/645128)_. – ryyker Jan 06 '16 at 21:55
  • @ryyker: I have edited the answer to explain why I was wrong and tried answering the question again. I really do appreciate you guys taking the time to show me where I went wrong. It is chances to learn like these that are why I appreciate SO/SE so much. – dudeman Jan 06 '16 at 22:28
  • @MikeAtNobel - Sorry - Although _[C does use arrays](http://www.tutorialspoint.com/cprogramming/c_arrays.htm)_, C does not really have an _array type_. The `[]` symbols in C are referred to as _[postfix operators](http://www.tutorialspoint.com/cprogramming/c_operators.htm)_, and are used to create, manipulate and access elements arrays. But they do not depict an _array type_. The term _[type](https://msdn.microsoft.com/en-us/library/s3f49ktz.aspx)_ in C also has special meaning. It is an object that has specific size and feature characteristics for holding different _types_ of data. – ryyker Jan 06 '16 at 22:52
  • @ryyker: According to 6.2.5.20 of the standard, the array type is a derived type. – dudeman Jan 06 '16 at 23:02
0

For 2D array, the name of the array is also the address, but that is the address of the first 1D array within your 2D array.

So,

  • g[0] is the address of the first 1D array.
  • g[1] is the address of the second 1D array.
  • and so on.

To access individual elements, say of the first 1D array, you need something like:

  • *( g[0] + 0 ) for g[0][0]
  • *( g[0] + 1 ) for g[0][1]
  • and so on

Generally, you'll need *( g[i] + k ) to access element g[i][k]

artm
  • 17,291
  • 6
  • 38
  • 54
  • So if i replaced the last line with `sum += *(g[0]+k)`, it would be correct (since arrays are stored sequentially)? – b_pcakes Jan 06 '16 at 21:35
  • I don't think so, that line works for the first 1D array only. You still need two indexes to access 2D array, no matter you are using pointer or not. – artm Jan 06 '16 at 21:46
0

You need to understand the relation between pointer and arrays and pointer arithmetic first.
When used in an expression, in most context, arrays are converted to pointer to its first element.
For a 1D array

double x[6];  

x will converted to pointer to its first element x[0]. For a 2D array

int g[2][4];

g will convert to pointer to its first element g[0]. Note that g[0] is an array itself, therefore g is actually converted to pointer to an array.

Note that if p is a pointer to type T, then adding 1 will increment it to sizeof(T) in memory, i.e to the address of next continuous element.

So, the expression *(g+k) is equivalent to *(&g[0] + k). As, &g[0] is an address of an array, adding 1 will give the address of g[1] and adding k to it will give the address of g[k].

haccks
  • 104,019
  • 25
  • 176
  • 264
-1

sum += *(x+k); works because here (since x is an one dimensional array of int) x is a pointer to an int so when incrementing by k it points to the array element at position x[k]

sum += *(g+k); doesn't work because here (since g is a two dimensional array of int) g is a pointer to an 1D array of int so when we increment g by k it points to the array g[k]. So when you dereference it by *(g+k) you will get the array g[k] which is an address of 1st element.

So for following code snippet:

for (k=0; k<ROWS; k++)
sum += **(g+k);  

You will get the total sum of all the elements in the first column of g[][]

rootkea
  • 1,474
  • 2
  • 12
  • 32