【问题标题】:Casting pointer to memory buffer to pointer to VLA将指向内存缓冲区的指针转换为指向 VLA 的指针
【发布时间】:2015-04-15 23:42:57
【问题描述】:

在 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 中,使用 VLA 执行此操作是否安全?

    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 标准,这是合法的吗?

如果它是合法的,那就太奇怪了,因为这意味着 VLA 可以有效地启用 动态类型创建,例如,type(*)[variable] 类型的类型。

【问题讨论】:

  • 注意:我确认 OP 的代码 compiles on IDEONE 并按照 OP 所说的对不同于 4 的数组大小工作,这意味着它输出您设置的任何 ARRSIZEarrSize 到。跨度>
  • 根据 C11 草案规范它是合法的。我没有 C99 的副本,所以我无法确认它在上个世纪是合法的。
  • @user3386109:我问的是 C99,因为在 C11 中 VLA 是可选的
  • @MattMcNabb,是的,有%zd。对应size_t的有符号类型。
  • @Mints97,也考虑完全放弃你的分配功能。它没有做任何简单的malloc(sizeof(int[n][m])) 无法实现的事情,并且您可以避免多余的演员表。演员阵容很糟糕。

标签: c arrays pointers c99


【解决方案1】:

是的,这是合法的,是的,可变修改类型系统非常有用。您可以使用自然数组语法来访问一个连续的二维数组,其维度直到运行时才知道。

它可以被称为语法糖,因为没有它们就无法使用这些类型,但它可以生成干净的代码(在我看来)。

【讨论】:

  • 谢谢,这是一个有趣的答案。我从未考虑过将 VLA 用作 2D 数组。
  • @Mints97 如果您还没有想到,您也可以在此处使用经过 SO 批准的 malloc idiom ptr = malloc(N * sizeof *ptr);,例如int (*ptr)[cols] = malloc(rows * sizeof *ptr);
  • 谢谢,我知道 =) 我只是对 C 类型系统的这种复杂性感兴趣。
【解决方案2】:

我会说它是有效的。 Final version of the C99 standard(引用 Wikipedia)在第 7.5.2 段 - Array declarators alinea 5 中说: 如果大小是一个不是整数常量表达式的表达式:... 每次对其进行评估时,它的值都应大于零。每个实例的大小 可变长度数组类型在其生命周期内不会改变

它甚至明确表示它可以用于sizeof 只要尺寸永远不会改变尺寸在哪里 表达式是 sizeof 运算符的操作数的一部分,并更改 size 表达式不会影响运算符的结果,它是否未指定 评估大小表达式

但该标准还规定,这仅允许在块范围声明符或函数原型中:具有可变修改类型的普通标识符(如 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
    };
...

【讨论】:

    猜你喜欢
    • 2017-05-15
    • 2019-04-20
    • 2014-09-22
    • 2021-01-13
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多