将“指向常量的指针”转换为“指向常量VLA的指针”。

8
在这个片段中,使用指向VLA的指针来更容易地访问大型查找表:
#pragma GCC diagnostic warning "-Wcast-qual"

char
lookup(int a, int b, int c, char const *raw, int x, int y, int z)
{
    typedef char const (*DATA_PTR)[a][b][c];

    DATA_PTR data = (DATA_PTR)raw;

    return (*data)[x][y][z];
}

GCC 6.2.0 在编译时出现问题,而 Clang 4.0.0(trunk) 则可以正常编译,两者在启用 -Wcast-qual 选项时均可。

In function 'lookup':
warning: cast discards 'const' qualifier from pointer target type [-Wcast-qual]
   DATA_PTR data = (DATA_PTR)raw;
                   ^

代码无论哪种方式运行都符合预期。
我猜测GCC可能会混淆“指向VLA的常量元素的指针”和“指向常量VLA的指针”,但我不确定...
有没有一种方法可以让GCC保持安静而不用操心警告? 这是GCC的一个bug吗?
编辑1:实际代码的详细信息:
struct table {
    int a;
    int b;
    int c;
    char *raw;
};

char
lookup2(struct table const *table, int x, int y, int z)
{
    typedef char const(*DATA_PTR)[table->a][table->b][table->c];

    DATA_PTR data;
    data = (DATA_PTR)table->raw; // GCC ok
    data = (DATA_PTR)(char const *)table->raw; // GCC raises -Wcast-qual

    return (*data)[x][y][z];
}

编辑2:

因此,在C11标准草案第6.7.3/9中指出:

如果数组类型的规范包括任何类型修饰符,则元素类型是带修饰符的,而不是数组类型。

请参见@hvd回答。

消除-Wcast-qual的一种方法:

    DATA_PTR data = (DATA_PTR)(intptr_t)raw;

3
“指向const元素的可变长数组指针”和“指向const可变长数组的指针”是同一样东西。一个const数组是由const元素组成的数组。看起来像是一个错误。 - emlai
为什么不让整个过程更加类型安全,将raw变成char const (*raw)[a][b][c] - StoryTeller - Unslander Monica
@StoryTeller 我已经添加了代码的样式,但是-Wcast-qual还是有点奇怪。 - diapir
@StoryTeller 这真是一件令人欣慰的事,谢谢。请随意将您的评论添加为答案,这样我就可以关闭问题了。干杯! - diapir
@diapir - 在GCC 6.3中也没有固定。显然,“-Wall -Wextra -pedantic”并不会打开“-Wcast-qual”(叹气)。 - StoryTeller - Unslander Monica
发布的代码返回了一个char,而不是一个const char,因此编译器(在这种情况下为gcc)会在语句typedef char const (*DATA_PTR)[a][b][c];和函数签名中丢弃const - user3629249
1个回答

7
这是C语言中长期存在的问题。这也是为什么……
int array[2];
const int (*ptr)[2] = &array;

在C语言中,以下声明是无效的(但在C++中有效):它声明了一个指向const限定整数数组的指针,这不是一个const限定的整数数组,因此不能应用指向该类型的指针可以隐式转换为指向该类型const限定版本的规则。
在你的情况下,你正在从const char *(指向const限定类型的指针)转换为char const (*)[a][b][c](指向非const限定类型的指针),这是-Wcast-qual警告的原因。
clang从未费心实现C语言的这种特殊性质,它将C代码视为C++语义,即const元素的数组本身也是const限定的。
通常可以通过在结构体中包装数组来解决这个问题:
typedef struct { char d[a][b][c]; } const *DATA_PTR;

但是这对于可变长度数组不是一个选择。我认为除了在这里根本不使用多维数组或者不使用-Wcast-qual外,没有其他合适的解决办法。


那就调整警告吧... 你知道在C语言中使用C++规则(如clang)可能存在潜在危险的情况吗?标准中没有这样的理由:6.7.3.9如果数组类型的规范包括任何类型限定符,则元素类型是被限定的,而不是数组类型。 - diapir
1
@diapir C++规则在const正确性方面是安全的,它也已经被提议用于C语言(尽管我不知道它的状态)。如果您现在使用它,唯一的风险是可能会意外地编写无效/尚未有效的C代码,然后在使用严格的C编译器时导致错误。 - user743382

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