【问题标题】:better understanding type promotion of variadic parameters in c更好地理解 c 中可变参数的类型提升
【发布时间】:2017-11-09 07:22:25
【问题描述】:

当在 c 中调用可变参数函数时,整数参数被提升为 int,浮点参数被提升为 double

由于原型没有为可选参数指定类型,因此在调用可变参数函数时,默认参数提升是在可选参数值上执行的。这意味着 charshort int 类型的对象(无论是否已签名)将酌情提升为 intunsigned int;并且float 类型的对象被提升为double 类型。因此,如果调用者将char 作为可选参数传递,则将其提升为int,并且该函数可以使用va_arg (ap, int) 访问它。

int 类型在 32 位机器上应该是 4 字节,在 64 位机器上应该是 8 字节,对吗?
所以我想知道当我将long long int 传递给像 printf 这样具有%lld 格式的变量参数函数时会附加什么。
而且,我想知道当我以%Lf 格式将long double 变量传递给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

如果我理解标准,只有charshort 被提升为int。我想知道在较小的架构中会发生什么,例如 16 位或 8 位 MCU。我认为int 的大小取决于架构,但我想知道sizeof(int) 是否可以在 8 位架构上为 1。在这种情况下,将short 升级为int 可能是不可能的,除非丢失一些位

【问题讨论】:

  • int 必须至少 16 位,但标准不作进一步保证。
  • long long 每个 C99 必须至少为 64 位
  • An int must be able to contain a short。此外,该标准指定int must be at least 16 bits,无论底层架构如何。 _Bool 也被提升为 int
  • 添加--short 完全有可能与int 大小相同,或者intlong 大小相同。也可能有 16 位 chars,在这种情况下,也可能有 16 位 ints 和 sizeof (int) == 1。这些是实现细节,不依赖于架构。
  • 由于size_t 使用了错误的格式说明符,您的程序都有未定义的行为。 (应该是%zu

标签: c floating-point int printf c99


【解决方案1】:

"int 类型在 32 位机器上应该是 4 字节,在 64 位机器上应该是 8 字节,对吗?"没有。根据标准,ints 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) 的约束:

以下内容可以用在表达式中,只要是 int 或 unsigned 可以使用 int:

  • 具有整数类型(int 或 unsigned int 除外)的对象或表达式,其整数转换等级小于或等于 int 和 unsigned int 的等级。
  • _Bool、int、signed int 或 unsigned int 类型的位域。

如果一个 int 可以表示原始类型的所有值(受限制 通过宽度,对于一个位域),该值被转换为一个 int; 否则,它将转换为无符号整数。这些被称为 整数promotions.58) 所有其他类型都被整数不变 促销

如果您将long double 传递给printf() no conversion is made (§6.5.2.2 6)

如果表示被调用函数的表达式有一个类型 不包括原型,整数提升在 每个参数和具有浮点类型的参数都被提升为 双倍的。这些称为默认参数提升。

printf() 语句中的参数对应的转换说明符与这些提升和转换无关,除非说明符及其对应参数的类型不匹配时会出现未定义的行为。

因此,整数提升被执行,floats 被转换为doubles,但"No other conversions are performed implicitly" (§6.5.2.2 8)

解决您对问题的编辑:“这让我认为并非所有参数都提升为int。”正确的。只有整数转换等级“小于或等于intunsigned int”的整数类型才能进行整数提升。浮点类型更简单; floats 升级为 double。就是这样。

可能值得指出的是,根据§6.2.5 10,存在三种真正的浮点类型,floatdoublelong doublefloat 可能持有的值是double 可能持有的值的子集,而double 可能持有的值又是long double 可能持有的值的子集。因此,long double 类型没有提升的可能性。

另外,根据§6.3.1.1 1

long long int 的秩要大于 long int 的秩, 应大于 int 的等级,应大于 比short int 的秩,它应该大于的秩 签名字符。

因此,long long intlong int 不可能升级为 intunsigned int

至于您最后担心的是将short 提升为int 可能在某些实现中不可能不丢失一些位,请注意§6.2.5 8 保证int 必须能够包含short,因为int的转化排名必须大于short的转化排名:

对于任意两个具有相同符号和不同的整数类型 整数转换等级(见 6.3.1.1),类型的取值范围 具有较小整数转换等级的值的子范围 其他类型。

【讨论】:

  • 我仅以 printf 为例。我想知道,如果我写我的可变函数,我传递给它的都是转换为 int 还是更小?我编辑了原始帖子来解释
  • @mastupristi-- 任何可变参数函数的提升规则都是相同的。
  • @David-- 浮点类型应该是相同的:float 被提升为double,并且long double 没有转换。我添加了一些其他的东西
  • @mastupristi-- 我错过了什么?这不就是我说的:“floats 被提升为double。仅此而已。”或者,以前,“执行整数提升,floats 转换为 doubles,但‘没有隐式执行其他转换’。”
  • 次要:“如果您使用 %Lf 转换说明符将 long double 传递给 printf(),则不会进行转换:” --> 不需要“带有 %Lf 转换说明符”。我指出这一点,因为学习者有时会错误地认为格式字符串会以某种方式影响传递的参数。
【解决方案2】:

int 类型在 32 位机器上应该是 4 字节,在 64 位机器上应该是 8 字节,对吗?

可能不会。首先,C 不保证 int 的大小,除了声明它必须至少为 2 个字节(至少能够保持值 2^16/2 - 1 = 32767)。 C 中没有任何东西可以阻止 int 为 8 个字节,但这不是很方便。

约定是:所有 8 位和 16 位计算机都使用 16 位 int。所有其他计算机使用 32 位 int。这是行业事实上的标准。尽管理论上 C 允许,但任何与此约定的偏差都将是非常奇特的。


所以我想知道当我将 long long int 传递给具有 %lld 格式的变量参数函数(如 printf)时会追加什么。

你得到一个 long long int。没有提升发生,因为变量不是提到的小类型之一。使用的格式说明符与提升规则无关。


这让我觉得并非所有参数都提升为 int

确实如此。仅提升标准提及的类型。默认参数提升的正式定义可以在 C11 6.5.2.2/6 中找到:

...对每个参数执行整数提升,并且 具有浮点类型的参数被提升为双精度。这些是 称为默认参数promotions。

意味着只有小整数类型(参见整数提升规则)和float 被提升。没有其他类型。


为什么您认为打印各种字体大小的示例与默认参数提升有任何相关性,我不知道。

【讨论】:

  • UV 表示“使用的格式说明符与促销规则无关。”
【解决方案3】:

所以我想知道当我将 long long int 传递给变量时会附加什么 像 printf 这样的参数函数,格式为%lld。而且,我又想知道 当我使用%Lflong double 变量传递给printf 时会附加什么@ 格式(无论是在 32 位还是 64 位机器上)。

什么都不会发生,因为long long int 必须至少与int 大小相同。即使在最“悲观”的情况下,sizeof(long long int) == sizeof(int),参数仍然“按原样”传递(没有任何提升)。这同样适用于long double

【讨论】:

    猜你喜欢
    • 2011-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 2011-12-18
    • 1970-01-01
    • 2013-03-14
    相关资源
    最近更新 更多