直接使用指向数组和数组地址的指针不一致

gab*_*r12 8 c pointers

此代码示例正确打印数组.

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

而这个打印两个垃圾值.

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

为什么两个代码示例的行为不同?

Gri*_*han 9

声明:

int b[2] = {1, 2};
Run Code Online (Sandbox Code Playgroud)

创建两个数组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 
Run Code Online (Sandbox Code Playgroud)

在上图中,具有值的存储器单元? 意味着垃圾值而未分配(存储器来自0xbf5c7884未分配给我们的阵列).的值1,2在地址存储在存储器中0xbf5c787c0xbf5c7880,即在阵列分配b[].

让我们来代替印刷价值观,我们打印您使用您的代码访问的内存地址(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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

 C address: 0xbf5c787c 0xbf5c7880 
 B address: 0xbf5c787c 0xbf5c7884 
Run Code Online (Sandbox Code Playgroud)

检查此代码是否正常工作@ Codepade
通知,(c + i)打印带有值的单元格的正确地址1,2因此第一个代码中的输出是正确的.而(&b + i)打印地址值未分配给数组b[](在数组之外b[])而访问此内存会在运行时给出未定义的行为(不可预测).

实际上b和之间存在差异&b.

  • b是一个数组,它的类型是int[2],b衰变为int*大多数表达式中第一个元素的地址(读取:一些例外,其中数组名称不会衰减成指向第一个元素的指针?).并b + 1指向int数组中的下一个元素(注意图表).

  • &b是完整数组的地址,其类型是int(*)[2], (&b + 1)指向int[2]未在程序中分配的下一个类型数组(在图中注意其中的(&b + 1)点).

要知道与其他一些有趣的差异b&b阅读:什么sizeof(&array)回报?

在第一个代码snipe中,当你这样做时c = &b,你将分配数组的地址int*(在我们的例子中0xbf5c787c).使用GCC编译器,此语句将发出警告:"从不兼容的指针类型分配".
因为c 是指针int,所以 *(c + i)打印存储在地址的整数(c + i).对于 数组中第二个元素i = 1 的值(c + 1)(在我们的示例中0xbf5c7880)因此正确*(c + 1)打印2.

关于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 
}
Run Code Online (Sandbox Code Playgroud)

在你的第二个代码,添加i&b使其指向外面分配的内存和printf语句您使用访问内存的*引用操作原因在运行时这些代码无效的内存访问和行为是未定义的.这就是第二段代码在不同执行时表现不同的原因.

您的代码编译因为语法正确,但在运行时,OS内核可以检测到未分配的内存.这可能导致操作系统内核向导致异常的进程发送信号核心转储(有趣的是:由于OS检测到进程的内存权限违规 - 对有效内存的无效访问给出:SIGSEGV,并且访问无效地址给出:SIGBUS).值得一提的是,您的程序可以在没有任何失败的情况下执行并产生垃

关于第二个代码,使用'指向数组的指针'打印数组的正确方法如下:

#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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

b[0] = (*c)[0] = 1
b[1] = (*c)[1] = 2  
Run Code Online (Sandbox Code Playgroud)

检查@ codepade.*c由于[]操作符的优先级高于* 取消引用操作符(而如果使用指向int的指针,则不需要像上面的代码那样使用括号),需要注意周围的括号.


das*_*ght 5

这是因为ptr+i应用指针算术运算的指针类型:

  • 在第一种情况下,您添加i指针int,这与索引数组相同.由于指向数组的指针与指向其第一个元素的指针相同,因此代码可以正常工作.
  • 在第二种情况下,您添加i指向两个数组的指针int.因此,添加会使您超出分配的内存,从而导致未定义的行为.

以下是这一点的快速说明:

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

在具有32位ints 的系统上,这将打印以8个字节分隔的地址 - 大小为int[2]:

0xbfbd2e58
0xbfbd2e60
0xbfbd2e68
Run Code Online (Sandbox Code Playgroud)


oua*_*uah 1

int *c = &b;     
Run Code Online (Sandbox Code Playgroud)

这实际上是无效的。你需要一个演员阵容。

int *c = (int *) &b; 
Run Code Online (Sandbox Code Playgroud)

两个表达式:

 *(c+i)

 and 

 *(&b+i)
Run Code Online (Sandbox Code Playgroud)

不一样。在第一个表达式中i添加到 a int *,在第二个表达式中i添加到 a int (*)[2]。随着int *c = (int *) &b;您将 a 转换int (*)[2]int *. c + i指向but的i第-个int元素,指向将指针值移动到实际数组对象之外的元素。c&b+iint [2]b

  • 确实,“int *c = &amp;b;”无效,但添加强制转换并不是解决方案。如果您想要第一个元素的地址,请编写“int *c = b;”或“int *c = &amp;b[0];”。如果你想要数组的地址,请写“int (*c)[2] = &amp;b;”。将指针从一种类型转换为另一种(不兼容)类型很少能解决问题——除非您问的是一个相当奇怪的问题。 (2认同)