变量函数和常量

时间:2016-10-29 15:54:12

标签: c variadic-functions

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

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

该功能如下所示:

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

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

测试表明,假设它们int似乎工作正常,所以编译器似乎将它们推到int,即使这些常量也可以用单个char或每个short

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

谢谢!

2 个答案:

答案 0 :(得分:3)

我喜欢Jonathan Leffler's answer,但我认为我已经掌握了一些技术细节,对于那些打算编写可移植库或提供具有可变函数的API的人来说,因此需要深入研究细节。

变量参数受default argument promotions的约束(C11草案N1570为PDF;第6.5.2.2节函数调用,第6段):

  

..对每个参数和参数执行整数提升   将float类型提升为double。这些被称为默认参数促销

     

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

     
      
  • 一个提升类型是有符号整数类型,另一个提升类型是相应的无符号整数类型,并且该值可在两种类型中表示;

  •   
  • 这两种类型都是指向字符类型的合格或非限定版本或无效的指针

  •   

浮点常量的类型为double,除非它们以fF为后缀(如1.0f中所示),在这种情况下它们的类型为{ {1}}。

在C99和C11中,如果整数常量为1,则整数常量为float; int(AKA long)如果他们适合其中一个;但是long int(AKA long long)。由于许多编译器假设没有大小后缀的整数常量是人为错误或拼写错误,如果整数常量不是long long int类型,则总是包含后缀是一个好习惯。

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

    {li>

    intu U

    {li>

    unsigned intl L

  • long intluulLUULlULu或{{ 1}} uL

  • {li>

    Ulunsigned long intllLL Ll

    {li>

    lLlong long int(或llu或其任何大写或小写变体)LLU

integer promotion规则见6.3.1.1节。

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

  • ULL被提升为unsigned long long int

  • 其值可以由float表示的所有整数类型都会提升为double。 (这包括未签名且已签名的intint,以及类型charshort和较小_Bool位字段的位字段。)< / p>

  • 其值可以由int(但不是unsigned int表示)的所有整数类型都会提升为unsigned int。 (这包括int位字段无法用unsigned intunsigned int位,换句话说)表示,以及typedef&#39; d int的别名,但是我想,那就是它。)

  • 至少与CHAR_BIT * sizeof (unsigned int)一样大的整数类型不变。例如,这包括unsigned int / intlong / long intlong long类型。

有一个&#39;陷阱&#39;在我要指出的规则中:&#34;签名为unsigned是好的,未签名的签名是iffy&#34;

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

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

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

    实际上,几乎所有体系结构都与上述相反,即获得的有符号整数值与减去的无符号值匹配(1 +无符号整数类型的最大可表示值)。我听说过一些奇怪的信号可能表示整数溢出或类似奇怪的东西,但我从来没有在这些机器上得到我的手套。

如果将上述规则与printf说明符进行比较,那么man 3 printf手册页(由Linux手册页项目提供)非常有用。最后的long long int示例函数(size_t所需的C99,C11或POSIX)也应该很有趣。

答案 1 :(得分:1)

当你写1时,这是一个int常数。没有其他类型允许编译器使用。如果函数的非变量原型需要不同的类型,编译器会将整数1转换为适当的类型,但就其本身而言,1int不变。因此,在您的示例中,所有6个参数都是int

在调用variadic函数处理它们之前,你必须知道参数的类型。使用printf()函数族,格式字符串告诉它期待什么;类似于scanf()系列函数。

请注意,默认转换适用于与可变参数函数的省略号对应的参数。例如,给定:

char c = '\007';
short s = 0xB0hD;
float f = 3.1415927;
致电:

int variadic_function(const char *, ...);

使用:

int rc = variadic_function("c s f", c, s, f);

实际上将cs转换为int,将f转换为double