Thread: Stdarg :(

  1. #1
    Registered User
    Join Date
    Jul 2004
    Posts
    12

    Stdarg :(

    Ok here is my problem:

    I have a bunch of strings like this:

    unsigned char hello[]="hello";
    unsigned char goodbye[]="goodbye";

    Now I have a struct with different values in it:

    typedef struct
    {
    unsigned char **phrase;
    //more values are in the actual program
    }Greetings;

    I wanna sent it to a function that loads in the strings one by one. So I could for example use this:

    InitGreetings(&phrases, hello, goodbye);

    So far I have this but it does not work:
    Code:
    void InitGreeting(Greetings *tempGreets, unsigned char *tempPhrase,...)
    {
    int loop;
    unsigned char *s;
    tempGreets->phrase=(unsigned char **)malloc(sizeof(unsigned char*) *2);
    
    va_list ap;
    
    va_start(ap, *tempPhrase);
    
    for(loop=0; s=va_arg(ap, unsigned char*); loop++)
    {
    tempGreets->phrase[loop]*s;
    printf("%s", s);
    }
    
    va_end(ap);
    }
    Only thing that happens is the second one is displayed but not the first. The first one prints weird things no matter what string.

    Here is the code if you wann look at it:

    Code:
    #include <stdio.h>
    #include <conio.h>
    #include <stdarg.h>
    #include <stdlib.h>
    
    
    typedef struct
    {
    unsigned char **phrase;
    //more values are in the actual program
    }Greetings;
    
    Greetings phrases;
    
    void InitGreeting(Greetings *tempGreets, unsigned char *tempPhrase,...);
    
    unsigned char hello[]="hello";
    unsigned char goodbye[]="goodbye";
    
    
    int main()
    {
    InitGreeting(&phrases, hello, goodbye);
    getch();
    return 0;
    }
    
    void InitGreeting(Greetings *tempGreets, unsigned char *tempPhrase,...)
    {
    int loop;
    unsigned char *s;
    tempGreets->phrase=(unsigned char **)malloc(sizeof(unsigned char*) *2);//this is different in real program but its close enough
    
    va_list ap;
    
    va_start(ap, *tempPhrase);
    
    for(loop=0; s=va_arg(ap, unsigned char*); loop++)
    {
    tempGreets->phrase[loop]*s;
    printf("%s", s);
    }
    
    va_end(ap);
    }
    There could be some typos but you get the point.

  2. #2
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    
    typedef struct
    {
      unsigned char **phrase;
    } Greetings;
    
    Greetings phrases;
    
    void InitGreeting(Greetings *, int, ...);
    
    unsigned char hello[] = "hello";
    unsigned char goodbye[] = "goodbye";
    
    int main()
    {
    // Pass the number of args (3) also
      InitGreeting(&phrases, 3, hello, goodbye, "salami");
      getchar();
      return 0;
    }
    
    void InitGreeting(Greetings *tempGreets, int ngreets, ...)
    {
      int loop;
      unsigned char *s;
      va_list ap;
    
      tempGreets->phrase = malloc(sizeof(unsigned char *)*ngreets);
    
      va_start(ap, ngreets);
    
      for(loop = 0;loop < ngreets;loop++)
      {
    // s becomes pretty pointless here. Remove it if you want
        s = va_arg(ap, unsigned char *);
        tempGreets->phrase[loop] = s;
        printf("%s\n", s);
      }
    
      va_end(ap);
    }
    Try that one. You can't reliably find the end of the arglist using va_arg()'s return value. I threw in "salami" just to show you that you don't need to pass variables. I wasn't sure if you knew that, but if you didn't you do now

    From the man page:
    type va_arg( va_list ap, type);

    If there is no next argument, or if type is not compatible
    with the type of the actual next argument (as promoted
    according to the default argument promotions), random
    errors will occur.
    I have no idea what you were doing here:
    Code:
    tempGreets->phrase[loop]*s;
    Did you forget an = sign? You wouldn't want to dereference s in this situation if that's the case.

    Also, try indenting your code a bit to make it easier to read.
    Last edited by itsme86; 08-02-2004 at 03:04 PM.

  3. #3
    Registered User
    Join Date
    Jul 2004
    Posts
    12
    Quote Originally Posted by itsme86
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    
    typedef struct
    {
      unsigned char **phrase;
    } Greetings;
    
    Greetings phrases;
    
    void InitGreeting(Greetings *, int, ...);
    
    unsigned char hello[] = "hello";
    unsigned char goodbye[] = "goodbye";
    
    int main()
    {
    // Pass the number of args (3) also
      InitGreeting(&phrases, 3, hello, goodbye, "salami");
      getchar();
      return 0;
    }
    
    void InitGreeting(Greetings *tempGreets, int ngreets, ...)
    {
      int loop;
      unsigned char *s;
      va_list ap;
    
      tempGreets->phrase = malloc(sizeof(unsigned char *)*ngreets);
    
      va_start(ap, ngreets);
    
      for(loop = 0;loop < ngreets;loop++)
      {
    // s becomes pretty pointless here. Remove it if you want
        s = va_arg(ap, unsigned char *);
        tempGreets->phrase[loop] = s;
        printf("%s\n", s);
      }
    
      va_end(ap);
    }
    Try that one. You can't reliably find the end of the arglist using va_arg()'s return value. I threw in "salami" just to show you that you don't need to pass variables. I wasn't sure if you knew that, but if you didn't you do now

    From the man page:


    I have no idea what you were doing here:
    Code:
    tempGreets->phrase[loop]*s;
    Did you forget an = sign? You wouldn't want to dereference s in this situation if that's the case.

    Also, try indenting your code a bit to make it easier to read.
    Err I changed some of my original program so that my code wouldn't take up the whole page. It was suppose to be taken out. Also thank you. It works !. If anyone can figure out a way to not have to send in the number of strings let me know.
    Last edited by ExtremelyStupid; 08-02-2004 at 03:10 PM.

  4. #4
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    It's really just not possible to not tell InitGreeting() how many arguments you're passing to it in some way. Check this page out: http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?stdarg+5

    This paragraph specifically:
    It is up to the calling routine to determine how many arguments there
    are, since it is not possible to determine this from the stack frame.
    For example, execl passes a 0 to signal the end of the list. Printf can
    tell how many arguments are supposed to be there by the format.
    So you have to tell InitGreeting() in some way where the end of the list is. I suppose you could try calling it like this:
    Code:
    InitGreeting(&phrases, hello, goodbye, NULL);
    Then when s == NULL you know you've reached the end of the list. Telling it exactly how many arguments you're passing it just makes it simpler to allocate the memory you need for yours. If you used the method I just introduced you'd have to either loop twice through the arg list (once to count how many there are) and then allocate memory for your phrases array, or you'd have to keep realloc()'ing the memory for it every time through the loop.
    Last edited by itsme86; 08-02-2004 at 04:19 PM.

  5. #5
    Registered User
    Join Date
    Jul 2004
    Posts
    12
    Quote Originally Posted by itsme86
    It's really just not possible to not tell InitGreeting() how many arguments you're passing to it in some way. Check this page out: http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?stdarg+5

    This paragraph specifically:


    So you have to tell InitGreeting() in some way where the end of the list is. I suppose you could try calling it like this:
    Code:
    InitGreeting(&phrases, hello, goodbye, NULL);
    Then when s == NULL you know you've reached the end of the list. Telling it exactly how many arguments you're passing it just makes it simpler to allocate the memory you need for yours. If you used the method I just introduced you'd have to either loop twice through the arg list (once to count how many there are) and then allocate memory for your phrases array, or you'd have to keep realloc()'ing the memory for it every time through the loop.
    I thought at the end of the list it would automatically equal NULL.

  6. #6
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    EDIT: No, it doesn't automatically tag a NULL at the end of the list. The calling function is responsible for that as the webpage I posted points out.

    Here's a modification to your code that does it by marking the end of the arg list with NULL:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    
    typedef struct
    {
      unsigned char **phrase;
    } Greetings;
    
    Greetings phrases;
    
    void InitGreeting(Greetings *, ...);
    
    unsigned char hello[] = "hello";
    unsigned char goodbye[] = "goodbye";
    
    int main()
    {
      InitGreeting(&phrases, hello, goodbye, NULL);
      getchar();
      return 0;
    }
    
    void InitGreeting(Greetings *tempGreets, ...)
    {
      int loop, ngreets;
      unsigned char *s;
      va_list ap;
    
    // Count the number of args
      va_start(ap, tempGreets);
      for(loop = 0;;loop++)
        if(va_arg(ap, unsigned char *) == NULL)
          break;
      va_end(ap);
      ngreets = loop;
    
    // Allocate the memory
      tempGreets->phrase = malloc(sizeof(unsigned char *)*ngreets);
    
    // Loop again, this time storing the args in the array
      va_start(ap, tempGreets);
      for(loop = 0;loop < ngreets;loop++)
      {
        s = va_arg(ap, unsigned char *);
        tempGreets->phrase[loop] = s;
        printf("%s\n", s);
      }
      va_end(ap);
    }
    EDIT (again): Passing ngreets directly to InitGreeting() (like I did in my original reply) has the added benefit of causing the compiler (most compilers anyway) to barf or at least throw up a warning if you forget to supply that argument. If the prototype looks like InitGreeting(Greetings *, int, ...); and you try calling it with InitGreeting(&phrases, hello, goodbye); then the compiler is going to see that the type for hello is not an int and complain about it. However, if you forget to add a NULL to the end of your agument list with the method presented in this reply, the compiler will most certainly happily compile it and then you'll just get runtime problems.
    Last edited by itsme86; 08-02-2004 at 04:35 PM.

  7. #7
    Quote Originally Posted by itsme86
    I suppose you could try calling it like this:
    Code:
    InitGreeting(&phrases, hello, goodbye, NULL);
    Beware, the (char*) typecast is not an option with variadics.
    Code:
    InitGreeting(&phrases, hello, goodbye, (char*) NULL);
    Last edited by Emmanuel Delaha; 08-02-2004 at 04:57 PM. Reason: 'void *' replaced by 'char *'
    Emmanuel Delahaye

    "C is a sharp tool"

  8. #8
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    Sorry, you're right. But shouldn't the NULL be cast to unsigned char * instead of void *? I mean, it's already a void *.

  9. #9
    Registered User
    Join Date
    Jul 2004
    Posts
    12
    How come on this page http://ccrma.stanford.edu/planetccrm.../stdarg.3.html

    The author can use while(*fmt)?

  10. #10
    Quote Originally Posted by itsme86
    Sorry, you're right. But shouldn't the NULL be cast to unsigned char * instead of void *? I mean, it's already a void *.
    NULL is not necesseraly a (void*), but you are correct, it must be typecasted to (char *) if a string is expected.

    I fix my post.
    Emmanuel Delahaye

    "C is a sharp tool"

  11. #11
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    Quote Originally Posted by ExtremelyStupid
    How come on this page http://ccrma.stanford.edu/planetccrm.../stdarg.3.html

    The author can use while(*fmt)?
    Because as long as fmt contains a conversion character, the function can assume there's another arg to fill that spot. So if there's 3 conversion characters in the fmt string then the function assumes there's going to be 3 args following the fmt string.

    Hope that makes sense.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. stdarg - say function.
    By Vber in forum C Programming
    Replies: 5
    Last Post: 03-23-2003, 05:26 PM
  2. stdarg, checking for # of entered arguments
    By Dual-Catfish in forum C++ Programming
    Replies: 1
    Last Post: 06-14-2002, 08:28 AM