Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
275 views
in Technique[技术] by (71.8m points)

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

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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("
 C address: ");  // outputs correct values 
  for (i = 0; i < 2; i++) {
    printf("%p ", (void*)(c + i));
  }
  printf("
 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
", 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).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...