直接使用指向數組和數組地址的指針不一致

[英]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?

為什么兩個代碼示例的行為不同?

4 个解决方案

#1


9  

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:

創建一個兩個int的數組,其值為1,2。假設系統大小為int的是4個字節,那么數組b []應該存儲在內存中,如下所示:

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[].

在上圖中,存儲單元的值是多少?表示垃圾值而未分配(0xbf5c7884的內存未分配給我們的數組)。值1,2存儲在地址0xbf5c787c和0xbf5c7880的存儲器中,該存儲器在數組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:

讓我們而不是打印值,我們使用(c + i)和(&b + i)打印您在代碼中訪問的內存地址,為此請考慮以下程序:

#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.

檢查此代碼是否正常工作@Codepade注意,(c + i)打印值為1,2的單元格的正確地址,因此第一個代碼中的輸出是正確的。而(&b + i)打印未分配給數組b []的地址值(在數組b []之外)並且訪問此內存會在運行時給出未定義的行為(不可預測)。

Actually there is difference between b and &b.

實際上b和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是一個數組,它的類型是int [2],b在大多數表達式中作為第一個元素的地址衰減到int *中(讀取:一些異常,其中數組名稱沒有衰減成指向第一個元素的指針?)。並且b + 1指向數組中的下一個int元素(注意圖)。

  • &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).

    &b是完整數組的地址,其類型為int(*)[2],(&b + 1)指向未在程序中分配的int [2]類型的下一個數組(圖中的通知,其中(&b + 1) )分)。

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

要了解b和&b之間的一些其他有趣的差異,請閱讀:sizeof(&array)返回什么?

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.

在第一個代碼snipe中,當你執行c =&b時,你將數組的地址分配給int *(在我們的示例中為0xbf5c787c)。使用GCC編譯器,此語句將發出警告:“從不兼容的指針類型分配”。因為c是指向int的指針,所以*(c + i)打印存儲在地址(c + i)的整數。對於i = 1,值(c + 1)指向數組中的第二個元素(在我們的示例中為0xbf5c7880),因此*(c + 1)正確打印2。

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 * c =&b;在第一個代碼中我強烈建議閱讀@ AndreyT的答案如下。使用指針訪問數組元素的正確和簡單方法如下:

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.

在第二個代碼中,將i添加到&b使其指向外部分配的內存,而在printf語句中使用* dereference運算符訪問內存會導致無效的內存訪問,並且此代碼在運行時的行為為Undefined。這就是第二段代碼在不同執行時表現不同的原因。

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.

您的代碼編譯,因為語法上它是正確的,但在運行時,操作系統內核可以檢測到未分配的內存。這可能導致操作系統內核向導致異常的進程發送信號核心轉儲(有趣的是:由於OS檢測到進程的內存權限違規 - 對有效內存的無效訪問給出:SIGSEGV,並且訪問無效地址給出:SIGBUS)。值得一提的是,您的程序可以在沒有任何失敗的情況下執行並產生垃

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).

檢查@codepade。需要注意圍繞* c的括號,因為[]運算符的優先級高於* dereference運算符(而如果使用指向int的指針,則不需要括號,如上面的代碼所示)。

#2


5  

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

這是因為指針算術運算ptr + i應用於的指針類型:

  • 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.
  • 在第一種情況下,將i添加到指向int的指針,這與索引數組相同。由於指向數組的指針與指向其第一個元素的指針相同,因此代碼可以正常工作。

  • 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.
  • 在第二種情況下,將i添加到指向兩個int數組的指針。因此,添加會使您超出分配的內存,從而導致未定義的行為。

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]:

在具有32位整數的系統上,這將打印以8個字節分隔的地址 - int [2]的大小:

0xbfbd2e58
0xbfbd2e60
0xbfbd2e68

#3


1  

int *c = &b;     

This is actually not valid. You need a cast.

這實際上是無效的。你需要一個演員。

int *c = (int *) &b; 

The two expressions:

這兩個表達式:

 *(c+i)

 and 

 *(&b+i)

are not same. In the first expression i is added to a int * and in the second expression i is added to a int (*)[2]. With int *c = (int *) &b; you convert a int (*)[2] to an int *. c + i points to the i-th int element of c but &b+i points to the int [2] element of b moving the pointer value outside the actual array object.

不一樣。在第一個表達式中,i被添加到int *中,而在第二個表達式中,i被添加到int(*)[2]中。用int * c =(int *)&b;你將int(*)[2]轉換為int *。 c + i指向c的第i個int元素但是&b + i指向b的int [2]元素,將指針值移動到實際數組對象之外。

#4


1  

The first code is broken. The assignment

第一個代碼被破壞了。分配

int *c = &b;

is invalid. The right-hand side has type int (*)[2], while the object on the left has type int *. These are different, incompatible types. The compiler should have told you about this error by issuing a diagnostic message. Don't ignore diagnostic messages issued by compilers.

是無效的。右側的類型為int(*)[2],而左側的對象的類型為int *。這些是不同的,不兼容的類型。編譯器應該通過發出診斷消息告訴您有關此錯誤的信息。不要忽略編譯器發出的診斷消息。

The code, despite suffering from the above problem, was accepted by the compiler due to a non-standard compiler extension, which allowed the compiler to convert int (*)[2] pointer to int * type preserving the numerical value of the pointer (the physical address). So, you ended up with int * pointer pointing to the beginning of your int [2] array. Not surprisingly, accessing memory through that pointer lets you to see the contents of the array.

盡管遇到上述問題,代碼由於非標准的編譯器擴展而被編譯器接受,這允許編譯器將int(*)[2]指針轉換為int *類型,保留指針的數值(物理地址)。所以,最終你的int *指針指向int [2]數組的開頭。毫不奇怪,通過該指針訪問內存可以讓您查看數組的內容。

Your second code is also broken in more than one way. It doesn't suffer from the first problem, since you don't force any conversion of &b value (which, again, has type int (*)[2]) to anything else, but apply pointer arithmetic directly to &b. According to the rules of pointer arithmetic, the expression &b + 1 produces a pointer that points beyond the original b array. Dereferencing such pointer is illegal. So, *(&b + 1) already produces undefined behavior. On top of that, the expression *(&b + 1) has type int [2], which decays to pointer type int *. So, in your second code you are attempting to print an int * value with %d format specifier. This is also undefined behavior. The manifestations of that undefined behavior is what you see in your second example.

您的第二個代碼也會以多種方式被破壞。它不會受到第一個問題的影響,因為你不強制將&b值(同樣具有類型int(*)[2])的任何轉換強制轉換為其他任何東西,而是將指針算法直接應用於&b。根據指針算法的規則,表達式&b + 1產生一個指向超出原始b數組的指針。取消引用這樣的指針是非法的。所以,*(&b + 1)已經產生了未定義的行為。最重要的是,表達式*(&b + 1)具有類型int [2],它衰減到指針類型int *。因此,在第二個代碼中,您嘗試使用%d格式說明符打印int *值。這也是未定義的行為。這個未定義行為的表現形式就是你在第二個例子中看到的。

In other words, in the first piece of code you got luckier than in the second one, which is why the output of the former looks more meaningful.

換句話說,在第一段代碼中,你比第二段更幸運,這就是為什么前者的輸出看起來更有意義。


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2013/07/15/72a2bd1a6ef7a783b99c412fc8d355fe.html



 
粤ICP备14056181号  © 2014-2020 ITdaan.com