Archive

Posts Tagged ‘person’

Variable number of arguments for a function in C

October 13, 2013 No comments

If you have ever program in C, I’m sure you had met printf(), it is, probably, the first thing you used in this language (at least for a Hello World). Well, this function accepts as many arguments as keywords you enter in its first argument, so here we have a variable number of arguments.

Some people think that only printf() and scanf() functions worth it, but there are so many cases where it is needed, for example when creating a function with optional arguments (these functions are common in C++, but C doesn’t allow it so easily), when concatenating some strings, filling data variables, internationalizing, and more.

All we must do is to include stdarg.h wichi provides us the tools to make it happen.

Let’s see this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

void putargs(int nargs, ...)
{
  va_list ap;
  int i;
  int num;

  va_start(ap, nargs); // nargs, because is the last known argument
  for (i =0; i<nargs; i++)
    {
      num=va_arg(ap, int); // All arguments are int
      printf("Argument %d = %d\n", i, num);
    }
  va_end(ap);
  printf("\n");
}

int main(int argc, char *argv[])
{
  putargs(5, 6, 77, 445, 34, 23, 12, 43, 32);
  putargs(2, 23, 94);
  putargs(0);

  return EXIT_SUCCESS;
}

We can see here, function putargs() prints out values of possible arguments we’ve passed the function, being the first of them the number of arguments we have, because we have no way to count the number of arguments passed (like printf, we will use as many arguments as %[x] we have in the format string). So we will have to use different techniques to know the total number of arguments, like:

  • Ask the user, like we’ve done here, with an argument telling the function how many arguments to read.
  • Search for a value inside all arguments, we can read arguments until the value I found is 5 (for example). It is used so often when passing pointers to the function, I will read arguments until I get a NUL value.
  • Like printf() use a number of keywords, this will be the number of arguments

We just have to copy this sample code and make some test, have in mind when we start looking for arguments, we must call va_start() with a va_list variable and the last known argument we gave the function, then each time we call va_arg, we will get a different argument from the “unknown argument list”, that is the list of arguments starting by the three dots.

In the next example we will have a variable argument function used to fill a struct with people data. We will have the function new_person where we must say the name, but it will add data to the struct as you are adding arguments when you call the function, it’s fun and easy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#define MAX_STRLEN 64

struct TPersonData
{
  char name[MAX_STRLEN];
  char blog[MAX_STRLEN];
  char facebook[MAX_STRLEN];
  char twitter[MAX_STRLEN];
  char city[MAX_STRLEN];
  char drink[MAX_STRLEN];   /* Don't know anything else I can put for this example :) */
  char phone[MAX_STRLEN];
};

/* Remember always to clear data. Variable initialization is important if we don't
 want to see garbage. */

struct TPersonData cleanPerson()
{
  struct TPersonData p;
  strcpy(p.name, "");
  strcpy(p.blog, "");
  strcpy(p.facebook, "");
  strcpy(p.twitter, "");
  strcpy(p.city, "");
  strcpy(p.drink, "");
  strcpy(p.phone, "");

  return p;
}

struct TPersonData new_person (char *name, ...)
{
  va_list vl;
  char *temp;
  int num = 1;
  struct TPersonData out = cleanPerson();

  strcpy(out.name, name);

  va_start(vl, name);
 
  while ( (temp = va_arg(vl, char*))!=NULL)
    {
      switch (num++)
    {
    case 1:
      strcpy(out.blog, temp);
      break;
    case 2:
      strcpy(out.facebook, temp);
      break;
    case 3:
      strcpy(out.twitter, temp);
      break;
    case 4:
      strcpy(out.city, temp);
      break;
    case 5:
      strcpy(out.drink, temp);
      break;
    case 6:
      strcpy(out.phone, temp);
      break;
    default:
      break;
    }
    }
  va_end(vl);

  return out;
}

void printPerson(struct TPersonData data)
{
  printf ("Name: %s\n", data.name);
  printf ("Blog: %s\n", data.blog);
  printf ("Facebook: %s\n", data.facebook);
  printf ("Twitter: %s\n", data.twitter);
  printf ("City: %s\n", data.city);
  printf ("Drink: %s\n", data.drink);
  printf ("Phone: %s\n", data.phone);
  printf ("\n\n");
}

int main(int argc, char *argv[])
{
  struct TPersonData p = new_person("Gaspar Fernandez", "Binary Prose", NULL);
  printPerson(p);
 
  p = new_person("John Doe", "JD Blog", "http://facebook.com/johndoe", "@johndoe", "New York", NULL);
  printPerson(p);
  return EXIT_SUCCESS;
}

It’s important not to forget the NULL after all arguments are passed, or we will see some unexpected behavior in runtime.

Photo: Robyn Petrovich (Flickr) CC-by

Top