Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistency in using pointer to an array and address of an array directly

Tags:

c

pointers

This code sample prints the array correctly.

int b[2] = {1, 2};
int *c = &b;
int  i, j,k = 0;
for (i = 0;i < 2; i++) {
    printf("%d ", *(c+i));
}

while this one prints two garbage values.

int b[2] = {1, 2};
int  i, j,k = 0;
for (i = 0;i < 2; i++) {
    printf("%d ", *(&b+i));
}

why are the two code samples behaving differently?

like image 624
gabber12 Avatar asked Jul 15 '13 18:07

gabber12


People also ask

What is difference between pointer to array and array of pointers?

Here is a list of the differences present between Pointer to an Array and Array of Pointers. A user creates a pointer for storing the address of any given array. A user creates an array of pointers that basically acts as an array of multiple pointer variables. It is alternatively known as an array pointer.

What is the relationship between an array and a pointer?

An array is represented by a variable that is associated with the address of its first storage location. A pointer is also the address of a storage location with a defined type, so D permits the use of the array [ ] index notation with both pointer variables and array variables.

How do you assign the address of an array to a pointer?

To assign a pointer to the array we can use the following declaration... int a[10]; int *pa = &a[0]; Basically this assigns the memory address of a[0] the first element in array a to pointer of type int .


2 Answers

The declaration:

int b[2] = {1, 2};

Creates an array of two int with values 1, 2.
Suppose in a system size of int is 4-bytes, then the array b[] should be stored in memory something like as follows:

first ele        +----------+                
     (b + 0) ---►|     1    | 0xbf5c787c  <----- &b ,    (c + 0)
next ele         +----------+ 
     (b + 1) ---►|     2    | 0xbf5c7880  <------------- (c + 1)
                 +----------+              
     (b + 2) ---►|     ?    | 0xbf5c7884  <----- (&b + 1) next array  
                 +----------+                    
             ---►|     ?    | 0xbf5c7888  
                 +----------+ 
             ---►|     ?    | 0xbf5c788c  <----- (&b + 2) next array  
                 +----------+      
             ---►|     ?    | 0xbf5c7890
                 +----------+               

? means garbage value
b[] array in memory from 0xbf5c787c to 0xbf5c7880  
each cell is four bytes 

In above diagram memory cells with value ? means garbage values and not allocated (memory from 0xbf5c7884 is not allocated in for our array). The values 1, 2 are stored in memory at address 0xbf5c787c and 0xbf5c7880, that is allocated in array b[].

Let's instead of printing values, we print addresses of memory that you access in your code using (c + i) and (&b + i), for this consider following program:

#include<stdio.h>
int main(){
  int b[2] = {1, 2}; 
  int  i = 0;
  int *c = &b; //Give warning: "assignment from incompatible pointer type" 
  printf("\n C address: ");  // outputs correct values 
  for (i = 0; i < 2; i++) {
    printf("%p ", (void*)(c + i));
  }
  printf("\n B address: ");  // outputs incorrect values/ and behaving differently 
  for (i = 0; i < 2; i++) {
    printf("%p ", (void*)(&b + i));  // Undefined behavior 
  }
  return 1;
}

Outputs:

 C address: 0xbf5c787c 0xbf5c7880 
 B address: 0xbf5c787c 0xbf5c7884 

Check this code working @Codepade
Notice, (c + i) prints correct address of cells with value 1, 2 hence outputs in your first code is correct. Whereas (&b + i) prints address value that is not allocated to array b[] (that is outside of array b[]) And accessing this memory gives undefined behaviour(unpredictable) at runtime.

Actually there is difference between b and &b.

  • b is an array and its type is int[2], b decays into int* as address for first element in most expressions (read: some exceptions where array name not decaying into a pointer to first element?). And b + 1 points to next int elements in array (notice the diagram).

  • &b is address of complete array and its type is int(*)[2], (&b + 1) points to next array of type int[2] that is not allocated in your program (notice in diagram that where (&b + 1) points).

To know some other interesting differences between b and &b read: What does sizeof(&array) return?

In first code snipe, when you do c = &b, you are assigning array's address to int* (in our example 0xbf5c787c). With GCC compiler this statement will give warning: "assignment from incompatible pointer type".
Because c is pointer to int, so *(c + i) prints integer stored at address (c + i). For i = 1 the value (c + 1) points to second element in array (in our example at 0xbf5c7880) hence *(c + 1) prints 2 correctly.

Regarding assignment int *c = &b; in first code I highly suggest read @AndreyT's answer below. The correct and simple way to access array elements using pointer will be as follows:

int b[2] = {1, 2};
int *c = b;   // removed &, `c` is pointer to int  
int i;
for (i = 0; i < 2; i++){
    printf("%d ", *(c + i)); 
 // printf("%d ", c[i]); // is also correct statement 
}

In your second code, adding i to &b make it pointing to outside allocated memory and in printf statement you access memory using * dereference operator cause invalid memory access and behavior of this code at run time is Undefined. That is the reason that second piece of code behaving differently at different execution.

Your code compiles because syntactically it correct, But at runtime accessing of unallocated memory can be detected by OS kernel. This may causes OS kernel send a signal core dump to the process which caused the exception (interesting to note: as OS detects memory right violation by a process -- An invalid access to valid memory gives: SIGSEGV, and access to an invalid address gives: SIGBUS). In worth case your program may execute without any failure and produce garbage results.

Regarding second code, correct way to print array using 'pointer to array' will be as below:

#include<stdio.h>
int main(){
  int b[2] = {1, 2}; 
  int  i;
  int (*c)[2] = &b;   // `c` is pointer to int array of size 2
  for(i = 0; i < 2; i++){
     printf(" b[%d] = (*c)[%d] = %d\n", i, i, (*c)[i]); // notice (*c)[i]
  }
  return 1;
}

Output:

b[0] = (*c)[0] = 1
b[1] = (*c)[1] = 2  

Check @codepade. Point to be notice parenthesis around *c is needed as precedence of [] operator is higher then * dereference operator (whereas if you use pointer to int you don't need parentheses as in above code).

like image 96
Grijesh Chauhan Avatar answered Sep 23 '22 15:09

Grijesh Chauhan


This is because of the pointer type to which the pointer arithmetic operation ptr+i is applied:

  • In the first case, you add i to a pointer to int, which is the same as indexing an array. Since the pointer to an array is the same as the pointer to its first element, the code works.
  • In the second case, you add i to a pointer to an array of two ints. Therefore, the addition puts you beyond the allocated memory, causing undefined behavior.

Here is a quick illustration of this point:

int b[2] = {1,2};
printf("%p\n%p\n%p\n", (void*)&b, (void*)(&b+1), (void*)(&b+2));

On a system with 32-bit ints this prints addresses separated by eight bytes - the size of int[2]:

0xbfbd2e58
0xbfbd2e60
0xbfbd2e68
like image 45
Sergey Kalinichenko Avatar answered Sep 25 '22 15:09

Sergey Kalinichenko