从实施printf
的人的角度来看待这个问题。
由于printf
的参数是通过省略号(...
)传递的,因此它们会被提升整数。我知道char
,short
和int
会升级为int
,而long long
则不会升级。同样适用于他们的unsigned
同行。
这意味着在阅读varargs时,va_arg(args, int)
应该用于char
,short
和int
,va_arg(args, long long)
应该用于long long
}}
我的问题是,long
和size_t
是否得到晋升,如果有,我们会做什么?互联网上有很多关于整数提升的消息来源,但我还没有看到有关这些类型的讨论。
P.S。我很感激参考标准。
答案 0 :(得分:2)
C说(强调我的):
(C99,6.3.1.1p2)“可以在任何可以使用int或unsigned int的表达式中使用以下内容:
- 具有整数转换等级较小的整数类型的对象或表达式 大于或等于int和unsigned int的等级。
- _Bool,int,signed int或unsigned int。
类型的位字段如果int可以表示原始类型的所有值,则该值将转换为int;否则,它将转换为unsigned int。这些被称为整数促销.48)整数促销不会改变所有其他类型。“
答案 1 :(得分:2)
long
的整数转换等级必须大于int
(6.3.1.1p1)的等级,因此{em}需要va_arg(args, long)
,即使 long
与int
具有相同的表示(和精度)。请注意,在大多数64位平台上,long
是64位; Windows(LLP64平台)是一个例外。
size_t
必须是无符号整数类型(6.5.3.4p5,7.19p2),建议整数转换等级不大于long int
(7.19p4);它需要具有至少16位的精度(7.20.3p2,最小值SIZE_MAX
)。它不需要是(typedef到a)标准整数类型,但允许它是。
整数转换等级有size_t
:
int
,因此size_t
参数将被提升为int
(如果size_t
的精度小于{{1}的精度1}})或int
(如果两种类型具有相同的精度)。在任何一种情况下,您都需要编写unsigned int
(即使va_arg(args, unsigned int)
参数被提升为size_t
,7.16.1.1p2允许使用等效的无符号类型。int
的相同,即int
与size_t
的类型相同。在这种情况下,允许unsigned int
或va_arg(args, unsigned int)
。va_arg(args, size_t)
。在这种情况下,必须使用int
。请注意,即使va_arg(args, size_t)
的精度与size_t
的精度相同,也可以获得1和3中的任何一个。
这意味着要使用int
提取size_t
参数,有必要知道或推断va_arg
的整数转换等级。这可以使用类型通用宏(6.5.1.1)来完成:
size_t
如果#define va_arg_size_t(args) _Generic((+(sizeof(0))), \
int: (size_t) va_arg((args), unsigned int), \
unsigned int: (size_t) va_arg((args), unsigned int), \
default: va_arg((args), size_t))
被上面使用的一元加运算符提升为size_t
,那么我们会提取int
;如果unsigned int
被提升为size_t
,或者是unsigned int
的typedef,那么我们会提取unsigned int
;如果它未被提升且与unsigned int
不同,那么我们会点击unsigned int
块。我们无法提供default
本身作为选项,因为size_t
是size_t
的typedef会产生冲突。
请注意,这是一个不受unsigned int
限制的问题,size_t
和ptrdiff_t
有同样的问题(对于后者,wchar_t
可以容纳任何wint_t
}值并且不受推广,但无法保证将wchar_t
提升为wchar_t
,这与wint_t
被提升为{{的保证不同1}})。我建议标准需要引入新类型char
,int
和spromo_t
,同样适用于ppromo_t
中的类型。 (当然,你可以使用上面的wpromo_t
,但这是一个痛苦的问题。)
答案 2 :(得分:1)
类型long
的参数不会被提升。与size_t
相关的整数促销可归纳如下:
size_t
的范围为int
,则提升为int
size_t
的范围为unsigned int
,则提升为unsigned int
size_t
的转化率(以及宽度)高于unsigned int
,且不会促销。如果size_t
是unsigned int
的别名或宽度大于size_t
,则表示非常简单。在这些情况下,不会发生促销,您可以使用size_t
来读取可变参数。
边缘情况是
int
的范围为size_t
int
不在unsigned int
范围内,但在unsigned int
范围内,实际上不是int
如果size_t
包含填充,unsigned int
是一个与unsigned int
宽度相同的扩展整数类型,则会发生后者。
假设整数表示不包含填充位,您可以通过读取{{1}}可变参数来覆盖两个边缘情况,这将适用于C语言的所有合理实现,尽管可能是未定义的行为。