使用带有可变参数的函数(va_list)打印字符串

0x0*_*584 2 c string pointers variadic-functions

我正在尝试打印参数变量类型字符串,但我一直坚持这个

想法

尝试_Str[i]在 while 循环中使用as 表达式在srting 内部移动,并且一旦_Str[i] == '\0'返回(-1)定义为EOF之后我们测试'\n' == EOF返回EOF。比切换到下一个变量

编码

主文件

int main(int argc, char** argv)
{ 
    ps("test1", "test2");
    return 0;
} 
Run Code Online (Sandbox Code Playgroud)

基数

#ifndef BASE_H_
 #define BASE_H_
  #ifndef EOF // (EOF) - End Of File character. (-1)
   #define EOF (-1)
  #endif// EOF 
 #include<stdio.h>// Facultatif  
 #include<stdarg.h> 

    int ps(const char* , ...); // our function


#endif // BASE_H_
Run Code Online (Sandbox Code Playgroud)

基地.c

int ps(const char* _Str, ...)
{
    int  i = 0;

    //char* string;
    //va_list
    //
        va_list arg;
        va_start(arg, _Str);

    //for(i = 0;    ; ++i)
    //  if(_Str[i] == '\0')
    //      putchar('\n');
    while(1)
    {
        while(_Str[i])
        {
            if( putchar(_Str[i]) == EOF)  //test on EOF value
            { 
                return EOF;// (-1)
            }//end if
         i++;
        }//end inner while
           if(putchar('\n') == EOF)  //occur right after we quit due to '\0'
           {
               return EOF;// (-1)
           }//end if
           string = va_arg(arg, char*);
    }//end outter while

        va_end(arg); 
        //
        return 1; //to meet spec.
}// end ps()
//
//
Run Code Online (Sandbox Code Playgroud)

使用 GCC 编译

[ar.lnx@host print] $ make
Building objects..
gcc -c -o main.o main.c -I. -Wall 
Building objects..
gcc -c -o base.o base.c -I. -Wall 
Building the application core..
gcc -o x main.o base.o  -I. -g
Finish.
[ar.lnx@host print] $ ./x
Segmentation fault (core dumped)
[ar.lnx@host print] $ 
Run Code Online (Sandbox Code Playgroud)

我被困在这个问题上,不知道该怎么做,有人可以帮助我理解问题并解决它吗?

M O*_*ehm 6

正如 mfro 已经说过的那样:您必须为您的功能找到一种方法来停止打印。此刻,您愉快地走出了可变参数数组的界限。您可以离开内部无限循环的唯一方法是何时putchar返回EOF。这可能会发生,但可能性不大;putcharEOF在失败时返回;输出流没有尽头,只要您的程序将数据倒入其中,它就会运行。

(这与输入不同,输入在读取文件末尾或用户键入时结束Ctrl-D。例如getcharEOF是一种通常迟早会发生的条件。输出不是这种情况。)

如果你想实现你的可变参数函数,你基本上有两种可能性: 前置一个计数:

ps(3, "Salut", "mon", "ami");
Run Code Online (Sandbox Code Playgroud)

ot 附加一个哨兵值,通常是NULL

ps("C'est", "la", "vie", NULL);
Run Code Online (Sandbox Code Playgroud)

例如,哨兵变体:

void ps(const char *str, ...)
{
    va_list arg;

    va_start(arg, str);

    while (str) {
        puts(str);
        str = va_arg(arg, const char *);
    }

    va_end(arg);
}
Run Code Online (Sandbox Code Playgroud)

当然,存在忘记将哨兵放在最后的风险(或者,如果您选择了计数变体,则您忘记更新计数)。使用 GCC 的函数属性,可以让编译器在没有哨兵时发出警告。这更好,但仍然很难看,因为在函数调用的末尾有额外的信息。

如果将实现包装在可变参数宏中,则可以静默地附加哨兵:

#define ps(...) ps(__VA_ARGS__, NULL)
Run Code Online (Sandbox Code Playgroud)

ps将 noew 调用ps带有哨兵的函数。(为宏和函数使用不同的名称可能是个好主意。在这种情况下,通常会调用宏。)

仍然存在类型安全问题。没有人会阻止你像这样调用你的函数:

ps(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

因为可变参数可以是任何类型。(该printf函数依赖于格式良好的格式字符串来为所有参数找到正确的类型。)

因为我们已经进入了宏领域,你可以使用复合字面量来创建一个NULL以字符串结尾的数组:

#define ps(...) print_strings((const char *[]){__VA_ARGS__, NULL})
Run Code Online (Sandbox Code Playgroud)

使用直接的函数来打印所有字符串:

void print_strings(const char *str[])
{
    while (*str) puts(*str++);
}
Run Code Online (Sandbox Code Playgroud)

这至少会给你关于传递非字符指针的警告。