c-通过省略号发送时"long"和"size_t"的整数提升



从实现printf的人的角度来看这个问题。

由于printf的自变量是通过省略号(...)传递的,因此它们被提升为整数。我知道charshortint被提升为int,而long long没有被提升。与unsigned类似。

这意味着在读取变容时,va_arg(args, int)应用于charshortint,而va_arg(args, long long)应用于CCD15。

我的问题是,longsize_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的整数转换秩有三种可能性:

  1. 它小于int的精度,因此size_t参数将提升为int(如果size_t的精度小于int的精度)或unsigned int(如果两种类型具有相同的精度)。在任何一种情况下,都需要编写va_arg(args, unsigned int)(即使size_t参数被提升为int,7.16.1.1p2也允许使用等效的无符号类型)
  2. int相同,即size_tunsigned int为同一类型。在这种情况下,CCD_ 40或CCD_
  3. 大于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_tunsigned int的typedef,这将发生冲突。

请注意,这是一个不限于size_t的问题,ptrdiff_twchar_t具有相同的问题(对于后者,wint_t可以保持任何wchar_t值,并且不受提升,但不能保证wchar_t提升为wint_t,这与保证char被提升为int不同)。我建议该标准需要引入新的类型spromo_tppromo_twpromo_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相关的整数促销可以总结如下:

  1. 如果size_tint的范围内,则升级为int
  2. 如果size_tunsigned int的范围内,则升级为unsigned int
  3. 否则,size_t具有比unsigned int更大的转换等级(因此宽度),并且不会发生升级

琐碎的情况是,如果size_tunsigned int的别名或宽度大于CCD_86。在这些情况下,不会发生升级,您可以使用size_t来读取可变参数。

边缘情况是

  • size_tint的范围内
  • size_t不在int的范围内,但在unsigned int的范围内而实际上不是unsigned int

如果int包含填充,则后者可能发生,如果size_t是与unsigned int宽度相同的扩展整数类型。

假设整数表示不包含填充位,则可以通过读取unsigned int变元参数来覆盖这两种边缘情况,该参数将适用于C语言的所有合理实现,尽管可能存在未定义的行为。

最新更新