C 指向数组第一个元素的常量指针的指针是如何工作的?

C 指向数组第一个元素的常量指针的指针是如何工作的?,c,arrays,pointers,C,Arrays,Pointers,我想测试是否可以更改指向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(

我想测试是否可以更改指向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)。当我尝试此操作时,程序崩溃。

程序在
printf
处崩溃,因为
test
假设当它取消引用
a
时,结果对象是指针。如果它是一个,并且包含一个有效地址,则第二次取消引用将生成一个int对象。唉,
a
包含数组的地址,它在数字上是它的第一个元素的地址。其中的4或8个字节被视为一个地址(因为
test
认为
*a
是一个指针),然后代码尝试访问地址1处的内存,以便打印该地址处假定的int值。但是地址无效,因此程序崩溃


既然我们已经确定程序将数组开头的数据视为指向int的指针,我们就知道
(*a)+
的作用:它将数组中的值增加
sizeof(int)
,以便“指针”指向下一个int“元素”。我猜您机器上的int是4字节长的,因为1+4=5,这是打印出来的。

通过执行
&a
您正在创建一个指向数组的指针(
int(*)[]

然后,当这个指向数组的指针被传递到
test
函数时,它被转换为指向指针的指针(
int**

然后
(*a)+是UB

1。那么为什么是5?

在类似于GCC的现代C实现中,指向数组的指针与数组的开头具有相同的数值,当数组衰减为指针时,地址值也具有相同的数值:它们都是数组的开头地址

因此,在
test
中,
int**a
指向数组的开头,
(*a)++
将指针延迟为
int*
,并将指针增加1
int
元素,这通常是通过将
sizeof(int)
添加到指针的数值来实现的

然后,
1+sizeof(int)
给你5

2。为什么在第二种情况下会崩溃?


假设您使用的是32位x86机器,或指针类型与
int
type大小相同的机器,则
*a
等于
1
。然后进一步取消对
1
处内存地址的指针的引用通常会导致segfault。

此代码在C中是非法的,您应该得到编译器诊断。(如果没有,请提高警告级别)。运行生成的任何可执行文件的结果都是毫无意义的

该代码是非法的,因为
int(*)[5]
未隐式转换为
int const**

指向C中数组的第一个元素的常量指针

没有这样的事。你误解了数组是什么。数组是一系列连续的元素
inta[5]
类似于
inta除了有
5
整数而不是
1
之外


inta
inta[1]导致相同的内存布局。唯一的区别是用于访问该内存的语法。

我发现看到
printf(“%p\n”,a)很有启发性
printf(“%p\n”,&a)a
是数组时,code>打印相同的地址<代码>&a
的类型为
int(*)[5]
,与您所说的不同。另外,由于它不是数组类型,所以不会衰减(更不用说衰减为
int**
)。