我想测试是否可以更改指向C中数组的第一个元素的常量指针。测试时我得到了一些我不理解的奇怪输出:
//Constant pointer to pointer to constant value
void test(int const * * const a) {
//printf("%d", **a); //Program crashes (2)
(*a)++;
}
int main()
{
int a[5] = { 1,2,3,4,5 };
test(&a);
printf("%d", *a); //Prints 5 as output (1)
return 0;
}
我希望编译器在尝试编译(* a)++时给出错误,但我可以运行代码,但是当我尝试打印元素时,我得到一个奇怪的值(1)。
然后我想打印出数组第一个元素的值(2)。当我尝试这个时,程序崩溃了。
答案 0 :(得分:2)
程序在printf
崩溃,因为test
假定在解引用a
时,结果对象是指针。如果它是一个并且包含有效地址,则第二个解除引用将产生一个int对象。唉,a
包含数组的地址,它在数字上是第一个元素的地址。 4或8个字节被认为是一个地址(因为test
认为*a
是指针),然后代码尝试访问地址1 的内存以便打印在该地址处假定的int值。但地址无效,因此程序崩溃。
现在我们已经确定程序认为数组开头的数据是指向int的指针,我们知道(*a)++
做了什么:它将值增加sizeof(int)
以便“指针“指向下一个int”元素“。我猜你机器上的int是4个字节长,因为1 + 4 = 5,打印出来。
答案 1 :(得分:2)
通过执行&a
,您正在创建一个指向数组的指针(int (*)[]
)。
然后当这个指向数组的指针传递给test
函数时,它被转换为指向指针的指针(int **
);
然后(*a)++;
是UB。
<强> 1。那么为什么5?
在类似GCC的C的现代实现中,指向数组的指针具有与数组开头相同的数值,因此当数组衰减到指针时的地址值:它们都是数组的起始地址。
因此,在test
中,int **a
指向数组的开头,(*a)++
将指针称为int *
,并将指针递增1 int
element,通常实现为将sizeof(int)
添加到指针的数值。
然后,1+sizeof(int)
给你5。
<强> 2。为什么它在第二种情况下崩溃?
假设您使用的是32位x86计算机或指针类型与int
类型相同的某台计算机,则*a
等于1
。然后在1
的内存地址处进一步解除引用指针通常会给你一个段错误。
答案 2 :(得分:1)
此代码在C中是非法的,您应该获得编译器诊断。 (如果没有,请调高警告级别)。运行任何可执行文件的结果都是没有意义的。
代码是非法的,因为int (*)[5]
不会隐式转换为int const **
。
指向C
中数组的第一个元素的常量指针
没有这样的事情。你误解了什么是数组。数组是一系列连续的元素。 int a[5]
与int a;
类似,只是有5
个而不是1
。
int a;
和int a[1];
导致相同的内存布局。唯一的区别是用于访问该内存的语法。