更好地理解 c 中可变参数的类型提升
better understanding type promotion of variadic parameters in c
当在 c 中调用可变参数函数时,整数参数被提升为 int,浮点参数被提升为 double
Since the prototype doesn’t specify types for optional arguments, in a call to a variadic function the default argument promotions are performed on the optional argument values. This means the objects of type char
or short int
(whether signed or not) are promoted to either int
or unsigned int
, as appropriate; and that objects of type float
are promoted to type double
. So, if the caller passes a char
as an optional argument, it is promoted to an int
, and the function can access it with va_arg (ap, int)
.
int
类型在32位机器上应该是4字节,在64位机器上应该是8字节,对吗?
所以我想知道当我将 long long int
传递给像 printf 这样具有 %lld
格式的变量参数函数时附加什么。
而且,我再次想知道当我将 long double
变量传递给具有 %Lf
格式的 printf 时附加了什么(无论是在 32 位还是 64 位机器上)。
[已编辑]
在 32 位机器上,我试过这个:
#include <stdio.h>
int main(void)
{
printf("sizeof(int) %d\n", sizeof(int));
printf("sizeof(long int) %d\n", sizeof(long int));
printf("sizeof(long long int) %d\n", sizeof(long long int));
printf("%lld\n", 1LL<<33);
printf("sizeof(float) %d\n", sizeof(float));
printf("sizeof(double) %d\n", sizeof(double));
printf("sizeof(long double) %d\n", sizeof(long double));
return 0;
}
结果是:
sizeof(int) 4
sizeof(long int) 4
sizeof(long long int) 8
8589934592
sizeof(float) 4
sizeof(double) 8
sizeof(long double) 12
这让我觉得并非所有参数都被提升为 int
,否则我将打印 0 而不是 8589934592。
可能只有小于 int
的参数被提升为 int
。类似的东西可能适用于浮点类型。
[已编辑]
在 64 位机器上我 运行 这个:
int main(void)
{
printf("sizeof(int) %lu\n", sizeof(int));
printf("sizeof(long) %lu\n", sizeof(long));
printf("sizeof(long long) %lu\n", sizeof(long long));
return 0;
}
并得到
sizeof(int) 4
sizeof(long) 8
sizeof(long long) 8
如果我理解标准,只有char
和short
被提升为int
。我想知道在较小的架构中会发生什么,例如 16 位或 8 位 MCU。我认为 int
大小取决于体系结构,但我想知道 sizeof(int)
是否可以在 8 位体系结构上为 1。在这种情况下,将 short
提升为 int
是不可能的,除非丢失一些位
So I wonder what append when I pass a long long int
to a variable
argument function like printf with %lld
format. And, again, I wonder
what append when I pass a long double
variable to printf with %Lf
format (no matter if on 32 or 64 bit machines).
没有任何反应,因为 long long int
必须至少与 int
大小相同。即使在大多数 "pesimistic" 的情况下,sizeof(long long int) == sizeof(int)
,参数仍然被传递 "as is"(没有任何提升)。同样适用于 long double
.
"int
类型在 32 位机器上应该是 4 字节,在 64 位机器上应该是 8 字节,对吗?"没有。根据标准,int
s must be at least 16 bits in width (§5.2.4.2.1),但没有进一步规定。
当您将 long long int
传递给 printf()
时,它不受 the integer promotions (§6.3.1.1 2):
的约束
The following may be used in an expression wherever an int or unsigned
int may be used:
- An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to
the rank of int and unsigned int.
- A bit-field of type _Bool, int, signed int, or unsigned int.
If an int can represent all values of the original type (as restricted
by the width, for a bit-field), the value is converted to an int;
otherwise, it is converted to an unsigned int. These are called the
integer promotions.58) All other types are unchanged by the integer
promotions.
如果你将 long double
传递给 printf()
no conversion is made (§6.5.2.2 6):
If the expression that denotes the called function has a type that
does not include a prototype, the integer promotions are performed on
each argument, and arguments that have type float are promoted to
double. These are called the default argument promotions.
与 printf()
语句中的参数对应的转换说明符与这些提升和转换无关,除非如果说明符及其相应参数的类型不匹配,则会出现未定义的行为.
所以,进行整数提升,float
s被转换为double
s,但是"No other conversions are performed implicitly" (§6.5.2.2 8).
解决您对问题的编辑:"this makes me think that not all parameters are promoted to int
."正确。只有具有整数转换等级 "less than or equal to the rank of int
and unsigned int
" 的整数类型才能进行整数提升。对于浮点类型来说更简单; float
晋升为 double
。就这些了。
可能值得指出的是,根据§6.2.5 10,存在三种真正的浮点类型,float
、double
和long double
。 float
可能持有的值是 double
可能持有的值的子集,而 double
又可能是 [=14= 可能持有的值的子集].因此,long double
类型无法升级。
进一步,根据§6.3.1.1 1:
The rank of long long int shall be greater than the rank of long int,
which shall be greater than the rank of int, which shall be greater
than the rank of short int, which shall be greater than the rank of
signed char.
所以 long long int
或 long int
不可能被提升为 int
或 unsigned int
。
至于您最后担心的是,在某些实现中,将 short
提升为 int
可能无法在不丢失一些位的情况下进行,请注意 §6.2.5 8 保证 int
必须能够包含 short
,因为 int
的转换等级必须大于 short
:
For any two integer types with the same signedness and different
integer conversion rank (see 6.3.1.1), the range of values of the type
with smaller integer conversion rank is a subrange of the values of
the other type.
int type should be 4 byte on 32 bit machines and 8 byte on 64 bit machines, is that right?
可能不会。首先,C 不保证 int
的大小,除了声明它 必须 至少为 2 个字节(至少能够保持值 2^16 /2 - 1 = 32767)。 C 中没有什么可以阻止 int
成为 8 个字节,但这不是很方便。
惯例是:所有 8 位和 16 位计算机都使用 16 位 int
。所有其他计算机都使用 32 位 int
。这是行业事实上的标准。任何偏离这个约定的行为都是非常奇特的,尽管理论上 C.
是允许的。
So I wonder what append when I pass a long long int to a variable argument function like printf with %lld format.
你得到一个long long int。没有提升发生,因为变量不是提到的小类型之一。使用的格式说明符与促销规则无关。
this makes me think that not all parameters are promoted to int
确实如此。仅提升标准提及的类型。默认参数提升的正式定义可以在 C11 6.5.2.2/6 中找到:
...the integer promotions are performed on each argument, and
arguments that have type float are promoted to double. These are
called the default argument promotions.
意味着只有小整数类型(参见整数提升规则)和 float
得到提升。没有其他类型。
为什么您认为打印各种字体大小的示例与默认参数提升有任何关联,我不知道。
当在 c 中调用可变参数函数时,整数参数被提升为 int,浮点参数被提升为 double
Since the prototype doesn’t specify types for optional arguments, in a call to a variadic function the default argument promotions are performed on the optional argument values. This means the objects of type
char
orshort int
(whether signed or not) are promoted to eitherint
orunsigned int
, as appropriate; and that objects of typefloat
are promoted to typedouble
. So, if the caller passes achar
as an optional argument, it is promoted to anint
, and the function can access it withva_arg (ap, int)
.
int
类型在32位机器上应该是4字节,在64位机器上应该是8字节,对吗?
所以我想知道当我将 long long int
传递给像 printf 这样具有 %lld
格式的变量参数函数时附加什么。
而且,我再次想知道当我将 long double
变量传递给具有 %Lf
格式的 printf 时附加了什么(无论是在 32 位还是 64 位机器上)。
[已编辑]
在 32 位机器上,我试过这个:
#include <stdio.h>
int main(void)
{
printf("sizeof(int) %d\n", sizeof(int));
printf("sizeof(long int) %d\n", sizeof(long int));
printf("sizeof(long long int) %d\n", sizeof(long long int));
printf("%lld\n", 1LL<<33);
printf("sizeof(float) %d\n", sizeof(float));
printf("sizeof(double) %d\n", sizeof(double));
printf("sizeof(long double) %d\n", sizeof(long double));
return 0;
}
结果是:
sizeof(int) 4
sizeof(long int) 4
sizeof(long long int) 8
8589934592
sizeof(float) 4
sizeof(double) 8
sizeof(long double) 12
这让我觉得并非所有参数都被提升为 int
,否则我将打印 0 而不是 8589934592。
可能只有小于 int
的参数被提升为 int
。类似的东西可能适用于浮点类型。
[已编辑]
在 64 位机器上我 运行 这个:
int main(void)
{
printf("sizeof(int) %lu\n", sizeof(int));
printf("sizeof(long) %lu\n", sizeof(long));
printf("sizeof(long long) %lu\n", sizeof(long long));
return 0;
}
并得到
sizeof(int) 4
sizeof(long) 8
sizeof(long long) 8
如果我理解标准,只有char
和short
被提升为int
。我想知道在较小的架构中会发生什么,例如 16 位或 8 位 MCU。我认为 int
大小取决于体系结构,但我想知道 sizeof(int)
是否可以在 8 位体系结构上为 1。在这种情况下,将 short
提升为 int
是不可能的,除非丢失一些位
So I wonder what append when I pass a
long long int
to a variable argument function like printf with%lld
format. And, again, I wonder what append when I pass along double
variable to printf with%Lf
format (no matter if on 32 or 64 bit machines).
没有任何反应,因为 long long int
必须至少与 int
大小相同。即使在大多数 "pesimistic" 的情况下,sizeof(long long int) == sizeof(int)
,参数仍然被传递 "as is"(没有任何提升)。同样适用于 long double
.
"int
类型在 32 位机器上应该是 4 字节,在 64 位机器上应该是 8 字节,对吗?"没有。根据标准,int
s must be at least 16 bits in width (§5.2.4.2.1),但没有进一步规定。
当您将 long long int
传递给 printf()
时,它不受 the integer promotions (§6.3.1.1 2):
The following may be used in an expression wherever an int or unsigned int may be used:
- An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.
- A bit-field of type _Bool, int, signed int, or unsigned int.
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.58) All other types are unchanged by the integer promotions.
如果你将 long double
传递给 printf()
no conversion is made (§6.5.2.2 6):
If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.
与 printf()
语句中的参数对应的转换说明符与这些提升和转换无关,除非如果说明符及其相应参数的类型不匹配,则会出现未定义的行为.
所以,进行整数提升,float
s被转换为double
s,但是"No other conversions are performed implicitly" (§6.5.2.2 8).
解决您对问题的编辑:"this makes me think that not all parameters are promoted to int
."正确。只有具有整数转换等级 "less than or equal to the rank of int
and unsigned int
" 的整数类型才能进行整数提升。对于浮点类型来说更简单; float
晋升为 double
。就这些了。
可能值得指出的是,根据§6.2.5 10,存在三种真正的浮点类型,float
、double
和long double
。 float
可能持有的值是 double
可能持有的值的子集,而 double
又可能是 [=14= 可能持有的值的子集].因此,long double
类型无法升级。
进一步,根据§6.3.1.1 1:
The rank of long long int shall be greater than the rank of long int, which shall be greater than the rank of int, which shall be greater than the rank of short int, which shall be greater than the rank of signed char.
所以 long long int
或 long int
不可能被提升为 int
或 unsigned int
。
至于您最后担心的是,在某些实现中,将 short
提升为 int
可能无法在不丢失一些位的情况下进行,请注意 §6.2.5 8 保证 int
必须能够包含 short
,因为 int
的转换等级必须大于 short
:
For any two integer types with the same signedness and different integer conversion rank (see 6.3.1.1), the range of values of the type with smaller integer conversion rank is a subrange of the values of the other type.
int type should be 4 byte on 32 bit machines and 8 byte on 64 bit machines, is that right?
可能不会。首先,C 不保证 int
的大小,除了声明它 必须 至少为 2 个字节(至少能够保持值 2^16 /2 - 1 = 32767)。 C 中没有什么可以阻止 int
成为 8 个字节,但这不是很方便。
惯例是:所有 8 位和 16 位计算机都使用 16 位 int
。所有其他计算机都使用 32 位 int
。这是行业事实上的标准。任何偏离这个约定的行为都是非常奇特的,尽管理论上 C.
So I wonder what append when I pass a long long int to a variable argument function like printf with %lld format.
你得到一个long long int。没有提升发生,因为变量不是提到的小类型之一。使用的格式说明符与促销规则无关。
this makes me think that not all parameters are promoted to int
确实如此。仅提升标准提及的类型。默认参数提升的正式定义可以在 C11 6.5.2.2/6 中找到:
...the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.
意味着只有小整数类型(参见整数提升规则)和 float
得到提升。没有其他类型。
为什么您认为打印各种字体大小的示例与默认参数提升有任何关联,我不知道。