C- printf为什么需要强制性参数



c语言中printf函数的定义是:

int printf(const char * _Format, ...);

scanf和许多类似的功能也是如此。

为什么有_Format强制性参数?

格式字符串是必须的

具体来说,要读取其他变量参数,请使用va_start(然后反复使用va_arg,一次用于要读取的每个变量参数)。当您调用va_start时,您需要将其传递格式字符串(或更一般而言,最后一个不变参数到函数)。

例如,这与printf一样,但要打印到stdout和您选择的另一个文件:

void tee(FILE *f, char const *fmt, ...) { 
    va_list ap;
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
    va_start(ap, fmt);
    vfprintf(f, fmt, ap);
    va_end(ap);
}

这使用vprintfvfprintf,因此它不(直接)使用va_arg本身,仅使用va_startva_end,但这足以说明fmt如何使用va_start

一次,这实际上并不需要。回到C时,您可以具有相当于:int f(...);的功能。

但是,在第一个C标准化工作期间,这被取消了上述宏(va_startva_argva_end),它需要至少一个命名参数。较旧的宏对呼叫大会提出了许多要求:

  1. 参数总是以相同的方式传递,无论类型或数字如何。
  2. 总是很容易找到通过的参数。

随着常规的c呼叫约定(所有参数均在堆栈上传递,参数从右向左推动)是事实。您基本上只是看着堆栈的顶部,向后移动返回地址,这是第一个参数。

随着其他呼叫惯例,事情并不是那么简单。例如,仅从左至右推动参数意味着第一个参数(在printf的情况下,格式字符串)被埋在堆栈下的一些任意距离,并在其之后使用任意数量的其他参数。

他们想出的方式是将以前的(命名)参数传递给 va_start(而va_start是通常使用该参数地址的宏)。如果您从右向左推动,这将为您提供一个地址,任何需要的距离都需要堆栈,则va_arg可以回路 up 堆栈以检索其他变量参数。

这显然被视为可接受的妥协,尤其是因为采用变量参数的函数几乎总是以至少一个命名的参数。

,因为它不想猜测要打印什么

这是必不可少的,因为printf使用打印数据。想象一下,如果您什么都没打印会发生什么。没有什么。那么,为什么要删除该参数?

关于scanf是同一件事:您需要以某种方式读取数据,如果您不知道此数据的格式,该怎么办?

某些功能没有参数,因为它们不需要它们,例如

void Hello(void) { puts("Hello"); }

因此,他们可以'生存'没有参数。关于printf

int printf(void) { //imaginary function, don't use it!
    // WTF? What to print?
    // Absolutely nothing! What's the purpose then?
    return smth;
}

然后printf 当没有参数传递时,绝对没有用。

一般而言,具有未知数参数的函数依赖于va_startva_argva_end来处理函数参数列表中未明确的参数。

va_start需要使用的最后一个命名参数。因此,一个具有未知数参数的函数必须至少具有一个命名参数。

printf指定格式规范的参数/参数是最佳选择,作为必需的参数/参数。

没有格式描述,printf不会理解要打印的内容。对于C,一切都只是字节,因此printf不知道传递了什么样的数据,因此不知道如何表示。

当您是新来的C时,您可能还没有意识到这是多么真实,尤其是如果您学到了一种语言,即Print()了解它所看到的数据类型。

最新更新