简体   繁体   中英

Why won't *num show the zeroth element value?

In this code:

#include<stdio.h>
  int main()
  {
    int num[2] = {20, 30};
    printf("%d", num);
    printf("%d", &num[0]);
    return 0;
  }

As far as I know, both the printf statement will print the address of the first element in num because in the first statement, num is a pointer to an int.

But if num is a pointer, then it should also have any address but on printing its address (with printf("%d", &num) ), it's showing the address of the first element.

In a 2-D array the whole thing becomes confusing too:

#include<stdio.h>
int main(void)
{
    int num[ ] [2]={20,30,40,50};
    printf("%d",*num);
    return 0;
}

This program is printing the address of zeroth element that is the address of num[0][0] . But why does it do this? Why isn't it printing the value stored in it, since they all have same address( num,num[0] and num[0][0] )?

First things first; array variables are not pointers; they do not store an address to anything.

For a declaration such as

T a[N];

memory will be laid out as

         +---+
   a[0]: |   |
         +---+
   a[1]: |   |
         +---+
          ...
         +---+
 a[N-1]: |   |
         +---+

For a 2D MxN array, it will look like

              +---+
     a[0][0]: |   |
              +---+
     a[0][1]: |   |
              +---+
               ...
              +---+
   a[0][N-1]: |   |
              +---+
     a[1][0]: |   |
              +---+
     a[1][1]: |   |
              +---+
               ...
              +---+
 a[M-1][N-1]: |   |
              +---+

The pattern should be obvious for 3D and higher arrays.

As you can see, no storage is set aside for a separate variable a that contains the address of the first element; instead, there is a rule in the C language that an expression of type "N-element array of T " will be converted ("decay") to an expression of type "pointer to T " and the value of the expression will be the address of the first element of the array, except when the array expression is one of the following:

  • an operand of the sizeof operator
  • an operand of the unary & operator
  • an operand of the _Alignof operator (C99 and later)
  • a string literal used to initialize an array in a declaration

So given the declaration

T a[N];

all of the following are true:

Expression         Type        Decays to         Value
----------         ----        ---------         -----
         a         T [N]       T *               address of first element, &a[0]
        *a         T           n/a               value stored in first element
        &a         T (*)[N]    n/a               address of the array, which is 
                                                   the same as the address of the
                                                   first element of the array
      a[i]         T           n/a               value stored in the i'th element
     &a[i]         T *         n/a               address of the i'th element
  sizeof a         size_t      n/a               total number of bytes used by the
                                                   array
 sizeof *a         size_t      n/a               total number of bytes used by the
                                                   first element of the array
 sizeof &a         size_t      n/a               total number of bytes used by a 
                                                   pointer to the array 

The expression a has type "N-element array of T "; it is not the operand of the unary & or sizeof operators, so it is converted to a pointer to the first element of the array, amd its value is the address of that element.

The expression &a has type "pointer to N-element array of T "; since a is an operand of the unary & operator, the conversion rule above isn't applied (which is why the expression has type T (*)[N] instead of T ** ). However, since the address of the array is the same as the address of the first element of the array, it yields the same value as the expression a .

The expression &a[0] has type "pointer to T ", and explicitly points to the first element of the array. Again, this value will be the same as the previous two expressions.

For a 2D array

T a[M][N];

all of the following are true:

Expression         Type        Decays to         Value
----------         ----        ---------         -----
         a         T [M][N]    T (*)[N]          address of first subarray, a[0]
        *a         T [N]       T *               address pf first subarray, a[0]
        &a         T (*)[M][N] n/a               address of the array, which is 
                                                   the same as the address of the
                                                   first subarray, which is the same
                                                   as the address of the first element
                                                   of the first subarray.
      a[i]         T [N]       T *               address of first element of i'th
                                                   subarray
     *a[i]         T           n/a               value of first element of i'th subarray
     &a[i]         T (*)[N]    n/a               address of the i'th subarray
  sizeof a         size_t      n/a               total number of bytes used by the
                                                   array
 sizeof *a         size_t      n/a               total number of bytes used by the
                                                   first subarray
 sizeof &a         size_t      n/a               total number of bytes used by a 
                                                   pointer to the array 

