Inspired by the question. How does execlp work, what is the last NULL argument ? I looked through the answers to similar questions, but did not find a detailed description:

  1. Functions with a variable number of parameters
  2. How to declare a function with a variable number of arguments in C?
  3. C and a variable number of arguments

Etc.

PS It is assumed that the reader of this text has already graduated from kindergarten, and does not ask questions of the level “Have I given out any mistake here, what is this?”. And do not confuse C with C ++.

Kicks, cuffs and other indications of "you're wrong" are welcome.

  • Minusator, your comment would be interesting. - VladD
  • @VladD, did not understand :) "share knowledge ..." then what is it about? - PinkTux
  • No, it is interesting to me that the one who put a minus (it's not me), explained what is wrong in his opinion. - VladD 2:42 pm
  • @VladD, well, in that sense. Yes, all garbage. It is written more for itself, more for “packing”, “compressing” and “refreshing” the data in one’s own head. And public laying out is so, a test of the adequacy of the presentation :) Pros or cons do not care. - PinkTux

1 answer 1

In C, it is possible to declare and use functions with a variable number of arguments. This possibility is provided by the features of working with the stack when calling functions, but now they will not be considered in detail, only the practical side of the issue. The magic that happens in <stdarg.h> also outside the <stdarg.h> , curious, and will find an explanation for this shamanism.

The prototype of such a function might look like this, for example:

 void print_messages( const char * title, ... ); 

And the challenge is:

 print_messages( "Вот что имею сообщить", "это раз", "это два", "это три", NULL ); 

And print accordingly:

 Вот что имею сообщить: - это раз. - это два. - это три. 

Well, its implementation:

 void print_messages( const char * title, ... ) { va_list ap; const char * message; va_start( ap, title ); printf( "%s:\n", title ); message = va_arg( ap, const char * ); while( message ) { printf( " - %s.\n", message ); message = va_arg( ap, const char *); } va_end( ap ); } 

The most important issue here is determining the end of the list of arguments. In this case, NULL used to mark it. But this is not always acceptable. For example, when NULL is a valid argument value. Or, say, 0 / -1 in the case of integers.

The first solution to this problem is to pass the number of arguments to the first parameter:

 void print_numbers( size_t amount, ... ) { va_list ap; int number; va_start( ap, amount ); printf( "Total numbers: %zu, let's go! ", amount ); while( amount-- ) { number = va_arg( ap, int ); printf( " [%d]", number ); } va_end( ap ); } 

Call:

 print_numbers( 3, 11, 22, 33 ); 

This method can be used only when arguments of the same type are passed to the function. The same can be achieved by passing an array of values ​​with its size. But this is not always justified. Yes, and now considered a technological demo, and not expediency (which is always on the conscience of the programmer, but then /dev/brain attached to it).

Whisper: well, TMTOWTDI also happens in hi, hi-hi ...

And what if the arguments can be of different types? This is where a method called format comes to the rescue. Any sishnik is familiar with it by *printf* . But it is not interesting. We will invent our own, on the same principle.

We define:

  • the character 'c' means char
  • 's' - short
  • 'l' - long
  • 'z' - char *

Prototype:

 void print_something( const char * format, ... ); 

Implementation:

  void print_something(const char * fmt, ...) { va_list ap; va_start( ap, fmt ); /* Гусары, молчать! */ long l; int i; char c; char *z; while( *fmt ) { switch( *fmt ) { case 'c': /* см дальше */ c = (char)va_arg( ap, char ); printf( "char: '%c'\n", c ); break; case 's': /* см дальше */ s = (short)va_arg( ap, short ); printf( "short: '%d'\n", s ); break; case 'i': i = va_arg( ap, int ); printf( "int: '%d'\n", i ); break; case 'l': l = va_arg( ap, long ); printf( "long: '%lu'\n", l );; break; case 'z': z = va_arg( ap, char * ); printf( "char *: '%s'\n", z );; break; default: printf( "Хрен знает что передали: '%c'\n", *fmt ); break; } fmt++; } } 

Next: char , short and so on, which is less than int .

These lines excite the compiler:

 c = (char)va_arg( ap, char ); /* 'char' is promoted to 'int' when passed through '...' */ s = (short)va_arg( ap, short ); /* 'short' is promoted to 'int' when passed through '...' */ 

I will not draw conclusions, I will leave on a homework.