【问题标题】:Constant array types in C, flaw in standard?C中的常量数组类型,标准中的缺陷?
【发布时间】:2008-11-20 13:28:10
【问题描述】:

C99 规范第 6.7.3.8 段声明

如果数组类型的规范包括任何类型限定符,则元素类型是如此限定的,而不是数组类型。如果函数类型的规范包含任何类型限定符,则行为未定义。

rationale(逻辑页87,物理页94)中,给出了将平面指针转换为(可变长度)数组指针的示例。

void g(double *ap, int n)
{
    double (*a)[n] = (double (*)[n]) ap;
    /* ... */ a[1][2] /* ... */
}

当然,如果数组ap没有在函数内被修改,它应该被标记为const,但是强制转换

void g(const double *ap, int n)
{
    const double (*a)[n] = (const double (*)[n]) ap;
    /* ... */
}

不保留 const 限定符,因为(根据 6.7.3.8)它适用于目标的元素,而不是目标本身,它具有数组类型 double[n]。这意味着如果给出适当的标志(-Wcast-qual 用于 GCC),编译器将正确地抱怨。没有办法在 C 中表示 const 数组类型,但这种转换非常有用且“正确”。 -Wcast-qual 标志可用于识别数组参数的滥用,但误报会阻止其使用。请注意,索引a[i][j] 的可读性更高,并且与许多编译器相比,生成的机器代码比ap[i*n+j] 更好,因为前者允许将一些整数算术从内部循环中提升出来,而无需进行分析。

编译器是否应该将此视为一种特殊情况,有效地将限定符从元素提升到数组类型,以确定给定的强制转换是否删除了限定符,或者是否应该修改规范?没有为数组类型定义赋值,因此与 6.7.3.8 相比,限定符总是应用于数组类型而不仅仅是元素会不会造成伤害?

【问题讨论】:

  • ~(logical page N ≡ physical page N) 时让我感到很头疼。

标签: c standards


【解决方案1】:

这是一个已知问题,在过去 10 年中,comp.std.c 已多次讨论过该问题。最重要的是,您提出的具体案例目前在标准 C 中是不合法的;您需要删除限定符或避免使用指向数组的指针来引用数组中的限定元素。

如果您认为自己有解决问题的好主意,可以将其发布到news:comp.std.c 进行讨论。如果其他人同意这是一个好主意,您或其他人可以提交缺陷报告以改变行为(有几个委员会成员经常使用 comp.std.c,因此可能会审查 DR 的人的反馈会在提交之前很有用)。我认为您提出的让限定符影响数组本身的提议可能存在一些问题,但我必须多考虑一下。

【讨论】:

  • 谢谢,我知道我不是第一个遇到这种情况的人。当我搜索 comp.std.c 时,我发现了几个人们想要一个类似的任务但没有明确演员表的情况。有了演员表,我认为这个例子是完全安全的并且符合标准,尽管它看起来对编译器是错误的。
  • C99 添加了几个特性,使 C 对数字更有吸引力,尤其是 VLA 和限制。代替解决这种情况的标准,如果编译器能够确定强制转换实际上是安全的,那就太好了。
  • 它可能是“安全的”,因为它在现有实现中不能按预期工作,但根据今天标准的措辞方式,我认为编译器是正确的,这在技术上是不正确的t 有效的 C 代码。
  • 不管怎样,编译器只在给出特殊标志时才会抱怨(-Wcast-qual 用于 GCC,不包括在 -Wall -Wextra 中)。当前的 Intel 和 PGI 编译器中不提供此类标志。我的想法是规范可以使 const 提升精确,因此演员表不会“看起来很危险”。
  • @MichaelGraczyk 不,这是一个指向可变数组的const 指针(您的版本允许使用a[i][j] = 0.0)。我想要一个指向const 数据的指针。请注意,gcc -Wcast-qual 警告您的版本会丢弃来自指针目标类型的 const 限定。
【解决方案2】:

C 程序员(但不是编译器设计者)可能的解决方法:

-Wcast-qual 的 gcc 不会对此抱怨:

void g(const double *ap, int n)
{
    int i;
    struct box 
    {
      double a[n];
    };
    const struct box *s = (const struct box *)ap;

    for (i=0; i<n; ++i)
    {
       doStuffWith(s->a[i]);
       /* ... */
    }
}

即使它不是很优雅。尾随数组成员 a 在 C89 和 C99 之间的含义也略有不同,但至少你得到了预期的效果。

【讨论】:

  • 它实际上看起来像s[i].a[j],编译器可能会以与a[i][j] 相同的方式对其进行优化。此解决方案避免了警告,但它增加了严重的混淆,特别是对于我使用的 4 维数组。
【解决方案3】:

指针(即数组)的情况很尴尬,但这是我对细节的回忆:

const double *ap 是一个指向双精度常量的指针;

double *const ap 是一个指向 double 的常量指针;

const double *const ap 是一个指向常量 double 的常量指针;

所以我相信可以按照您的要求进行操作,尽管我已经很多年没有尝试过了——您使用的 gcc 选项在我上次执行此操作时不可用!

编辑:这个答案对于这个问题是不正确的 - 我把它留下来保留下面的 cmets,它为普通人(或生锈的 C 开发人员......)澄清了这个问题

【讨论】:

  • 我的 C 生锈了,但为什么方法签名中的 const double *const ap 不能完全按照他的要求执行?常量数组引用和常量元素引用?
  • 不,示例中的 const 都是指元素,而不是指针。语言(或编译器?)的一个怪癖使它看起来好像没有保留元素上的 const 限定符,但在逻辑上它被保留了。
  • 第二个const 无关紧要,因为我们不关心更改指针ap 的值。我们想将“指向 const double 的指针”转换为“指向 const double 数组的指针”,这在 C 中无法表达,因此我们将其转换为“指向 const double 数组的指针”,这会触发警告。
  • > 你使用的 gcc 选项在我上次执行此操作时不可用 BTW,-Wcast-qual 至少从 2.5.8(1993 年)开始就在 gcc 中。
  • @Jed - 应该给你一个关于我上次这样做的提示!说真的,大概是在 90 年代中后期的某个时间,所以当时我们的产品很可能支持 2.5.8 或更早版本。
猜你喜欢
  • 2021-03-19
  • 2011-03-12
  • 1970-01-01
  • 2012-06-05
  • 2014-12-16
  • 1970-01-01
  • 1970-01-01
  • 2021-11-04
  • 2021-04-09
相关资源
最近更新 更多