我认为数组变量不能在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)
很明显,数组的基址已被更改。 怎么可能?
答案 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个字节的内存然后该地址将存储在第一个数组元素....
拿笔和纸,然后你就可以轻松搞定。