Reputation: 258
i'm trying to print argument variables type string and i've been stuck on this
trying to move inside the srting using _Str[i]
as Expression inside a while loop and once _Str[i] == '\0'
return (-1) defined as EOF
after that we test on '\n' == EOF
to return EOF
. than switch to the next variable
int main(int argc, char** argv)
{
ps("test1", "test2");
return 0;
}
#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_
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()
//
//
[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] $
i'm stuck on this and have no idea of what to do, can anyone help me understand the problem and solve it?
Upvotes: 2
Views: 19403
Reputation: 210968
There are two possible solutions. Either you marke the end of the argument list:
#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;
}
Or you passe the number of arguments to your function:
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;
}
Finally the function ps
with 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;
}
Upvotes: 1
Reputation: 29126
As mfro already said: You must find a means for your function whan to stop printing. At the moment, you happily walk beyond the bound of your variadic array. The only way that you can leave the inner, infinite loop is when putchar
returns EOF
. That might occur, but it is not very likely; putchar
only returns EOF
on failure; the output stream has no end, it runs as long as your program pours data into it.
(That's different from input, where input ends when the end of the file has been read or if the user types Ctrl-D. With, say, getchar
, EOF
is a condition that usually occurs sooner or later. That's not the case with output.)
If you want to implement your variadic function, you have basically two possibilities: Prepend a count:
ps(3, "Salut", "mon", "ami");
ot append a sentinel value, usually NULL
:
ps("C'est", "la", "vie", NULL);
Fo example, the sentinel variant:
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);
}
There is, of course, the risk that you forget to place the sentinel at the end (or, if you chose the count variant, that you forget to update the count). With GCC's function attributes, you can make the compiler issue a warning when there isn't a sentinel. That's better, but still ugly, because there is extra information at the end of the function call.
If you wrap the implementation in a variadic macro, you can silently append the sentinel:
#define ps(...) ps(__VA_ARGS__, NULL)
The macro ps
will noew call the function ps
with a sentinel. (It may be a good idea to use different names for the macro and the function. In this scenario, the macro will usually be called.)
There's still the problem of type safety. No-one will stop you from calling your function like this:
ps(1, 2, 3);
because variadic arguments can be of any type. (The printf
function relies on a well-formed format string to find the correct type for all arguments.)
Because we have already entered macro territory, you could use compound literals to create a NULL
-terminated array of strings:
#define ps(...) print_strings((const char *[]){__VA_ARGS__, NULL})
with a straight-forward function to print all strings:
void print_strings(const char *str[])
{
while (*str) puts(*str++);
}
That will give you at least warnings about non-char pointers being passed.
Upvotes: 7