C 函数调用中的默认参数提升 [英] Default argument promotions in C function calls

查看:35
本文介绍了C 函数调用中的默认参数提升的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

设置

我有一些关于在 C 中调用函数时默认参数提升的问题.这是 C99 标准 (pdf)(强调添加并分解为便于阅读的列表):

第 6 段

<块引用>

  1. 如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并且类型为float的参数被提升为 double.这些被称为默认参数提升.
  2. 如果参数数量不等于参数数量,则行为未定义.
  3. 如果函数定义的类型包含原型,并且原型以省略号 (, ...) 或参数类型结束升级后与参数类型不兼容,行为未定义.
  4. 如果函数定义为不包含原型的类型,并且提升后的参数类型与提升后的参数类型不兼容,则行为未定义,除非对于以下情况:
    • 一个提升类型是有符号整数类型,另一个提升类型是对应的无符号整数类型,值在两种类型中都可以表示;
    • 这两种类型都是指向字符类型或 void 的限定或非限定版本的指针.

第 7 段

<块引用>

  1. 如果表示被调用函数的表达式的类型确实包含原型,则参数将被隐式转换,就像通过赋值一样,转换为相应参数的类型,采用的类型为每个参数都是其声明类型的非限定版本.
  2. 函数原型声明符中的省略号会导致参数类型转换在最后一个声明的参数之后停止.默认参数提升是在尾随参数上执行的.

第 8 段

<块引用>

  1. 不会隐式执行其他转换;特别是,不会将参数的数量和类型与不包含函数原型声明符的函数定义中的参数进行比较.

我所知道的

  • 默认参数提升charshortint/unsigned int> 和 floatdouble
  • 可变参数函数的可选参数(如 printf)受默认参数提升的约束

为了记录,我对函数原型的理解是这样的:

void func(int a, char b, float c);//函数原型void func(int a, char b, float c) {/* ... */}//函数定义

问题

我真的很难理解这一切.以下是我的一些问题:

  • 原型函数和非原型函数的行为真的有很大不同吗,例如在默认提升和隐式转换方面?
  • 默认参数提升何时发生?总是这样吗?还是只是在特殊情况下(例如可变参数函数)?是否取决于函数是否被原型化?

解决方案

Upvoted AProgrammer's answer-那些才是真正的好货.

对于那些想知道为什么事情是这样的人:在 1988 年之前的黑暗时代,经典的K&R"C 中没有函数原型这样的东西,而设置默认参数提升是因为 (a) 本质上是免费的",因为将一个字节放入寄存器中的成本不会超过将一个字放入寄存器中的成本,并且 (b) 减少潜在的参数传递错误.第二个原因并没有完全解决,这就是为什么在 ANSI C 中引入函数原型是 C 语言中最重要的变化.

至于默认促销何时开始:当参数的预期类型为未知时,使用默认参数促销,也就是说,当没有原型或当参数是可变参数时.

Setup

I have a few questions about the default argument promotions when calling a function in C. Here's section 6.5.2.2 "Function calls" Paragraphs 6, 7, and 8 from the C99 standard (pdf) (emphasis added and broken into lists for ease of reading):

Paragraph 6

  1. 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.
  2. If the number of arguments does not equal the number of parameters, the behavior is undefined.
  3. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined.
  4. If the function is defined with a type that does not include a prototype, and 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;
    • both types are pointers to qualified or unqualified versions of a character type or void.

Paragraph 7

  1. If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type.
  2. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

Paragraph 8

  1. No other conversions are performed implicitly; in particular, the number and types of arguments are not compared with those of the parameters in a function definition that does not include a function prototype declarator.

What I know

  • The default argument promotions are char and short to int/unsigned int and float to double
  • The optional arguments to variadic functions (like printf) are subject to the default argument promotions

For the record, my understanding of a function prototype is this:

void func(int a, char b, float c);  // Function prototype
void func(int a, char b, float c) { /* ... */ }  // Function definition

Question

I'm having a really hard time groking all of this. Here are some questions I have:

  • Do prototyped and non-prototyped functions' behavior really differ so much, such as with regard to default promotions and implicit conversions?
  • When do default argument promotions occur? Is it always? Or is it just in special cases (like with variadic functions)? Does it depend on whether a function is prototyped?

解决方案

Upvoted AProgrammer's answer—those are the real goods.

For those of you who are wondering why things are this way: in the dark ages before 1988, there was no such thing as a function prototype in classic "K&R" C, and the default argument promotions were instituted because (a) there were essentially "free", as it costs no more to put a byte in a register than to put a word in a register, and (b) to cut down on potential errors in parameter passing. That second reason never quite cut it, which was why the introduction of function prototypes in ANSI C was the single most important change ever in the C language.

As to when default promotions kick in: default argument promotions are used exactly when the expected type of the argument is unknown, which is to say when there's no prototype or when the argument is variadic.

这篇关于C 函数调用中的默认参数提升的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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