C数组指针问题

时间:2009-07-11 15:33:46

标签: c arrays pointers

我认为数组变量不能在C中更改,即基数 数组的地址是不可更改的,但以下代码是矛盾的 我的假设:

#include <stdlib.h>
#include <stdio.h>

void changeArray(int **a)
{
   *a = malloc(sizeof(int));  
}

int main()
{
   int a[10];
   a[0] = 1;
   printf("%d\n",a[0]);
   changeArray(&a);
   printf("%d\n",a[0]);
}

此代码打印:

1
6750576(some random value)

很明显,数组的基址已被更改。 怎么可能?

6 个答案:

答案 0 :(得分:14)

代码正在玩 evil 指针游戏,没有运气。您正在将类型为int(*)[10]的指针传递给需要类型为int**的指针的函数。您传递的指针的值为数组的“基址”。

因此,当您取消引用int**时,它会在该地址处认为它会获得int*,即使它看到的是int对象。它将从malloc返回的地址写入该存储单元。

回到主页,打印出该单元格。请注意,它是数组第一个元素的,而不是数组第一个元素的地址。所以打印的是您在被调用函数中写入的地址的整数值解释。

你做的是未定义的行为:该函数想要一个int**,所以你必须给它一个int**


以下是我认为您对此事的看法

  
      
  • 您听到有人说数组名称是指向其第一个元素的常量指针
  •   
  • 你获取该指针的地址,抛弃任何const
  •   
  • 您乐意在指针中写入其他地址,并希望它不会崩溃
  •   
  • 您再次使用该数组,期望它现在“覆盖”malloc创建的未初始化的内存区域。
  •   

但这种观点存在缺陷。第一点是最有缺陷的,因为数组名称不是常量指针。如果第一点是正确的,那么实际上你的代码片段会更有意义。但是,在极少数情况下(sizeof,address-of),数组名称将生成一个地址值,该值在您在表达式中使用时引用其第一个元素。

因为在使用address-of时不会生成该地址值,所以您将获得指向该数组的指针(这正是您使用address-of运算符编写的)。由于数组及其第一个元素具有相同的地址是有意义的,因此数组的地址恰好等于其第一个元素的地址。所以你实际上做的是写入第一个元素,而不是写入一些指针(实际上不存在)。


对评论的回应

考虑当阵列的类型在实践中很重要时(在这些实验中)会发生什么。你有一个数组,其元素本身就是数组。

// array of 3 "array of 10 int"
int a[3][10];

因此,通过在a&以外的表达式中使用sizeof,您将获得指向第一个元素int(*)[10]的指针。这是至关重要的,因为以下内容写入一个偏移2*sizeof(int)*10字节

的整数
a[2][0] = 1;
  // (a + 2) refers to a offset by 2*sizeof(int)*10 bytes

如果该表达式中的a会给你一个int**,那么编译器就不知道它应该在哪里正确存储整数1,因为有关该元素的任何大小信息类型丢失了。它肯定可以将大小存储在内存中,但在哪里?在数组中,没有空间。此外,sizeof无法再为您提供编译时结果。

答案 1 :(得分:2)

在此代码中,您尚未更改阵列的基址。您已更改数组中第一个元素的值。

答案 2 :(得分:1)

&a不会导致您认为int **。它评估为int *[10],就像a在以下代码段中所做的那样:

#include <stdio.h>

int main(void)
{
        int a[10];
        printf("%p %p\n", a, &a);
        return 0;
}

这为我打印:

0xbffff058 0xbffff058

因此,您的changearray函数正在将malloc返回的指针指定给a中数组main的第一个元素。

此外,如果您使用-Wall进行编译,则会看到警告(如您所愿):

bad.c:14: warning: passing argument 1 of ‘changeArray’ from incompatible pointer type

答案 3 :(得分:0)

您使用哪个编译器编译代码? 我尝试使用VC ++ 2005,代码是非法的,它说“不能将参数1从'int(* __ w64)[10]'转换为'int **'”

我对如何编译代码并获得所需结果感兴趣。

答案 4 :(得分:0)

表达式&a返回数组a的地址。该地址会被传递到chageArray()函数,就好像它是**int

changeArray()函数内,表达式*a一次取消引用指针,因此表达式的结果是与该地址处的int变量相对应的左值。

在changeArray()函数之外,您可以认为表达式&a&a[0]相同。

所以你在changeArray()函数中所做的是

*(&a[0]) = malloc(sizeof(int));

相当于

a[0] = malloc(sizeof(int));

答案 5 :(得分:0)

void changeArray(int **a)
{
    printf("\n *a=%p",*a);
    *a = malloc(sizeof(int));
    printf("\n *a=%p",*a);
}


int main(){

   int a[10];
   a[0] = 1;
   printf("\n..correct...Array Address=%p",a);
   printf("\nbefore.....%d",a[0]);
   changeArray(&a);
   printf("\nafter....%d",a[0]);

   printf("\n Array address=%p",a);
return 0;
}

数组地址= 0x7fff287e6130 之前:

1
*a=0x1
*a=0x13a3e010

之后:

329506832
Array address=0x7fff287e6130

329506832是十进制值0x13a3e010

您可以在Windows程序员计算器中看到它。

数组地址是一个const指针,因此它永远不会改变。

这里* a表示数组的第一个值...而不是数组的地址。 对于交叉检查...在changeArray函数迭代数组之后,你会知道它的第一个元素变化而不是数组地址是const poiter ....

你正在做的是使用malloc系统调用你请求4个字节的内存然后该地址将存储在第一个数组元素....

拿笔和纸,然后你就可以轻松搞定。