0x0584
0x0584

Reputation: 258

printing a string using function with variable argument (va_list)

i'm trying to print argument variables type string and i've been stuck on this

The idea

trying to move inside the srting using _Str[i] as Expression inside a while loop and once _Str[i] == '\0' return (-1) defined as EOFafter that we test on '\n' == EOF to return EOF. than switch to the next variable

The Code

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] == '\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()
//
//

Compiling using 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] $ 

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

Answers (2)

Rabbid76
Rabbid76

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

M Oehm
M Oehm

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

Related Questions