开发者

How to use "..." (variable) argument? [duplicate]

开发者 https://www.devze.com 2023-04-02 12:06 出处:网络
This question already has answers here: Closed 11 years ago. 开发者_开发知识库 Possible Duplicate:
This question already has answers here: Closed 11 years ago. 开发者_开发知识库

Possible Duplicate:

What are variadic functions in accordance with C and C++?

I've seen ... argument in printf() function. Exactly HOW functions like printf or scanf work? How is it that they can have infinite input values?


This relies on the C calling convention, which is that the caller pops the arguments from the stack.

Because it's the caller that knows how many arguments (and what size they are) it must communicate this to the callee in some manner. With printf() and family, this is via the format string. With execv and family, this is indicated by having a terminating NULL parameter.

The callee reads the arguments from the stack using the macros defined in the standard header stdarg.h.

From memory (sketchy), you need to define a variable of type va_list, which is initialised from the preceeding argument using va_start, as in:

void print(const char* format, ...)
{
  va_list args;

  va_start(args, format);


  /* read an int */
  int i = va_arg(args, int);

  /* read an char* */
  char* pc = va_arg(args, char*);


  va_end(args);
}

Obviously, you have to parse the format string to know whether to read an int, or a char*, or a double, or a ...

HTH


For that you have to know a bit about the underlying function call and argument passing on the stack.

When you call a function, a few things need to be written on the stack, such as return address, pointer to previous stack frame etc. Another part written on the stack consists of the arguments of the function. In C, the arguments are pushed on the stack from right to left (unlike for example Pascal). This way, the first arguments of the function is on the top of the arguments list on the stack. (This is not mandated by the standard, but is what happens in reality).

Now, the way printf and scanf work is quite simple. They get a string as the first argument (which is on the top of the stack (I mean arguments list on the stack, but I write on the top of stack just for short)). Guided by that string, they try to retrieve other values from below where the format string is (on the stack). So, for example, if you call:

printf("%d %c %s\n", 0x12345678, 'a', "str");

What printf sees on the stack is (top of the stack is on the left, assuming int = 4 bytes, little-endian and 64-bit addresses):

<address of format string (8 bytes)>|0x78|0x56|0x34|0x12|0x61|0x00|0x00|0x00|<address of str (8 bytes)>

So, what it does is read the format string, reach %d, then read 4 bytes from below the address of format string (so it reads the |0x78|0x56|0x34|0x12| part) then sees %c and reads four character from after that (the |0x61|0x00|0x00|0x00) and interpret it as a character etc. (The reason %c reads four characters instead of one, is that char (as well as short) sent through ... is automatically cast to int)

You should note however that printf and scanf do this blindly. Therefore, if you send a double as argument of printf and read %d, it reads the same number of bytes (4), but interprets the bits as an int and not double --> What you get is gibberish. Also, if you have many %s (like %d, %c etc) but not enough arguments, printf and scanf blindly go over the other stack variables and interpret them as data/pointers.

Finally, good compilers nowadays read the format string for you and give you a warning if there is a mismatch between number of expected arguments in the format string and the actual arguments sent (or their types). gcc and clang even let you declare a function as following the same formatting of printf and scanf, so you can have this added protection on your custom functions as well.


Here are some useful links:

http://en.wikipedia.org/wiki/Variadic_function

http://www.gnu.org/s/hello/manual/libc/Variadic-Functions.html

http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=138

Hope these help you.

Also, please take some time to search the web for these questions.


manual to va_*

Technically the function being called must have the exact information about the arguments it is called with in each particular case (in case of *printf() the information is passed in form of format string). Having such information the function can extract the arguments from its stack frame using trivial pointer arithmetic.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号