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

printing a string using function with variable argument (va_list)

我正在尝试打印字符串类型的参数变量,但我一直卡在这个问题上

想法

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

代码

main.c

int main(int argc, char** argv)
{ 
    ps("test1", "test2");
    return 0;
} 

base.h

#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_

base.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] == '[=13=]')
    //      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 '[=13=]'
           {
               return EOF;// (-1)
           }//end if
           string = va_arg(arg, char*);
    }//end outter while

        va_end(arg); 
        //
        return 1; //to meet spec.
}// end ps()
//
//

使用 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] $ 

我被困在这个问题上,不知道该怎么做,谁能帮助我理解问题并解决它?

有两种可能的解决方案。要么标记参数列表的末尾:

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

#define END_OF_LIST NULL

int ps1( const char* firstStr, ... )
{
    va_list argptr;
    va_start( argptr, firstStr );

    const char* str = firstStr;
    while ( str != END_OF_LIST ) // terminate if end of argument list
    {
        printf( "%s\n", str );
        str = va_arg( argptr, const char* );
    }
    va_end( argptr );
    return 1;
}

int test_C (void)
{
    ps1( "test1", "test2", NULL );
                        // ^^^^ marke end of argument list
    return 0;
}

或者您将参数数量传递给您的函数:

int ps2( int count, ... )
{
    va_list argptr;
    va_start( argptr, count );

    for( int i = 0; i < count; i++ ) // do "count" times
    {
        const char *str = va_arg( argptr, const char* );
        printf( "%s\n", str );
    }
    va_end( argptr );
    return 1;
}

int main (void)
{
    ps2( 2, "test1", "test2" );
    //   ^ number of arguments
    return 0;
}

最后函数 psputchar:

#define EOF -1

int ps( const char* firstStr, ... )
{
    va_list argptr;
    va_start( argptr, firstStr );

    const char* str = firstStr;
    while ( str != END_OF_LIST )
    {
        int i = 0;
        while ( str[i] )
        {
            if( putchar( str[i]) == EOF )
                return EOF;
            i++;
        }
        if ( putchar( '\n' ) == EOF )
            return EOF;

        printf( "%s\n", str );
        str = va_arg( argptr, const char* );
    }
    va_end( argptr );
    return 1;
}

正如 mfro 所说:您必须为您的函数找到停止打印的方法。此刻,您愉快地走出了可变数组的界限。离开内部无限循环的唯一方法是 putchar returns EOF。这可能会发生,但可能性不大; putchar 仅 returns EOF 失败;输出流没有尽头,只要您的程序向其中输入数据,它就会运行。

(这与输入不同,输入在读取文件末尾或用户键入 Ctrl-D 时结束。例如,getchar, EOF 是一般迟早会出现的情况,输出不是这样。)

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

ps(3, "Salut", "mon", "ami");

不附加标记值,通常是 NULL:

ps("C'est", "la", "vie", NULL);

例如,哨兵变体:

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);
}

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

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

#define ps(...) ps(__VA_ARGS__, NULL)

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

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

ps(1, 2, 3);

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

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

#define ps(...) print_strings((const char *[]){__VA_ARGS__, NULL})

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

void print_strings(const char *str[])
{
    while (*str) puts(*str++);
}

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