gcc -std=c23
最新的 gcc 13.x (trunk) 给出了此代码的编译器错误 ( ):
int* p = false;\n
Run Code Online (Sandbox Code Playgroud)\n\n\n错误:
\nint *
使用类型“ ”初始化类型“_Bool
”时出现类型不兼容
这怎么可能是正确的呢?
\nC23 6.2.5 \xc2\xa78-9(类型 - 整数类型的定义):
\n\n\n\n
bool
与标准有符号整数类型相对应的类型和无符号整数类型是标准无符号整数类型。标准有符号整数类型和标准无符号整数类型统称为标准整数类型;
\n
C23 6.6 \xc2\xa78 (常量表达式 - 整型常量表达式的定义):
\n\n\n整数常量表达式应具有整数类型...
\n
C23 6.3.2.3(指针-空指针常量的定义)
\n\n\n值为 0 的整型常量表达式,此类表达式转换为 类型
\nvoid *
,或预定义常量nullptr
称为空指针常量
C23 6.5.16.1(简单分配):
\n\n约束
\n/--/ …
C23 在函数声明符中引入了新的语义:
\n\n\n6.7.6.3 函数声明符
\n[...]
\n13\xc2\xa0\xc2\xa0\xc2\xa0对于没有参数类型列表的函数声明符:效果就像是使用由关键字 组成的参数类型列表进行声明
\nvoid
。函数声明符提供函数的原型。
这似乎意味着具有空参数列表的函数定义可以等效地用()
or编写(void)
。
然而,该函数似乎并不能保证这种main
等价性:
\n\n5.1.2.2.1 程序启动
\n程序启动时调用的函数名为
\nmain
。该实现没有声明该函数的原型。它的返回类型应为int
且不带参数定义:Run Code Online (Sandbox Code Playgroud)\nint main(void) { /* ... */ }\n
或带有两个参数(此处称为
\nargc
和argv
,但可以使用任何名称,因为它们对于声明它们的函数来说是本地的):Run Code Online (Sandbox Code Playgroud)\nint main(int argc, char *argv[]) { /* ... */ }\n
或等效的或以某种其他实现定义的方式。
\n
这似乎并不能保证 是 的int main() { /* ... */ }
有效定义main
,或者等效定义是否涵盖此变体?
C23 标准显然引入了使用“auto”关键字进行自动类型推导,请参阅此处,就像 C++11 中一样。然而,似乎存在一些差异。
根据here,https://en.cppreference.com/w/cpp/keyword/auto,在C++11之后,不再auto
是C++中的存储持续时间说明符。
然而,我无法轻易找到 C23 的等效声明。在C23中是否仍然auto
是C中的存储类说明符?
还可以int auto x = 1;
用C23写吗?
编辑:第一个问题的答案是肯定的。但正如 Andrew Henle 在下面的评论中指出的,第二个问题是不同的:
还可以float auto x = 1;
用C23写吗?
正如 @AndrewHenle 和 @VladfromMoscow 在标准文档中引用的那样,6.7.1 存储类说明符,第 4 段
auto 仅应出现在具有文件范围的标识符的声明说明符中,或者如果要从初始值设定项推断类型,则应与其他存储类说明符一起出现。
float auto x = 1;
如果此声明不在文件范围内,则这似乎不涵盖这种情况。
这句话的解释是什么?
还有一个问题:这句话看起来很混乱,因为我们肯定可以使用 auto 而无需“其他存储说明符”,不是吗?喜欢auto a = 1;
。
即将推出的 C23 标准中提供了新的预处理器指令:#embed
这是一个简单的例子:
// Placing a small image resource.
#include <stddef.h>
void show_icon(const unsigned char *, size_t);
int main (int, char*[]) {
static const unsigned char icon_data[] = {
#embed "black_sheep.ico"
};
show_icon(icon_data, sizeof(icon_data));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是一个更详细的方法,从二进制数据初始化非数组(无论这意味着什么):
int main() {
/* Braces may be kept or elided as per normal initialization rules */
int i = {
#embed "i.dat"
}; /* i value is [0, 2^(embed element width)) first entry */
int i2 =
#embed "i.dat"
; …
Run Code Online (Sandbox Code Playgroud) 考虑这个联盟:
\ntypedef union\n{\n void* vptr;\n nullptr_t nptr;\n} pun_intended;\n
Run Code Online (Sandbox Code Playgroud)\nnullptr_t
据说与void*
1)兼容。好吧,如果我们将 初始化void*
为某个非零值怎么办?
pun_intended foo = { .vptr = (void*)42 }; \n
Run Code Online (Sandbox Code Playgroud)\nnullptr_t
引入之前是合法的。完整示例:
\n#include <stdio.h>\n#include <inttypes.h>\n#include <stddef.h>\n\ntypedef union\n{\n void* vptr;\n nullptr_t nptr; \n} pun_intended;\n\nint main(void)\n{\n pun_intended foo = { .vptr = (void*)42 };\n printf("Value: %" PRIuPTR "\\n", (uintptr_t)foo.vptr);\n\n if(foo.nptr != (void*)42)\n {\n puts("It …
Run Code Online (Sandbox Code Playgroud) 在C23中,nullptr
关键字得到了标准化。我也更愿意使用C23 之前nullptr
的版本NULL
,因为这意味着我可以编写可编译的代码:
我可以简单地NULL
在两种语言和每个 C 标准中使用,但这在 C++ 代码中是非常不寻常的,并且nullptr
无论如何正在成为两种语言的规范。
#define
据我所知,语言中不允许使用替换关键字,这可能会在定义兼容性宏时引起问题。基本上,我需要:
// only if this is neither C++ nor C23
#define nullptr /* something */
Run Code Online (Sandbox Code Playgroud)
我怎样才能正确定义这个宏?
C23 引入了属性[[reproducible]]
和[[unsequenced]]
。
在这里回答问题时,我做了以下例子:
#include <stdio.h>
#include <math.h>
int main (void)
{
float_t a = -248.75;
printf("%f\n", a);
unsigned char* ptr = (unsigned char*)&a;
for(size_t i=0; i<sizeof(a); i++)
{
printf("%.2X ", ptr[i]);
}
}
Run Code Online (Sandbox Code Playgroud)
在版本 13 之前的 gcc 以及 clang 的所有版本上,这给出了预期的输出(x86 小端):
-248.750000
00 C0 78 C3
Run Code Online (Sandbox Code Playgroud)
然而,当使用 gcc 13.1 编译时,我得到无意义的输出:
-248.750000
00 00 00 00
Run Code Online (Sandbox Code Playgroud)
进一步检查发现,罪魁祸首就是这个选项-std=c2x
。如果我删除它,程序将按预期运行。
使用的编译器选项:-std=c2x -pedantic-errors -Wall -Wextra -O3 -lm
. https://godbolt.org/z/4qbo74eEW
这是 gcc 中的已知错误吗?
新标准C(ISO/IEC 9899:2023,又名C23),
在附录H中,定义了几个宏和类型,
相对于实数和复数算术,
并符合 ISO/IEC 60559 标准(浮点算术)。
根据 H.1 小节,存在三个
实现定义的宏来测试
与附件 H 定义的一致性。
__STDC_IEC_60559_TYPES__
__STDC_IEC_60559_BFP__
__STDC_IEC_60559_DFP__
Run Code Online (Sandbox Code Playgroud)
此外,要“激活”关联的宏和类型,
用户必须定义宏:
__STDC_WANT_IEC_60559_TYPES_EXT__
Run Code Online (Sandbox Code Playgroud)
该标准的措辞在几个细节上很明确:
__STDC_IEC_60559_BFP__
是由编译器定义的,则意味着_FloatN
,_FloatN_t
和其他_FloatN*
类型也已定义。__STDC_IEC_60559_DFP__
是由编译器定义的,则意味着_DecimalN
,_DecimalN_t
和其他_DecimalN*
类型也已定义。__STDC_IEC_60559_TYPES__
无论哪种情况,都会定义宏。__STDC_IEC_60559_TYPES__
一个宏__STDC_IEC_60559_BFP__
或(或两者)。__STDC_IEC_60559_DFP__
在 H.11 小节中,类型long_double_t
被添加到 中<math.h>
。
long_double_t
但是,我无法推断在哪些条件下可以确保给定实现中的存在。
问题:
如果__STDC_IEC_60559_TYPES__
已定义,是否足以确定long_double_t
也在 中定义<math.h>
?
我的怀疑来自以下可能出现的特殊情况:
__STDC_IEC_60559_TYPES__
和__STDC_IEC_60559_DFP__
已定义,但__STDC_IEC_60559_BFP__
未定义。在本例中,定义了十进制类型。
但是,long_double_t
不是十进制类型。
子问题:
宏是否 …
我目前正在阅读 C23 标准的新草案(N3088),并且我注意到在 \xc2\xa75.2.4.1 中该标准指出:
\n\n\n该实现应能够翻译和执行使用但不超过\n这些结构和实体的以下限制的程序:...对象中的 32767 字节(仅在托管环境中)
\n
这是 ANSI C/C89 中的数字,自 C99 起它被提高到 65535 - 为何将其降低回原来的数量?
\n我试图在此处和WG 14 文档日志中查找任何对此的引用,但没有找到任何内容。
\nc ×10
c23 ×10
gcc ×2
attributes ×1
auto ×1
compiler-bug ×1
declaration ×1
null-pointer ×1
nullptr ×1
standards ×1
type-punning ×1