Final note: to print out pointer values, use the %p conversion specifier and cast the argument to (void *) (this is the pretty much the only time it's considered proper to explicitly cast a pointer to void * ):

printf( "   &a yields %p\n", (void *) &a );
printf( "    a yields %p\n", (void *) a );
printf( "&a[0] yields %p\n", (void *) &a[0] );

Edit

To answer a question in the comments:

num,num[] and num[][] are all different thing. There types are different.Here num decays and became pointer to a pointer and num[] decays and became pointer to int and num[][] is a int. Right?

Not quite.

Assuming a declaration like

int arr[10][10];

then the expression arr will decay to type int (*)[10] (pointer to 10-element array of int ), not int ** ; refer to the table above again. Otherwise you're right; arr[i] will decay to type int * , and arr[i][j] will have type int .

An expression of type "N-element array of T " decays to type "pointer to T "; if T is an array type, then the result is "pointer to array", not "pointer to pointer".

In the second example, num is a 2 dimensional array, or say an array of array. It's true that *num is its first element, but this first element is an array itself.

To get num[0][0] , you need **num .

printf("%d\n", **num);

Look how an array looks like:

int num[ ] [2]={20,30,40,50};

is better written as

int num[][2]={{20,30},{40,50}};

It is an array with 2 elements. Those 2 elements are, again, arrays with 2 ints.

In memory, they look like

20    30    40    50

but the difference is that num refers to the whole array, num[0] to the first "part- array" and num[0][0] to the first element of the first array.

They have the same address (because they start at the same place), but they have a different type.

That is, the address is not the only important thing with a pointer, the type is important as well.

Arrays are not pointers actually, though they tend to act in a bit similar way, but not always.

Say you have this array and a pointer:

int a[] = {1, 2, 3};
int i = 19;
int *ptr = &i;

Now here a is equal to &a , but the same is not true, for pointers ( ptr is not equal to &ptr ).

Now coming to the question:

Consider a single dimensional array:

int arr[] = {11, 19, 5, 9};

Here, this array elements are stored in contiguous memory locations. Say, with starting address 0 :

 ---------------------
| 11 |  19 |  5 |  9 |
 ---------------------
0   4     8    12   16

Now when you write name of the array, arr (for this example), you will get the starting address of the 1 st element. Though if you write &arr , then you get the starting address of the whole block(this includes all the elements of the array). Now when you write *arr , you actually get the value inside the 1 st element of this array.

Now consider this 2-dimensional array arr[][4] = {{11, 19, 5, 9}, {5, 9, 11, 19}}:

0   4     8    12   16   -> These are memory addresses
 ---------------------
| 11 |  19 |  5 |  9 | ----> These values represent the values inside each index
 ---------------------
| 5  |   9 | 11 | 19 |
 ---------------------
16   20   24    28   32

Here, when you write the name of the array, as arr , what you get is the address of the 1 st element of this array, which in this case will be address of this 0 th index:

0                           16                32
 ----------------------------------------------
| 0<sup>th</sup> index | 1<sup>st</sup> index |
 ----------------------------------------------

Now when you do &arr , here what you get is the base address for whole of the block, ie base address of this:

0   4     8    12   16 
 ---------------------
| 11 |  19 |  5 |  9 | 
 ---------------------
| 5  |   9 | 11 | 19 |
 ---------------------
16   20   24    28   32

Now, if you do *arr , in 1-dimensional array it gives you the value inside the 1 st element, though in 2-dimensional array, the value inside each index is actually one 1-dimensional array, hence you will get the address of this array:

0   4     8    12   16 
 ---------------------
| 11 |  19 |  5 |  9 | 
 ---------------------

Now if you do **arr , that is when you will actually get the value inside the 1 st element, which is 11 .

I hope it clears some doubts :-)

EDIT 1:

As brought to my attendtion, by fellow user, it seems there is a bit of a confusion somewhere, though I have explained in detail what is meant by what thingy. But just to justify, for this statement:

Now here __a is equal to &a__, but the same is not true, for pointers (__ptr is not equal to &ptr__).

The types of both a and &a will be different, as already stated, in the answer. If one performs pointer arithmetics, one will able to know that. Try performing a + 1 and &a + 1 , how they both react to pointer arithmetics will surely give a good idea.

Considering a 1-dimensional array:

int arr[] = {11, 19, 5, 9};


 ---------------------
| 11 |  19 |  5 |  9 |
 ---------------------
0   4     8    12   16

We cannot do a++ , though for a pointer:

int i = 4;
int *ptr = &i;

we can perform ptr++ , this will make ptr point to the next memory location.

I think it result means that the array not really a pointer, but it is converted to a pointer in some contexts that is expected a pointer, like pass to a function that expect a pointer argument.

see this code:

void test(int* num) {
     printf("test\n");
     printf("%p\n",num);
     printf("%p\n",&num);
     printf("%p\n",&num[0]);
  }

int main(){
         int num[2]={20,30};
         test(num);
         printf("main\n");
         printf("%p\n",num);
         printf("%p\n",&num);
         printf("%p\n",&num[0]);
         //other();
         return 0;
}

The output is:

test
0x7fff7a422300
0x7fff7a4222e8   //LOOK THIS! Is diferent from main!
0x7fff7a422300
main
0x7fff7a422300
0x7fff7a422300
0x7fff7a422300

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM