【发布时间】:2018-06-09 01:16:33
【问题描述】:
7.16.1.1 2 描述va_arg 如下(强调我的):
如果没有实际的下一个参数,或者 如果 type 与实际下一个参数的类型不兼容(根据提升 到默认参数promotions),行为是未定义的,除了以下 案例:
- 一种是有符号整数类型,另一种是对应的无符号整数 类型,并且值可以在两种类型中表示;
- 一种类型是指向 void 的指针,另一种是指向字符类型的指针。
现在据我了解,6.5.2.2(函数调用)似乎与我不矛盾,虽然我可能错了,但默认促销是:
-
char到int或unsigned(指定实现) -
signed char到int -
unsigned char到unsigned -
short到int -
unsigned short到unsigned -
float到double
当您知道传递给va_list 的确切底层类型时,这一切都很好而且很花哨(char 除外,因为它的签名是实现指定的,所以 AFAIK 无法便携检索)。
当您期望将来自 <stdint.h> 的类型传递给您的 va_list 时,情况会变得更加复杂。
-
int8_t和int16_t,通过逻辑极限观察推断,保证被提升或已经是int类型。然而,依靠我的原始“逻辑”极限观察是非常可疑的,所以我正在寻求你(和标准)对这个推论的确认(我可能会遗漏一些我什至没有的极端案例知道)。 - 同样适用于
uint8_t和uint16_t,除了基础类型是unsigned -
int32_t可能会或可能不会晋升为int。它可能大于、小于或与int完全相同。uint32_t也一样,但unsigned也一样。 如何可移植地检索传递给va_list的int32_t和uint32_t?也就是说,如何判断int32_t(uint32_t)是否已经提升为int(unsigned)? 换句话说,如何确定我应该使用va_arg(va, int)还是va_arg(va, int32_t)来检索传递给可变参数函数的int32_t,而不在任何平台上调用未定义的行为? - 我相信同样的问题也适用于
int64_t和uint64_t。
这是一个理论(仅涉及标准)问题,假设<stdint.h> 中的所有精确宽度类型都存在。我对“实践中的真实情况”类型的答案不感兴趣,因为我相信我已经知道它们。
编辑
我想到的一个想法是使用_Generic 来确定@987654365@ 的基础类型。我不确定您将如何使用它。我正在寻找更好(更简单)的解决方案。
【问题讨论】:
-
int32_t小于int的可能性很小。这将是一个相当奇怪的实现,因为 64 位int意味着short是 16 位或 32 位,而其他宽度没有对应的“本机”类型。跨度> -
@DevSolar:确实存在拜占庭式架构,尤其是在嵌入式领域。尽管如此,一个不将 int32_t 提升为 int_fast32_t 的系统会特别奇怪。就个人而言,除非有明显的解决方案,否则我不会打扰并满足于断言 PRIu32。
-
一个不那么疯狂的解决方案是使用
autoconf来发现编译器行为和一两个宏。 -
促销活动不尊重签名。根据 6.3.1.2 2,“如果 int 可以表示原始类型的所有值(受宽度限制,对于位域),则该值将转换为 int;否则,它将被转换为 unsigned int。”所以
uint8_t会变成int,而不是unsigned int。 -
unsigned char和unsigned short如果可以表示源类型的所有值,则提升为int,否则提升为unsigned int。
标签: c type-conversion language-lawyer variadic-functions integer-promotion