使用带有可变参数 (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;
}
最后函数 ps
与 putchar
:
#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++);
}
这至少会给您关于传递非字符指针的警告。
我正在尝试打印字符串类型的参数变量,但我一直卡在这个问题上
想法
尝试使用 _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;
}
最后函数 ps
与 putchar
:
#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++);
}
这至少会给您关于传递非字符指针的警告。