从实现printf
的人的角度来看这个问题。
由于printf
的自变量是通过省略号(...
)传递的,因此它们被提升为整数。我知道char
、short
和int
被提升为int
,而long long
没有被提升。与unsigned
类似。
这意味着在读取变容时,va_arg(args, int)
应用于char
、short
和int
,而va_arg(args, long long)
应用于CCD15。
我的问题是,long
和size_t
会升职吗?如果他们升职了,升职到什么程度?互联网上有很多关于整数促销的消息来源,但我没有看到任何关于这些类型的消息。
附言:如果能参考一下这个标准,我将不胜感激。
要求long
的整数转换秩大于int
的秩(6.3.1.1p1),因此即使long
具有与int
相同的表示(和精度),也需要va_arg(args, long)
。请注意,在大多数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
的精度小于int
的精度)或unsigned int
(如果两种类型具有相同的精度)。在任何一种情况下,都需要编写va_arg(args, unsigned int)
(即使size_t
参数被提升为int
,7.16.1.1p2也允许使用等效的无符号类型) - 与
int
相同,即size_t
与unsigned int
为同一类型。在这种情况下,CCD_ 40或CCD_ - 大于CCD_ 42。在这种情况下,必须使用
va_arg(args, size_t)
注意,即使size_t
的精度与int
的精度相同,1和3中的任何一个都可以获得。
这意味着,要使用va_arg
提取size_t
参数,需要知道或推断size_t
的整数转换秩。这可以使用类型通用宏(6.5.1.1)来完成:
#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
,或者是unsigned int
的typedef,则我们提取unsigned int
;如果它没有被提升,并且是与unsigned int
不同的类型,那么我们会碰到default
块。我们不能提供size_t
本身作为一个选项,因为如果size_t
是unsigned int
的typedef,这将发生冲突。
请注意,这是一个不限于size_t
的问题,ptrdiff_t
和wchar_t
具有相同的问题(对于后者,wint_t
可以保持任何wchar_t
值,并且不受提升,但不能保证wchar_t
被提升为wint_t
,这与保证char
被提升为int
不同)。我建议该标准需要引入新的类型spromo_t
、ppromo_t
和wpromo_t
,类似地,也需要引入stdint.h
中的类型。(当然,你可以如上所述使用_Generic
,但这会让人头疼。)
C says(emphasis mine):
(C99,6.3.1.1p2)"在可以使用int或无符号int的表达式中,可以使用以下内容:
--整数类型的对象或表达式,其整数转换秩小于大于或等于int和无符号int的秩。
--类型为_Bool、int、有符号int或无符号int的位字段。
如果int可以表示原始类型的所有值,则该值将转换为int;否则,它将转换为无符号int。这些被称为整数提升。48)所有其他类型都不会因整数提升而改变">
类型为long
的参数将不会升级。与size_t
相关的整数促销可以总结如下:
- 如果
size_t
在int
的范围内,则升级为int
- 如果
size_t
在unsigned int
的范围内,则升级为unsigned int
- 否则,
size_t
具有比unsigned int
更大的转换等级(因此宽度),并且不会发生升级
琐碎的情况是,如果size_t
是unsigned int
的别名或宽度大于CCD_86。在这些情况下,不会发生升级,您可以使用size_t
来读取可变参数。
边缘情况是
size_t
在int
的范围内size_t
不在int
的范围内,但在unsigned int
的范围内而实际上不是unsigned int
如果int
包含填充,则后者可能发生,如果size_t
是与unsigned int
宽度相同的扩展整数类型。
假设整数表示不包含填充位,则可以通过读取unsigned int
变元参数来覆盖这两种边缘情况,该参数将适用于C语言的所有合理实现,尽管可能存在未定义的行为。