不兼容指针赋值的技术合法性
technical legality of incompatible pointer assignments
C11 标准 ISO/IEC 9899:2011 (E) 在 §6.5.16.1/1 中规定了以下对简单赋值的约束:
One of the following shall hold:
- the left operand has atomic, qualified, or unqualified arithmetic type, and the right has
arithmetic type;
- the left operand has an atomic, qualified, or unqualified version of a structure or union
type compatible with the type of the right;
- the left operand has atomic, qualified, or unqualified pointer type, and (considering
the type the left operand would have after lvalue conversion) both operands are
pointers to qualified or unqualified versions of compatible types, and the type pointed
to by the left has all the qualifiers of the type pointed to by the right;
- the left operand has atomic, qualified, or unqualified pointer type, and (considering
the type the left operand would have after lvalue conversion) one operand is a pointer
to an object type, and the other is a pointer to a qualified or unqualified version of
void, and the type pointed to by the left has all the qualifiers of the type pointed to
by the right;
- the left operand is an atomic, qualified, or unqualified pointer, and the right is a null
pointer constant; or
- the left operand has type atomic, qualified, or unqualified
_Bool
, and the right is a pointer.
我对双方都是指向不同于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-pointer-types”,但不会中止编译(使用 -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)是 -pedantic-errors
:
$ 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 代码是不一致的,因此 -pedantic-errors
会导致大多数 C 程序和库无法编译。
您的代码示例与您对标准的引用不匹配。例子是初始化,6.5.16讲赋值
令人困惑的是,匹配类型要求在约束部分 6.5.16 中用于赋值,但 "only" 在语义部分 (6.7.9) 中用于初始化。所以编译器 "right" 不发出初始化诊断。
在C中,约束违规只需要"diagnostics",编译器可以继续编译,但不能保证生成的可执行文件是有效的。
在我的平台上,一个 Debian 测试,两个编译器都给我一个没有任何选项的诊断,所以我猜你的安装一定很旧和过时了。
Is there any reason why either of the programs would be standards-legal?
这些程序不是 "standards-legal"。它们包含约束违规,您已经从标准中引用了正确的文本。
编译器通过生成约束违规诊断来符合标准。该标准不要求编译在违反约束或其他错误程序的情况下中止。
它并没有说那么多,但唯一合理的结论是,由于包含违反约束的程序而生成的任何可执行文件都具有完全未定义的行为。 (虽然我看到人们试图反驳)。
推测如下:C(和 C++)用于多种用途;有时人们希望 "high level assembler" 用于他们的机器,而不关心可移植性或标准。据推测,编译器供应商将默认值设置为他们认为目标受众喜欢的值。
否,是
真的很简单。
不,不是合法的标准。是的,重要 历史原因。
C本来连演员表都没有。作为一种系统编程语言,将其用作超强大的美化汇编器不仅合理,而且是最佳实践,在过去确实 "only-practice"。
一条关键信息应该能说明问题:实现或强制执行规范确实不是编译器的工作。实际上,规范只是一个建议。编译器的实际工作是编译所有曾经编写过的 C 代码,包括 pre-C11、pre-C99、pre-C89,甚至 pre-K&R。这就是为什么有这么多可选限制的原因。具有现代代码风格的项目开启严格符合模式。
有标准中定义C的方式,也有实践中使用C的方式。所以你可以看到,编译器根本无法拒绝构建代码。
几十年来,开发人员一直在转向可移植的、严格符合标准的代码,但当 C 首次出现时,它的使用有点像一个非常强大的汇编程序,而且它是地址算术和类型双关的开放季节.那时的程序大多一次为一种架构编写。
C11 标准 ISO/IEC 9899:2011 (E) 在 §6.5.16.1/1 中规定了以下对简单赋值的约束:
One of the following shall hold:
- the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
- the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
- the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
- the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
- the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
- the left operand has type atomic, qualified, or unqualified
_Bool
, and the right is a pointer.
我对双方都是指向不同于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-pointer-types”,但不会中止编译(使用 -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)是 -pedantic-errors
:
$ 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 代码是不一致的,因此 -pedantic-errors
会导致大多数 C 程序和库无法编译。
您的代码示例与您对标准的引用不匹配。例子是初始化,6.5.16讲赋值
令人困惑的是,匹配类型要求在约束部分 6.5.16 中用于赋值,但 "only" 在语义部分 (6.7.9) 中用于初始化。所以编译器 "right" 不发出初始化诊断。
在C中,约束违规只需要"diagnostics",编译器可以继续编译,但不能保证生成的可执行文件是有效的。
在我的平台上,一个 Debian 测试,两个编译器都给我一个没有任何选项的诊断,所以我猜你的安装一定很旧和过时了。
Is there any reason why either of the programs would be standards-legal?
这些程序不是 "standards-legal"。它们包含约束违规,您已经从标准中引用了正确的文本。
编译器通过生成约束违规诊断来符合标准。该标准不要求编译在违反约束或其他错误程序的情况下中止。
它并没有说那么多,但唯一合理的结论是,由于包含违反约束的程序而生成的任何可执行文件都具有完全未定义的行为。 (虽然我看到人们试图反驳)。
推测如下:C(和 C++)用于多种用途;有时人们希望 "high level assembler" 用于他们的机器,而不关心可移植性或标准。据推测,编译器供应商将默认值设置为他们认为目标受众喜欢的值。
否,是
真的很简单。
不,不是合法的标准。是的,重要 历史原因。
C本来连演员表都没有。作为一种系统编程语言,将其用作超强大的美化汇编器不仅合理,而且是最佳实践,在过去确实 "only-practice"。
一条关键信息应该能说明问题:实现或强制执行规范确实不是编译器的工作。实际上,规范只是一个建议。编译器的实际工作是编译所有曾经编写过的 C 代码,包括 pre-C11、pre-C99、pre-C89,甚至 pre-K&R。这就是为什么有这么多可选限制的原因。具有现代代码风格的项目开启严格符合模式。
有标准中定义C的方式,也有实践中使用C的方式。所以你可以看到,编译器根本无法拒绝构建代码。
几十年来,开发人员一直在转向可移植的、严格符合标准的代码,但当 C 首次出现时,它的使用有点像一个非常强大的汇编程序,而且它是地址算术和类型双关的开放季节.那时的程序大多一次为一种架构编写。