Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 不兼容指针分配的技术合法性_C_Gcc_Clang_Language Lawyer_C11 - Fatal编程技术网

C 不兼容指针分配的技术合法性

C 不兼容指针分配的技术合法性,c,gcc,clang,language-lawyer,c11,C,Gcc,Clang,Language Lawyer,C11,C11标准ISO/IEC 9899:2011(E)规定了§6.5.16.1/1中简单赋值的以下约束条件: 下列情况之一应适用: 左操作数具有原子、限定或非限定算术类型,右操作数具有原子、限定或非限定算术类型 算术类型 左操作数具有结构或并集的原子、限定或非限定版本 与权利类型兼容的类型 左操作数具有原子、限定或非限定指针类型,以及 左值转换后左操作数的类型)两个操作数都是 指向兼容类型的合格或不合格版本的指针,以及指向的类型 左边的to具有右边所指类型的所有限定符 左操作数具有原子、限定或非限

C11标准ISO/IEC 9899:2011(E)规定了§6.5.16.1/1中简单赋值的以下约束条件:

下列情况之一应适用:

  • 左操作数具有原子、限定或非限定算术类型,右操作数具有原子、限定或非限定算术类型 算术类型
  • 左操作数具有结构或并集的原子、限定或非限定版本 与权利类型兼容的类型
  • 左操作数具有原子、限定或非限定指针类型,以及 左值转换后左操作数的类型)两个操作数都是 指向兼容类型的合格或不合格版本的指针,以及指向的类型 左边的to具有右边所指类型的所有限定符
  • 左操作数具有原子、限定或非限定指针类型,以及 左值转换后左操作数的类型(一个操作数是指针) 另一个是指向对象类型的合格或不合格版本的指针 void,并且左侧指向的类型具有指向的类型的所有限定符 凭权利
  • 左操作数是原子指针、限定指针或非限定指针,右操作数是空指针 指针常数;或
  • 左侧操作数的类型为原子、限定或非限定
    \u Bool
    ,右侧为指针
我感兴趣的是,在这种情况下,两边都是指向与
void
不同的不兼容类型的指针。如果我理解正确,这至少应该调用UB,因为它违反了这个约束。不兼容类型的一个例子应该是(根据§6.2.7和§6.7.2)
int
double

因此,应违反以下程序:

int main(void) {
  int a = 17;
  double* p;
  p = &a;
  (void)p;
}
gcc和clang都警告“-Wincompatible指针类型”,但不中止编译(使用
-std=c11-Wall-Wextra-pedantic进行编译)

类似地,下面的程序只会导致“-Wint conversion”警告,而编译很好

int main(void) {
  int a;
  double* p;
  p = a;
  (void)p;
}

来自C++,我预期任何一个测试用例都需要一个强制编译。有没有任何理由说明这两个项目都是合法的?或者,即使在通过显式使用

-std=c11
而不是
-std=gnu11
禁用娱乐性GNU C扩展时,支持这种代码风格至少有重要的历史原因吗?

编译器标志(gcc和clang)要求检查严格的标准符合性并拒绝编译不符合标准的代码是学究性错误:

$ gcc -std=c11 -pedantic-errors x.c
x.c: In function ‘main’:
x.c:3:15: error: initialization from incompatible pointer type [-Wincompatible-pointer-types]
   double* p = &a;
               ^
叮当声:

$ clang -std=c11 -pedantic-errors x.c
x.c:3:11: error: incompatible pointer types initializing 'double *' with an
      expression of type 'int *' [-Werror,-Wincompatible-pointer-types]
  double* p = &a;
          ^   ~~
1 error generated.

在野外,有相当一部分典型的C代码(至少可以说)是不符合标准的,因此学究式的错误会导致大多数C程序和库无法编译。

您的代码示例和引用的标准不匹配。示例是初始化,6.5.16讨论了赋值

令人困惑的是,匹配类型要求在约束第6.5.16节中用于赋值,但在语义部分(6.7.9)中“仅”用于初始化。因此,编译器有“权利”不为初始化发出诊断

在C语言中,违反约束只需要“诊断”,编译器可以继续编译,但不能保证生成的可执行文件是有效的

在我的Debian测试平台上,两个编译器都给了我一个没有任何选择的诊断,所以我猜你的安装一定很旧很过时

有没有任何理由说明这两个项目都是合法的

这些计划不是“合法标准”。它们包含约束冲突,并且您已经从标准中引用了正确的文本

编译器通过生成约束冲突诊断来符合标准。本标准不要求在违反约束或其他错误程序的情况下中止编译

它没有用那么多的话来说明,但唯一合理的结论是,由于包含约束冲突的程序而生成的任何可执行文件都具有完全未定义的行为。(不过,我见过有人试图反驳)

推测如下:C(和C++)用于多种用途;有时,人们希望机器具有“高级汇编程序”,而不关心可移植性或标准。大概是编译器供应商将默认设置为他们认为目标受众更喜欢的设置。

否,是
这真的很简单

不,不是法律标准。是的,重要的历史原因

C最初甚至没有铸型。作为一种系统编程语言,使用它作为一个超级强大的美化汇编程序不仅是合理的,而且是最佳实践,在当时是真正的“唯一实践”

一条关键的信息应该能说明一些事情:编译器的工作不是实现或强制执行规范。该规范实际上只是一个建议。编译器的实际工作是编译所有编写过的C代码,包括pre-C11、pre-C99、pre-C89,甚至pre-K&R。这就是为什么有这么多可选限制的原因。具有现代代码样式的项目启用严格一致的模式

标准中有C的定义方式,实践中也有C的使用方式。可以看出,编译器不能拒绝构建代码


几十年来,开发人员一直在转向可移植的、严格一致的代码,但当C第一次出现时,它的使用有点像一个真正强大的汇编程序,这是地址算法和类型双关的开放季节。当时的程序大多是一次为一种体系结构编写的。

我(在编写本文的过程中)说的当然是对的