示例代码(t0.c):
static int arr[ ];
int main( void )
{
return arr[ 0 ];
}
static int arr[ ] = { 0 };
Run Code Online (Sandbox Code Playgroud)
调用:
$ gcc t0.c -std=c11 -Wall -Wextra
<nothing>
$ clang t0.c -std=c11 -Wall -Wextra
<nothing>
$ cl t0.c /std:c11 /Za
t0.c(1): error C2133: 'arr': unknown size
$ gcc t0.c -std=c11 -Wall -Wextra -pedantic
t0.c:1:12: error: array size missing in ‘arr’
$ clang t0.c -std=c11 -Wall -Wextra -pedantic
<nothing>
Run Code Online (Sandbox Code Playgroud)
C11,6.2.5 类型,22:
未知大小的数组类型是不完整的类型。对于那种类型的标识符,通过在后面的声明中指定大小(具有内部或外部链接)来完成。
C11,6.9.2 外部对象定义,3:
如果对象的标识符声明是临时定义并具有内部链接,则声明的类型不应是不完整的类型。
C11,J.2 未定义行为,1:
具有内部链接和不完整类型的对象的标识符用暂定定义 …
语境:
... C编译器严格符合C89/C90标准
...并设置 /Zc 编译器选项以严格符合要求
Visual Studio 2019 中的 C++ 一致性改进、行为更改和错误修复:
... 可以指定 /permissive 以关闭编译器中的严格一致性模式。
第二个选项旨在禁用严格一致性模式......
钟:
请注意,这并不严格符合 ISO C99 ...
MSVC 与 ANSI C89 和 ISO C99 标准兼容,但并不严格遵守。
问:“严格符合”的定义是什么?它是微软发明的吗?
注意:C (n2596.pdf) 和 C++ (n4849.pdf) 标准均未使用术语“严格符合”/“严格符合”应用于实现。实施要么符合,要么不符合。无渐变。
UPD。我的猜测:在“严格一致性”(关于实现)下,微软的意思是“不支持任何扩展的一致性实现”。
后续问题:“语义违规不需要诊断”的理由是什么?。
\nN2596 工作草案 \xe2\x80\x94 2020 年 12 月 11 日 ISO/IEC 9899:202x (E),6.9.2 外部对象定义,语义,3:
\n\n\n如果对象标识符的声明是暂定定义并且具有内部链接,则声明的类型不应是不完整类型。
\n
这看起来像一个约束 \xe2\x80\x94 为什么它被放置在“语义”部分(违反要求不需要诊断)而不是“约束”部分?
\nUPD。类似的问题:理解IEEE 754:为什么convertFromInt和convertToIntegerXXX被归类为算术运算而不是转换运算?。
\n我试图在编译时确定_Float16受支持的:
#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <float.h>
#ifdef FLT16_MAX
_Float16 f16;
#endif
Run Code Online (Sandbox Code Playgroud)
调用:
# gcc trunk on linux on x86_64
$ gcc -std=c11 -pedantic -Wall -Wextra
t0.c:4:1: warning: ISO C does not support the '_Float16' type [-Wpedantic]
# clang trunk on linux on x86_64
$ clang -std=c11 -pedantic -Wall -Wextra
t0.c:4:1: error: _Float16 is not supported on this target
Run Code Online (Sandbox Code Playgroud)
在这里我们看到 gcc 和 clang:
FLT16_MAX_Float16主要问题:如何在编译时正确判断_Float16是支持的?
额外的问题:如果不支持相应的浮点类型,C11(或更新的)标准是否要求不定义 _MIN/宏?_MAX例如,对于整数类型 ( <stdint.h>),它是正确的:“也不应定义关联的宏”(C11,7.20 整数类型 <stdint.h>,4)。浮动类型也一样吗? …
示例代码(t0.c):
#include <stdio.h>
float f(float a, float b, float c) __attribute__((noinline));
float f(float a, float b, float c)
{
return a * c + b * c;
}
int main(void)
{
void* p = V;
printf("%a\n", f(4476.0f, 20439.0f, 4915.0f));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
调用和执行(来自 godbolt.org):
# icc 2021.1.2 on Linux on x86-64
$ icc t0.c -fp-model=fast -O3 -DV=f
0x1.d32322p+26
$ icc t0.c -fp-model=fast -O3 -DV=0
0x1.d32324p+26
Run Code Online (Sandbox Code Playgroud)
生成的汇编代码是相同的: https: //godbolt.org/z/osra5jfYY。
为什么相同的生成汇编代码不会产生相同的输出?
为什么void* p = f;重要?
C11,6.10.1 有条件包含,约束,1(强调):
控制条件包含的表达式应为整数常量表达式
C11,6.6 常量表达式,6(强调):
整数常量表达式117)应具有整数类型,并且只能具有整数常量、枚举常量、字符
sizeof常量、结果为整数常量的表达式、_Alignof表达式以及作为强制转换的直接操作数的浮点常量的操作数。
$ cat t333.c
#if (int)1.0
#endif
$ gcc t333.c -std=c11 -pedantic -c
t333.c:1:10: error: missing binary operator before token "1.0"
1 | #if (int)1.0
Run Code Online (Sandbox Code Playgroud)
这里(int)1.0是整型常量表达式。它具有整数类型并具有操作数,该操作数是浮点常量,是强制转换的立即操作数。但是,每个gcc代码都是无效的。
这意味着 6.10 预处理指令中使用的“整数常量表达式”受到限制 [1]?标准中有明确规定吗?
[1] 例如,“除了sizeof结果为整数常量、_Alignof表达式和作为强制转换的直接操作数的浮点常量的表达式”
c language-lawyer constant-expression c-preprocessor preprocessor-directive
为什么这段代码:
\nextern void x;\nRun Code Online (Sandbox Code Playgroud)\n导致:
\n$ cl t555.c /std:c11 /Za\nt555.c(1): error C2182: 'x': illegal use of type 'void'\nRun Code Online (Sandbox Code Playgroud)\n这里什么是非法的?
\nUPD。使用案例:
\n$ cat t555a.c t555.p.S\n#include <stdio.h>\n\nextern void x;\n\nint main(void)\n{\n printf("%p\\n", &x);\n return 0;\n}\n\n .globl x\nx:\n .space 4\n\n$ gcc t555a.c -std=c11 -pedantic -Wall -Wextra -c && as t555.p.S -o t555.p.o && gcc t555a.o t555.p.o && ./a.exe\nt555a.c: In function \xe2\x80\x98main\xe2\x80\x99:\nt555a.c:7:20: warning: taking address of expression of type \xe2\x80\x98void\xe2\x80\x99\n 7 | printf("%p\\n", &x);\n | ^\n0x1004010c0\n\n$ clang t555a.c -std=c11 …Run Code Online (Sandbox Code Playgroud) 有人可以解释一下是否i = x[i]++;会导致未定义的行为吗?
注意:x[i]和i并不都是易失性的且不x[i]重叠i。
有 C11,6.5 表达式,2(添加强调):
\n\n\n如果标量对象上的副作用相对于同一标量对象的不同副作用或使用同一标量对象的值的值计算而言是无序的,则该行为是未定义的。如果表达式的子表达式有多个允许的\n排序,并且在任何排序中出现这种未排序的副作用,则行为是未定义的。84)
\n
我认为:
\n是否有“多重允许的订购”?
\n总体而言:如何i = x[i]++;解释序列点、副作用和未定义的行为(如果有)?
UPD。结论:会i = x[i]++;导致 2 个副作用:
该标准没有定义副作用发生的顺序。
\n因此,根据 C11,4. 一致性,2:
\n\n\n未定义的行为在本国际标准中另外通过文字 \xe2\x80\x98\xe2\x80\x98undefinedbehavior\xe2\x80\x99\xe2\x80\x99 或通过省略任何明确的行为定义来表示。
\n
实验表明,GCC/LLVM/ICC 有序1-2,而 …
在阅读 C11 标准时,我对术语“指向对象的指针”和“指向对象类型的指针”的含义以及在标准中使用这些术语的一致性感到困惑。
根据我的理解:
这种理解正确吗?
现在考虑 C11,6.3.2.3p7(添加了强调):
指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐 68),则行为未定义。否则,当再次转换回来时,结果应等于原始指针。当指向对象的指针转换为指向字符类型的指针时,结果指向该对象的最低寻址字节。
在这里我们看到术语“指向对象类型的指针”和“指向对象的指针”都与相同的动词“转换”一起使用。那么,到底转换了什么:指针的值还是指针本身?
另一个例子:C11,6.5.6p7(强调):
就这些运算符的目的而言,指向不是数组元素的对象的指针的行为与指向长度为 1 的数组的第一个元素的指针相同,且对象的类型作为其元素类型。
考虑 C11,6.5.6p7(添加强调):
int* x[1];
Run Code Online (Sandbox Code Playgroud)
数组到底x包含什么:“指向对象的指针”或“指向对象类型的指针”?
UPD。在尝试使用指向非对象类型的指针时,发现 ICC 和 MSVC 缺乏对约束违规的诊断生成: https: //godbolt.org/z/77Y5snM4K。
我研究 C++ 术语。我无法理解“标点符号”一词。
例如,考虑https://eel.is/c++draft/lex.pptoken(添加了重点):
转换为标记的每个预处理标记应具有关键字、标识符、文字、运算符或标点符号的词汇形式。
这表明运算符和标点符号是不同的实体。那是对的吗?
注意:C 中有标点符号(首先),其中一些标点符号是运算符。
所有标点符号的列表是什么?换句话说:运算符或标点符号的哪些元素是标点符号?
c ×9
terminology ×3
c++ ×2
visual-c++ ×2
arrays ×1
c11 ×1
c23 ×1
clang ×1
constraints ×1
declaration ×1
gcc ×1
icc ×1
pointers ×1
punctuator ×1
semantics ×1
void ×1
x86-64 ×1