将指向较大数组的指针分配给指向较小VLA的指针

3

我注意到C编译器(gcc、clang、tinycc)允许将指向较大数组的指针分配给指向较小VLA的指针,而不会发出警告:

#include <stdio.h>
#if !__TINYC__
void take_vla(int N, char const X[1][N]) { printf("%zd\n", sizeof(*X)); }
#endif
int main()
{
    static char bigarray[]="0123456789abcdefghijklmnopqrstuvwxyz";
    //VLA
    int n = 3;
    char const (*subarray2)[n]=&bigarray;
    //char const (*subarray3)[(int){3}]=&bigarray; //VLA but clang doesn't see it as such (a bug, I guess)
#if !__TINYC__
    take_vla(3,&bigarray);
    take_vla(3,&"abcdefg");
#endif

    #if 0
        char const (*subarray1)[3]=&bigarray; //-Wincompatible-pointer-types
    #endif
}

这符合C语言标准吗?为什么?

在您声明 subarray2 时,n 仅用于地址计算,不会导致存储分配,例如在 subarray2++ 中,将添加 n 个字符的大小。当指针指向的对象与 n 的大小不同时,编译器可以在可能的情况下发出警告,但这将是运行时问题,通常不是编译时问题。 - Paul Ogilvie
1
@Skizz 是的,但是使用C语言的类型系统从来都不是那么简单。尝试将最后一个#if 0更改为#if 1,您将在该分配上获得警告(将较大的普通数组赋值给较小的普通数组)。如果“指针只是数字”应该没有关系,但对于编译器来说,它似乎有影响。 - Petr Skocik
1
@Skizz:在C语言中,指针不仅仅是CPU的数字,这是因为它们具有类型信息(这会影响C语言中的指针算术运算),并且因为它们是抽象的 - Eric Postpischil
2
代码不是严格符合C语言标准,因为赋值运算符的限制条件(C 2018 6.5.16.1 1)要求两个指针必须是指向兼容类型版本的指针,并且可能使两个数组类型的指针兼容的规则(6.2.7 3)在不同指定长度的数组情况下不提供这种兼容性。(在这种情况下,“指定”包括通过评估其大小表达式来确定长度。) - Eric Postpischil
@EricPostpischil:啊,我明白造成混淆的地方了,抱歉。人们读太多标准,就会变得对语法过于挑剔!(笑话) - Skizz
显示剩余8条评论
1个回答

3

const char[3]char[37] 不兼容。

"指向限定类型" 也不兼容于 "指向类型",不要将此与 "限定指针指向类型" 混淆。(遗憾的是,常量修饰符无法用于数组指针。)

相关部分是 C17 6.5.16.1 的简单赋值规则:

  • 左操作数具有原子、限定或未限定指针类型,并且(考虑了左值转换后左操作数将具有的类型)两个操作数都是指向兼容类型的限定或未限定版本的指针,并且左侧指向的类型具有右侧指向的类型的所有限定符;

查看各种编译器:

  • "gnu 模式" 下的 gcc 对检查 C 符合性毫无用处,必须使用 -std=cxx -pedantic-errors 进行编译。之后,gcc 的表现良好:gcc -std=c17 -pedantic-errors

    error: pointers to arrays with different qualifiers are incompatible in ISO C [-Wpedantic]

  • icc 给出与 gcc 相同的诊断结果,表现良好。

  • clang -std=c17 -pedantic-errors 不报错,因此显然不符合 C 标准。


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