可变函数和常量 [英] Variadic functions and constants

查看:25
本文介绍了可变函数和常量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

可变参数函数究竟如何处理数字常量?例如考虑以下代码:

How exactly do variadic functions treat numeric constants? e.g. consider the following code:

myfunc(5, 0, 1, 2, 3, 4);

函数如下所示:

void myfunc(int count, ...)
{
}

现在,为了使用 va_arg 迭代单个参数,我需要知道它们的大小,例如intshortcharfloat 等.但是对于我使用的数字常量,我应该假设什么大小在上面的代码中?

Now, in order to iterate over the single arguments with va_arg, I need to know their sizes, e.g. int, short, char, float, etc. But what size should I assume for numeric constants like I use in the code above?

测试表明,仅仅假设它们的 int 似乎工作正常,因此编译器似乎将它们推送为 int 即使这些常量也可以用单个表示charshort 每个.

Tests have shown that just assuming int for them seems to work fine so the compiler seems to push them as int even though these constants could also be represented in a single char or short each.

尽管如此,我正在寻找对我所看到的行为的解释.C 中用于将数字常量传递给可变参数函数的标准类型是什么?这是明确定义的还是依赖于编译器?32 位和 64 位架构有区别吗?

Nevertheless, I'm looking for an explanation for the behaviour I see. What is the standard type in C for passing numeric constants to variadic functions? Is this clearly defined or is it compiler-dependent? Is there a difference between 32-bit and 64-bit architecture?

谢谢!

推荐答案

我喜欢 Jonathan Leffler 的回答,但我认为对于那些打算编写可移植库或提供具有可变参数函数的 API 并因此需要深入研究细节的人,我会提供一些技术细节.

I like Jonathan Leffler's answer, but I thought I'd pipe up with some technical details, for those who intend to write a portable library or something providing an API with variadic functions, and thus need to delve in to the details.

可变参数受默认参数提升(C11 草案 N1570 为 PDF;第 6.5.2.2 节函数调用,第 6 段):

Variadic parameters are subject to default argument promotions (C11 draft N1570 as PDF; section 6.5.2.2 Function calls, paragraph 6):

.. 对每个参数执行整数提升,并且参数有类型 float 被提升为 double.这些被称为默认参数提升.

.. 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.

[If] .. 提升后的参数类型与提升后的参数类型不兼容,行为未定义,以下情况除外:

[If] .. the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases:

  • 一个提升类型是有符号整数类型,另一个提升类型是对应的无符号整数类型,值在两种类型中都可以表示;

  • one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types;

这两种类型都是指向字符类型或 void 的限定或非限定版本的指针

both types are pointers to qualified or unqualified versions of a character type or void

浮点常量属于 double 类型,除非它们以 fF 为后缀(如 1.0f),在这种情况下,它们属于 float 类型.

Floating-point constants are of type double, unless they are suffixed with f or F (as in 1.0f), in which case they are of type float.

在 C99 和 C11 中,如果整数常量合二为一,则它们属于 int 类型;long (AKA long int) 如果它们合二为一;long long (AKA long long int) 否则.由于许多编译器认为没有大小后缀的整数常量是人为错误或拼写错误,如果整数常量不是 int 类型,最好始终包含后缀.

In C99 and C11, integer constants are of type int if they fit in one; long (AKA long int) if they fit in one otherwise; of long long (AKA long long int) otherwise. Since many compilers assume an integer constant without a size suffix is a human error or typo, it is a good practice to always include the suffix if the integer constant is not of type int.

整数常量也可以有一个字母后缀来表示它们的类型:

Integer constants can also have a letter suffix to denote their type:

  • uU 用于 unsigned int

lL 表示 long int

lu or ul or LU or UL or lULuuLUl 用于 unsigned long int

lu or ul or LU or UL or lU or Lu or uL or Ul for unsigned long int

ll or LL or Ll or lL for long long int代码>

