将指向内存缓冲区的指针转换为指向可变长度数组的指针

14

在C语言中,我相信以下程序是有效的:将指向分配的内存缓冲区的指针转换为数组,如下所示:

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

#define ARRSIZE 4

int *getPointer(int num){
    return malloc(sizeof(int) * num);
}

int main(){
    int *pointer = getPointer(ARRSIZE);
    int (*arrPointer)[ARRSIZE] = (int(*)[ARRSIZE])pointer;

    printf("%d\n", sizeof(*arrPointer) / sizeof((*arrPointer)[0]));

    return 0;
}

(这将输出4)。

然而,在C99中,使用可变长度数组(VLAs)进行此操作是否安全?

    int arrSize = 4;
    int *pointer = getPointer(arrSize);
    int (*arrPointer)[arrSize] = (int(*)[arrSize])pointer;

    printf("%d\n", sizeof(*arrPointer) / sizeof((*arrPointer)[0]));

    return 0;

(也输出4)。

根据C99标准,这合法吗?

如果这是合法的,那将非常奇怪,因为这意味着可变长度数组有效地实现了动态类型创建,例如,类型为type(*)[variable]的类型。


2
注意:我确认OP的代码在IDEONE上编译通过,并且对于大小不为4的数组,它按照OP所说的输出,这意味着它会输出您设置的ARRSIZEarrSize - Mike Nakis
根据C11草案规范,这是合法的。我没有C99的副本,所以无法确认它在上个世纪是否合法。 - user3386109
@hyde:谢谢,我理解你的关注。我会尝试在未来遵循你的建议。你知道,我并不是一个经验丰富的SO用户 =) - user3079266
1
@MattMcNabb,是的,有%zd。它对应于size_t的带符号类型。 - Jens Gustedt
1
@Mints97,还考虑完全放弃你的分配函数。它什么也做不了,只需一个简单的malloc(sizeof(int[n][m]))就可以实现,而且您可以避免不必要的转换。转换是不好的。 - Jens Gustedt
显示剩余6条评论
2个回答

11
是的,这是合法的,而且“可变修改类型系统”非常有用。您可以使用自然数组语法访问一个连续的二维数组,两个维度在运行时未知。
它可以被称为语法糖,因为您没有这些类型也可以完成相同的操作,但它使代码更加清晰(我认为)。

谢谢,这是一个有趣的答案。我从未考虑过使用可变长度数组作为二维数组。 - user3079266
2
如果你还没有意识到,你可以在这里使用 SO 批准的 malloc 习惯用语,例如:ptr = malloc(N * sizeof *ptr);,这里也可以这样使用:int (*ptr)[cols] = malloc(rows * sizeof *ptr); - M.M
谢谢,我知道 =) 我只是对C语言的类型系统的复杂性感兴趣。 - user3079266
我经常使用这个功能将一个1维浮点数组(例如[20]个浮点数)强制转换为5x4矩阵、4x5矩阵、2x10矩阵等等。 - abelenky

2
我会说它是有效的。C99标准的最终版本(引用自维基百科)在第7.5.2段 - 数组声明符第5款中写道: 如果大小是不是整数常量表达式:... 每次计算时它都必须具有大于零的值。变长数组类型的每个实例的大小在其生命周期内不会改变。 它甚至明确表示可以在sizeof中使用,只要大小从不改变如果大小表达式是sizeof运算符的操作数的一部分,并且更改大小表达式的值不会影响运算符的结果,则未指定是否评估大小表达式。 但是标准还说,这只允许在块作用域声明符或函数原型中使用:一个具有可变长度类型的普通标识符(如6.2.3中定义的)应该具有块作用域和无链接或函数原型作用域。如果将标识符声明为具有静态存储期的对象,则不得具有可变长度数组类型。 稍后的一个例子解释了它不能用于成员字段,即使在块作用域中也不行:
...
void fvla(int m, int C[m][m]); // valid: VLA with prototype scope
void fvla(int m, int C[m][m]) // valid: adjusted to auto pointer to VLA
{
    typedef int VLA[m][m]; // valid: block scope typedef VLA
    struct tag {
        int (*y)[n]; // invalid: y not ordinary identifier
        int z[n]; // invalid: z not ordinary identifier
    };
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接