【问题标题】:I want default argument promotion's example我想要默认参数提升的例子
【发布时间】:2020-02-14 06:43:15
【问题描述】:

在 C 标准 6.5.2.2 中。第 6 段

如果表示被调用函数的表达式的类型不包含原型,则 对每个参数执行整数提升,浮点类型的参数是 晋升为双。这些称为默认参数提升。

“表示被调用函数的表达式具有不包含原型的类型”这可以用代码来描述吗?

char a = 1;
printf("%d", a); /*"the expression that denotes the called function" is printf, and `a`
doesn't have prototype(char). but a can't be replaced as `char a` originally.  ...? */

这是我的想法,可以适用于上述标准。是这样吗?

【问题讨论】:

  • C99 不再允许隐式定义的函数(消除语言的缺陷......)。如果您碰巧为旧标准编译,我只能推荐-Werror=implicit 命令行选项(GCC!——尽管大多数其他编译器都有类似的选项)。请注意,这些提升仍然适用于可变参数函数。

标签: c


【解决方案1】:

由于历史原因,C 中的函数可以以旧方式声明,无需原型,如下:

type function()

或带有原型的新方法,如:

type function(type parameter)
type function(type parameter, type parameter)
type function(type parameter, type parameter, type parameter)
… and so on

因此,给定参数类型的函数声明被称为具有原型。这会影响调用函数时参数的准备。使用原型,参数被转换为参数的声明类型。在没有原型的情况下,使用称为默认参数提升的规则将参数转换为默认类型。 (另外,如果一个函数只是被声明而不是定义,参数的名称可以省略,留下一个只列出类型的原型,如type function(type, type, type)。)

由于旧语法,要声明一个带有原型的函数说它没有参数,你需要明确地说voidtype function(void)。 (这点在C++中不同,它不必支持旧语法。在C++中,type function()是一个没有参数的原型。)

此外,原型可以通过将,... 放在一个或多个常规参数之后来指定变量参数:

type function(type parameter,...)

在这种情况下,第一个参数将转换为参数类型,但与 ... 对应的参数将使用默认参数提升进行转换。 printf 就是这样声明的。

默认的参数提升是:

  • int 窄的整数(从技术上讲,等级较低)如果可以表示源类型的所有值,则提升为int,否则提升为unsigned int
  • float 参数转换为 double

对于默认参数提升中的位域也有一些挑剔,我不能说这在我的代码中曾经出现过。

历史

在旧的 C 语法中,函数会被定义为:

type function(name0, name1, name2)
type name0;
type name1;
type name2;

它会在没有原型的情况下被声明,就像type function()一样。这意味着调用者不知道参数的实际类型。但是你不能只为char 参数传递一个char 值或为short 参数传递一个short 值,因为C 试图变得灵活,以便它可以在多种类型的计算机上工作,它有规则关于 charshort 值在表达式中被提升为 int。此外,像'X' 这样的字符常量的类型为int,而不是char,因此,如果有人用foo('X') 调用函数,编译器将不知道foo 是否真的只需要char 而不是int。因此,默认参数提升是为了匹配整数提升。

后来的 C 版本通过提供一种在对调用者可见的声明中声明参数类型的方法来解决此问题,因此参数始终与参数匹配(并且编译器有更多可用于提供错误消息的信息)。但是还是要支持旧语法,才能编译旧代码。

更多

使用 C 标准中的短语“表示被调用函数的表达式”是因为您可以通过指针调用函数,而不仅仅是它们的名称。例如,我们可以这样写:

    int (*FunctionPointer)() = (int (*)()) printf;

然后使用FunctionPointer("Hello, world.\n"); 调用printf。那么“表示被调用函数的表达式”就是FunctionPointer,尽管printf 有原型,但它没有原型。 (没有充分的理由使用printf 函数执行此操作,但一些深奥的代码可能会做一些令人讨厌的事情。)

你最初可以声明一个没有原型的函数:

int foo();

然后添加原型:

int foo(float x, char *y);

编译器将合并声明,生成的foo 将有一个原型。

【讨论】:

  • 你好。我有一个疑问。当函数用原型声明时,我们是否可以这样想:首先进行通常的默认提升,然后根据参数传递到的参数的类型截断或提升这个提升的值?例如。 int f(char param); in main char x=65; f(x); 我们能不能想,x 在默认提升方案的基础上先提升为 int,然后再根据 param 的类型为 char 再次截断?
  • 我想我在Herbert Schildt's C 书中读到了这种类型的解释。这种思维方式会产生不同的结果吗?
  • @AbhishekGhosh:不使用默认参数提升来提升参数,然后将其转换为参数类型。根据 C 2018 6.5.2.2 7,当表示函数的表达式具有原型时,与声明的参数对应的参数将转换为参数类型。使用默认参数提升来提升声明参数后对应于 ... 的尾随参数。所以每个论点都得到一种处理或另一种处理,而不是两者兼而有之。
  • @AbhishekGhosh:请注意,对于要转换为参数类型的参数,这是一种转换,而不是“截断”或“提升”。某些转换可能会产生等同于删除位或小数部分的结果,这可能就是您所说的截断。提升是一种不会更改值的转换类型(例如从整数类型转换为更广泛的整数类型时)。所有促销都是转化,但并非所有转化都是促销(或截断)。
【解决方案2】:

表示被调用函数的表达式是printf,对。

如果您不为 此函数 提供原型,例如不包括 <stdio.h> 并且不自己声明 printf(),编译器会将该函数识别为无原型。

在您的情况下,第一个参数是const char *,第二个参数是int

所以,不是a没有原型,而是printf()

a 的值将提升为int

假定的原型将是int printf();,并采用任意数量和任意类型的参数。

【讨论】:

  • 1.包括<stdio.h>以后使用printf()算不算原型? 2.你说第一个参数和第二个有原型,(我的想法)然后 printf 也包括原型。但是'a将被提升......'是如何发生的呢?这是定义的行为吗?
  • To 1.:是的,因为该标头包含printf() 的原型。您可以使用编辑器打开此文件。 -- To 2.: 我不明白这个问题。变量不能有原型,只有函数。顺便说一句,函数参数是一种变量。而且,是的,促销已定义,正如您的报价所示。
  • 1.在顶部写<stdio.h>与写int printf( const char* str, ...);相同,当编译器遇到printf("%d", a);时,它转到int printf( const char* str, ...);并且printf因为省略号...不包含原型,所以a被提升为int。我的理解对吗?? 2. 你的意思是原型为“函数原型是声明其参数类型的函数的声明。”不是“函数定义之前的声明”还是说原型的两个含义?
  • To 1.: 您的 printf() 示例由于可变参数而很特殊。这些论点中的每一个都得到了提升。 “printf 不包含原型”是什么意思? -- To 2.:当你声明或定义它时在你调用它之前,你得到了一个原型函数。您也可以在调用它之前定义一个函数,在这种情况下,编译器从定义中知道原型。函数原型使编译器知道所有数据类型、所有参数和返回值。
  • 这个答案不正确。 (a) 符合现代 C 标准的编译器将不接受没有声明调用的函数。 (b) 函数有无原型的区别在于是否声明了参数类型列表,而不是函数是否有声明。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-02
  • 1970-01-01
  • 2013-06-25
  • 2020-09-01
  • 2010-11-18
  • 1970-01-01
  • 2015-05-01
相关资源
最近更新 更多