【发布时间】:2016-12-03 10:11:05
【问题描述】:
这将是一个冗长的语言律师问题,所以我想快速说明为什么我认为它相关。我正在从事一个项目,其中严格遵守标准至关重要(编写一种编译为 C 的语言)。我要给出的示例似乎是 clang 的标准违规,因此,如果是这种情况,我想确认一下。
gcc 表示带有指向限制限定指针的条件的条件不能与带有 void 指针的条件语句共同存在。另一方面,clang 可以很好地编译这些东西。这是一个示例程序:
#include <stdlib.h>
int main(void){
int* restrict* A = malloc(8);
A ? A : malloc(8);
return 0;
}
对于 gcc,选项 -std=c11 和 -pedantic 可以包含或不包含在任何组合中,对于 clang 以及选项 -std=c11 和 -Weverything 也是如此。无论如何,clang 编译没有错误,gcc 给出以下内容:
tem-2.c: In function ‘main’:
tem-2.c:7:2: error: invalid use of ‘restrict’
A ? A : malloc(8);
^
c11 标准对条件语句做了以下说明,强调:
6.5.15 条件运算符
...
- 第二个和第三个操作数应满足以下条件之一:
——两个操作数都有算术类型;
——两个操作数具有相同的结构或联合类型;
——两个操作数都是void类型;
——两个操作数都是指向兼容类型的合格或不合格版本的指针;
——一个操作数是一个指针,另一个是一个空指针常量;或
— 一个操作数是指向对象类型的指针,另一个是指向限定或非限定版本的 void 的指针。
...
- 如果第二个和第三个操作数都是指针,或者一个是空指针常量,并且 other 是指针,结果类型是指向具有所有类型限定符的类型的指针 两个操作数引用的类型。此外,如果两个操作数都是指向 兼容类型或兼容类型的不同限定版本,结果类型为 指向复合类型的适当限定版本的指针;如果一个操作数是 空指针常量,结果具有另一个操作数的类型;否则,一个操作数 是指向 void 或 void 的限定版本的指针,在这种情况下,结果类型是 指向适当限定版本的 void 的指针。
...
在我看来,上面的第一个粗体部分表示这两种类型可以一起使用,第二个粗体部分将结果定义为指向限制限定版本 void 的指针。但是,如下所示,该类型不存在,因此表达式被 gcc 正确识别为错误:
6.7.3 类型限定符,第 2 段
指针类型以外的类型,其引用类型是对象类型,不应限制限定。
现在,问题是这个示例程序违反了“不得”条件,因此需要通过以下方式产生错误:
5.1.1.3 诊断,第 1 段
一个符合要求的实现应产生至少一个诊断消息(在 实现定义的方式)如果是预处理翻译单元或翻译单元 包含违反任何语法规则或约束的行为,即使该行为也是显式的 指定为未定义或实现定义。诊断消息不需要 在其他情况下产生。
通过静默处理错误类型,clang 似乎不符合标准。这让我想知道 clang 还默默地做了什么。
我在 x86-64 Ubuntu 机器上使用 gcc 版本 5.4.0 和 clang 版本 3.8.0。
【问题讨论】:
-
您可能是对的...但是如果您将
malloc()的结果转换为条件,使得第三个操作数不再是指向 void 的指针,错误就会消失。 -
如果我是你,我会倾向于避免在我的项目的 to-C 编译器发出的 C 代码中使用
restrict。通过restrict限定可以启用多少额外优化,任何给定编译器实际上会执行多少,以及结果可能会提高多少性能都不清楚。另一方面,通过使用restrict限定符,您的代码承担了它必须满足的额外义务以避免未定义的行为,并且并非所有这些都可以由编译器检查。我只是看不到回报,如果有的话,证明风险是合理的。 -
@JohnBollinger,该语言非常注重性能,所以我认为性能受到影响可能是不可接受的。我已经编写了一个正式模型,该模型使用 C 的子集用于发出的代码,连同为该语言定义的约束,正式证明遵守 C 标准中限制的定义。换句话说,为程序员要求这个小规则列表允许编译器做所有其他事情来确保正确性。不过,您对额外义务的看法是正确的,这是很多工作!
标签: c compiler-errors clang c11 restrict