簡體   English   中英

C 中 printf() 函數的工作(僅可變數量的參數)

[英]Working of printf() function in C (Only Variable number of Arguments)

我問這個問題是為了理解printf作為一個接受可變長度參數的函數的工作原理。

我正在從這里學習可變數量的參數概念,讓我感到困惑的是傳入va_arg(va_list,datatype)的數據類型。 我的意思是他們在這里提到了一種數據類型。 我們需要傳遞具有不同數據類型的參數的情況呢? printf函數中也是如此。

printf 如何准確計算出不同類型的參數類型。 根據我的想法,他們必須檢查第一個 const char* 參數中的每個 % 符號,然后檢查特定數據類型的標記。

以下變量參數列表函數向您解釋了printf將如何工作。

#include <stdio.h>
#include <stdarg.h>

void foo(char *fmt, ...) // This Function works like same as printf
{
    va_list ap;
    int d;
    char c, *s;

    va_start(ap, fmt);
    while (*fmt)
        switch (*fmt++) {
           case 's':              /* string */
               s = va_arg(ap, char *);
               printf("string %s\n", s);
               break;
           case 'd':              /* int */
               d = va_arg(ap, int);
               printf("int %d\n", d);
               break;
           case 'c':              /* char */
               /* need a cast here since va_arg only
                  takes fully promoted types */
               c = (char) va_arg(ap, int);
               printf("char %c\n", c);
               break;
        }
   va_end(ap);
}

main()
{
// call the foo function here
// like foo("%d%s%c",3,"hai",'a');
}

有關更多參考,請參閱va_arg的手冊頁

它不支持charfloat值,因此我們需要對其進行類型轉換。 對於float ,您需要對double精度值進行類型轉換。

printf是一個函數,而不是宏。 它被定義為

int printf(const char *, ...)

並且有可變數量的參數。

printf使用字符串來定義傳遞的參數數量。 每個 % 用於向堆棧移動並檢索參數。

所以,如果你通過

"%d %d %d", 1, 2

然后將顯示 1、2 和任意值。 這很糟糕:你可以使用這個函數沿着堆棧向下走。

通過時

"%d %d", 1, 2, 3

然后將顯示 1 和 2。 並且行為未定義:通常使用 __cdecl 調用約定,因此堆棧不會因為被調用者清理而損壞。

是的,它會檢查 %X 以查看要使用的數據類型或標志。 沒有這個,它就無法知道。 唯一的方法是使用 C++ 的可變參數模板。 除此之外,C如下所示..

此示例使用fwrite將數據寫入stdout

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstdarg>

void C_Printf(const char *fmt, ...)
{
    int fmtLength = strlen(fmt);
    va_list VariableArgs;
    va_start(VariableArgs, fmt);

    for (int I = 0; I < fmtLength; I++)
    {
        if (fmt[I] == '%')
        {
            switch(tolower(fmt[++I]))
            {
                case 'f':
                {
                    double d = va_arg(VariableArgs, double);
                    fwrite(&d, sizeof(double), 1, stdout);
                }
                break;

                case 'i':
                case 'd':
                {
                    int i = va_arg(VariableArgs, int);
                    fwrite(&i, sizeof(int), 1, stdout);
                }
                break;

                case 's':
                {
                    const char *str = va_arg(VariableArgs, const char *);
                    fwrite(&str[0], sizeof(char), strlen(str), stdout);
                }
                break;

                default:
                    break;
            }
        }
        else
            fwrite(&fmt[I], sizeof(char), 1, stdout);
    }
    va_end(VariableArgs);
}

int main()
{
    C_Printf("%s", "hello there");
}

在一些自由軟件標准 C 庫實現中查看printf的源代碼,例如GNU libcmusl-libc 我發現musl-libc非常易讀,查看src/stdio/printf.c然后查看src/stdio/vfprintf.c (真正的工作完成的地方)。 當然它根據格式控制字符串使用va_arg (參見stdarg(3) 。注意va_arg是在編譯器內部實現的,通過GCC中的__builtin_va_arg )。 GCC 還內置了對printf的支持

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM