在C中指向可变长度数组的指针的类型是什么?

tem*_*def 17 c pointers language-lawyer variable-length-array

这是一个简短的C程序,它提示用户输入一个数字,创建一个具有该大小的可变长度的int数组,然后使用指针算法来跳过已分配的元素:

#include <stdio.h>

int main() {
    /* Read a size from the user; inhibits compiler optimizations. */
    int n;
    scanf("%d", &n); // Yes, I should error-check. :-)

    /* We now have a VLA. */
    int arr[n];

    /* What is the type of &arr? */
    void* ptr = (&arr) + 1;

    /* Seems like this skipped over things properly... */
    printf("%p\n", arr);
    printf("%p\n", ptr);
}
Run Code Online (Sandbox Code Playgroud)

如果您愿意,可以在ideone上试试这个.输出表明该行

void* ptr = (&arr) + 1;
Run Code Online (Sandbox Code Playgroud)

获取可变长度数组中arr所有n元素的步骤,并以大小识别的方式执行.

如果这不是一个可变长度数组,我会完全放心它是如何工作的.编译器会知道它的类型arr(它将是int (*) [K]一些常量K),所以当我们向&arr它添加一个时可以跳过正确的字节数.

很明显,在运行时,我们可以如何评估(&arr) + 1.编译器arr会将堆栈中某处的大小存储起来,当我们向(&arr)它添加一个时,就知道要加载该大小以便计算要跳过的字节数.

但是,我不知道的是语言表达的类型是什么&arr.它是否分配了一些静态类型,表明它是一个可变长度数组(类似的东西int (*) [??])?规范是否说"表达式的类型是int (*) [K],K在运行时分配给数组的大小在哪里?" 规范是否禁止获取可变长度数组的地址,编译器恰好允许它?

Jon*_*ler 10

对于VLA,sizeof运算符不是编译时常量.它在运行时进行评估.在你的问题中,&arris 的类型是int (*)[n];- 指向n类型值数组的指针int,其中n是一个运行时值.因此,正如您所注意到的那样&arr + 1(除了括号中注明不需要括号之外不需要括号)是数组的开头arr- 地址是sizeof(arr)超出值的字节arr.

你可以打印尺寸arr; 它会给你合适的尺寸(printf()修饰符z).你可以打印之间的区别&arr + 1,并arr和你会得到大小为ptrdiff_t(printf()修改t).

因此:

#include <stdio.h>

int main(void)
{
    int n;
    if (scanf("%d", &n) == 1)
    {
        int arr[n];

        void* ptr = (&arr) + 1;

        printf("%p\n", arr);
        printf("%p\n", ptr);
        printf("Size: %zu\n", sizeof(arr));
        printf("Diff: %td\n", ptr - (void *)arr);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

两个样本运行(程序名称vla59):

$ vla59
23
0x7ffee8106410
0x7ffee810646c
Size: 92
Diff: 92
$ vla59
16
0x7ffeeace7420
0x7ffeeace7460
Size: 64
Diff: 64
$
Run Code Online (Sandbox Code Playgroud)

没有重新编译 - 但sizeof()每次运行程序时都是正确的.它是在运行时计算的.

实际上,您甚至可以每次使用不同大小的循环:

#include <stdio.h>

int main(void)
{
    int n;
    while (printf("Size: ") > 0 && scanf("%d", &n) == 1  && n > 0)
    {
        int arr[n];

        void* ptr = (&arr) + 1;

        printf("Base: %p\n", arr);
        printf("Next: %p\n", ptr);
        printf("Size: %zu\n", sizeof(arr));
        printf("Diff: %td\n", ptr - (void *)arr);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

样品运行vla11:

$ vla11
Size: 23
Base: 0x7ffee3e55410
Next: 0x7ffee3e5546c
Size: 92
Diff: 92
Size: 16
Base: 0x7ffee3e55420
Next: 0x7ffee3e55460
Size: 64
Diff: 64
Size: 2234
Base: 0x7ffee3e53180
Next: 0x7ffee3e55468
Size: 8936
Diff: 8936
Size: -1
$
Run Code Online (Sandbox Code Playgroud)