ll or LL or Ll or lL for long long int

lluLLU(或 ULL 或其任何大写或小写变体)用于 unsigned long long int

llu or LLU (or ULL or any of their uppercase or lowercase variants) for unsigned long long int

整体推广规则在第 6.3.1.1 节.

The integer promotion rules are in section 6.3.1.1.

总结 C11 的默认参数提升规则(与 C89 和 C99 相比有一些添加,但没有显着变化):

To summarize the default argument promotion rules for C11 (there are some additions compared to C89 and C99, but no significant changes):

  • float 被提升为 double

其值可以由 int 表示的所有整数类型都被提升为 int.(这包括无符号和有符号的 charshort,以及 _Boolintint 类型的位域较小的 unsigned int 位域.)

All integer types whose values can be represented by an int are promoted to int. (This includes both unsigned and signed char and short, and bit-fields of types _Bool, int, and smaller unsigned int bit-fields.)

其值可以由 unsigned int(但不是 int)表示的所有整数类型都被提升为 unsigned int.(这包括不能由 int 表示的 unsigned int 位字段(换句话说,CHAR_BIT * sizeof (unsigned int) 位), 以及 unsigned int 的 typedef 别名,但我认为仅此而已.)

All integer types whose values can be represented by an unsigned int (but not an int) are promoted to unsigned int. (This includes unsigned int bit fields that cannot be represented by an int (of CHAR_BIT * sizeof (unsigned int) bits, in other words), and typedef'd aliases of unsigned int, but that's it, I think.)

至少与 int 一样大的整数类型保持不变.这包括类型 long/long intlong long/long long intsize_t,例如.

Integer types at least as large as int are unchanged. This includes types long/long int, long long/long long int, and size_t, for example.

我想指出的规则中有一个问题":签名到未签名是可以的,未签名到签名是不确定的":

There is one 'gotcha' in the rules that I'd like to point out: "signed to unsigned is okay, unsigned to signed is iffy":

  • 如果参数被提升为有符号整数类型,但函数使用相应的无符号整数类型获取值,则函数使用模运算获取正确值.

  • If the argument is promoted to a signed integer type, but the function obtains the value using the corresponding unsigned integer type, the function obtains the correct value using modulo arithmetic.

也就是说,负值就像它们增加了(1 + 无符号整数类型中的最大可表示值),使它们成为正值.

That is, negative values will be as if they were incremented by (1 + maximum representable value in the unsigned integer type), making them positive.

如果参数被提升为无符号整数类型,但函数使用相应的有符号整数类型获取值,并且该值在两者中都可以表示,则函数获取正确的值.如果值在两者中都不能表示,则行为是实现定义的.

If the argument is promoted to an unsigned integer type, but the function obtains the value using the corresponding signed integer type, and the value is representable in both, the function obtains the correct value. If the value is not representable in both, the behaviour is implementation-defined.

在实践中,几乎所有架构都与上述相反,即获得的有符号整数值与减去(1 + 无符号整数类型的最大可表示值)的无符号值相匹配.我听说一些奇怪的机器可能会发出整数溢出或类似奇怪的信号,但我从来没有在这样的机器上戴过手套.

In practice, almost all architectures do the opposite of above, i.e. the signed integer value obtained matches the unsigned value substracted by (1 + the largest representable value of the unsigned integer type). I've heard that some strange ones may signal integer overflow or something similarly weird, but I have never gotten my mitts on such machines.

man 3 printf 手册页(由如果您将上述规则与 printf 说明符进行比较,则 Linux 手册页项目)非常有用.最后的 make_message() 示例函数(vsnprintf() 所需的 C99、C11 或 POSIX)也应该很有趣.

The man 3 printf man page (courtesy of the Linux man pages project) is quite informative, if you compare the above rules to printf specifiers. The make_message() example function at the end (C99, C11, or POSIX required for vsnprintf()) should also be interesting.

这篇关于可变函数和常量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