How can printf process variable parameters?

G

Googy

Hi friends!!

As we know that the input parameters in a function is fixed when the
function is defined but how does printf processes variable number of
input arguments ?

For example:

1. printf("Hey! how are you"); /*No. of arguments = 1*/
2. printf("Sum of %d and %d is %d",a,b,c"); /* No. of arguments = 4*/

I know it uses three macros va_start, va_arg, va_list ; but i don't
know how the stuff actually works...
Please explain.

Thanks in advance!!

Gaurav
 
C

Clever Monkey

Googy said:
Hi friends!!

As we know that the input parameters in a function is fixed when the
function is defined but how does printf processes variable number of
input arguments ?
[...]
I know it uses three macros va_start, va_arg, va_list ; but i don't
know how the stuff actually works...
Please explain.
Start here: <http://c-faq.com/varargs/index.html>

Most good C books will give you a gentle introduction to variable-length
argument lists. K&R references them a few times.
 
B

bert

Hi friends!!

As we know that the input parameters in a function is fixed when the
function is defined but how does printf processes variable number of
input arguments ?

For example:

1. printf("Hey! how are you"); /*No. of arguments = 1*/
2. printf("Sum of %d and %d is %d",a,b,c"); /* No. of arguments = 4*/

I know it uses three macros va_start, va_arg, va_list ; but i don't
know how the stuff actually works...
Please explain.

The printf() function is implementation-defined,
and therefore need not use these va_*** tokens.
It is "undefined behaviour" to pass arguments
which do not match, in their successive types and
total number, the field codes in the format string,
therefore the printf() function can be implemented
to assume that these codes can be used to derive
the number and type of the arguments, rather than
va_start etcetera.
--
 
L

Lew Pitcher

Hi friends!!

As we know that the input parameters in a function is fixed when the
function is defined but how does printf processes variable number of
input arguments ?

For example:

1. printf("Hey! how are you"); /*No. of arguments = 1*/
2. printf("Sum of %d and %d is %d",a,b,c"); /* No. of arguments = 4*/

I know it uses three macros va_start, va_arg, va_list ; but i don't
know how the stuff actually works...
Please explain.

It works by Magic.

Actually, it works by whatever mechanism the compiler writer chose to
use to implement va_start, et al. The mechanism is highly dependant on
the physical arrangement and location of arguments passed to a
function, and their format and promotions. That's why the macros are
made available; the underlying mechanisms are inherently non-portable,
and often obscure or convoluted.

One mechanism works only with a stack, and only if the arguments are
"pushed" onto the stack from right to left. With this mechanism, the
further "right" you go in the argument list, the higher the address on
the stack. To find any argument, you first need a starting point on
the stack, and then you need to know how much "up" the stack you have
to go, and how much /of/ the stack you have to inhale to make your
object up. In a case like that, the called function can take the
address of the left-most argument as the "base" or low point of the
stack. Knowing how big the object at this base point is, the function
can then compute the address of next object on the stack. Knowing the
size of /that/ object (by foreknowledge, or by parsing the contents of
the first object), the function can then compute the address of the /
next/ object. And so on. Now, you see why the standard makes this
opaque to the programmer, and gives him the va_* macros instead? It
needs a detailed understanding of the underlying parameter passing
implementation to work, and that's something that most application
programmers do not need.
 
J

jacob navia

Googy said:
Hi friends!!

As we know that the input parameters in a function is fixed when the
function is defined but how does printf processes variable number of
input arguments ?

For example:

1. printf("Hey! how are you"); /*No. of arguments = 1*/
2. printf("Sum of %d and %d is %d",a,b,c"); /* No. of arguments = 4*/

I know it uses three macros va_start, va_arg, va_list ; but i don't
know how the stuff actually works...
Please explain.

Thanks in advance!!

Gaurav

Easy one first.

If you take one vanilla implementation, like 32 bit windows
or 32 bit linux you have:
o A pointer to the start of the stack area where parameters are pushed
The macro va_start sets the va_list variable to point of the start
of the argument stack area.
o Then, each time you take out something from the va_list you increase
that pointer by sizeof(type) with appropiate rounding to a word size.

More difficult one:
Under the windows 64 operating system the first 4 arguments are
passed with the registers, no pushing into the stack. The above schema
would not work at all. Then, the va_list must point to a structure
where you have several pointers. One to the stack area where the
registers are saved, to pull possible values from them, then, a
pointer to the stack area where the arguments 5th to N are stored.
Interesting bugs can happen when you forget the 32 bytes of stack
offset that windows reserves, and the fact that floating point
registers are pulled from the SSE registers and integer/pointer
arguments are pulled from the integer registers.
Phew... took me a while.

HELL:
Absolute hell is of course, linux 64 bits. In that system, up
to 8 integer arguments can be passed in the integer registers,
up to 8 arguments can be passed in the floating point registers,
and the rest is passed in the stack... So, instead of just
looking at a single register save area you have to look to 2 of them
and maintain pointers to each one. Each pointer is incremented
independently, to the contrary of windows.

This is of course much more efficient schema than windows, but it is
pure hell for the compiler writer. Took me like a month of bug
after bug after bug.

For instance, structures are normally always passed in the stack, unless
they are smaller than a word...
 
J

jacob navia

bert said:
The printf() function is implementation-defined,
and therefore need not use these va_*** tokens.
It is "undefined behaviour" to pass arguments
which do not match, in their successive types and
total number, the field codes in the format string,
therefore the printf() function can be implemented
to assume that these codes can be used to derive
the number and type of the arguments, rather than
va_start etcetera.

That would be quite a crazy strategy... Why wouldn't printf
use the va_start macros???

Do you have any implementation example at hand?

Or a description how could that work?
 
?

=?iso-2022-kr?q?=1B=24=29CHarald_van_D=0E=29=26=0F

That would be quite a crazy strategy... Why wouldn't printf use the
va_start macros???

Do you have any implementation example at hand?

Or a description how could that work?

What if the implementation of the C library is written in assembly?
 
R

Richard Tobin

That would be quite a crazy strategy... Why wouldn't printf use the
va_start macros???

Do you have any implementation example at hand?

Or a description how could that work?
[/QUOTE]
What if the implementation of the C library is written in assembly?

Even if it were written in assembler, it would be strange for it to
be detectably different from a version written using va_*.

It's been suggested here recently that it might even use an
incompatible argument passing protocol, so that the guarantees for
normal stdarg functions would not apply (in particular, it's been
suggested that you can't reliably pass a char * for a %p item). That
would certainly be crazy. printf() should work *as if by* the usual
protocol.

-- Richard
 
?

=?iso-2022-kr?q?Harald_van_D=0E=29=26=0Fk?=


Could you please leave in the attribution notices? The below text was
written by jacob navia.
Even if it were written in assembler, it would be strange for it to be
detectably different from a version written using va_*.

Agreed. I was merely giving a reason why it might not use <stdarg.h>. I
have given a possible reason why it would not behave as if implemented
using <stdarg.h> elsewhere, but I know it takes more effort to write a
conforming implementation where printf("%u\n", 10) would fail, than it
would to write one where it will work.
It's been suggested here recently that it might even use an incompatible
argument passing protocol, so that the guarantees for normal stdarg
functions would not apply (in particular, it's been suggested that you
can't reliably pass a char * for a %p item). That would certainly be
crazy. printf() should work *as if by* the usual protocol.

It's not allowed to use an incompatible argument passing protocol (except
under the "as if" rule). You can safely assign printf to a properly
declared function pointer, and then call it from a unit where knowledge
that printf is present in the project at all is not available. It's
allowed to use an incompatible argument *retrieval* protocol, but except
for an extremely strict debugging implementation, it would make
absolutely no sense to do so.
 
R

Richard Tobin

Harald van D )& k said:
It's not allowed to use an incompatible argument passing protocol (except
under the "as if" rule). You can safely assign printf to a properly
declared function pointer, and then call it from a unit where knowledge
that printf is present in the project at all is not available. It's
allowed to use an incompatible argument *retrieval* protocol, but except
for an extremely strict debugging implementation, it would make
absolutely no sense to do so.

It's quite hard to imagine a compatible argument passing protocol
without a compatible argument retrieval protocol. If the caller does
the same thing with char * and void *, how is the callee (printf())
going to get it wrong?

Quite hard, but not impossible... I suppose the caller could, say,
store void * arguments in two locations, but char * in only one, and
have printf() extract the argument from the extra location. I vaguely
remember something about 68000s returning pointers in both address and
data registers (Chris Torek?) but in this case it would be entirely
gratuitous.

Do you think it was really the intention of the standard authors to
allow such an implementation?

-- Richard
 
?

=?iso-2022-kr?q?=1B=24=29CHarald_van_D=0E=29=26=0F

It's quite hard to imagine a compatible argument passing protocol
without a compatible argument retrieval protocol. If the caller does
the same thing with char * and void *, how is the callee (printf())
going to get it wrong?

The compiler might have built-in knowledge for *printf, and cause _any_
type mismatch to cause a nonfatal compile-time diagnostic and a fatal
runtime diagnostic. Meaning:

#include <stdio.h>
int main(void) {
printf("%d\n", "Hello");
printf("%d\n", 1u);
}

would generate diagnostics such as

example.c:3:type mismatch in argument to 'printf', will abort at runtime
example.c:4:type mismatch in argument to 'printf', will abort at runtime

Or, type info could be passed to the function along with type arguments,
and *printf would verify for exact type matches, while va_arg would
contain extra code to allow for sloppy matches.
Quite hard, but not impossible... I suppose the caller could, say, store
void * arguments in two locations, but char * in only one, and have
printf() extract the argument from the extra location.

That's another possibility.
I vaguely
remember something about 68000s returning pointers in both address and
data registers (Chris Torek?) but in this case it would be entirely
gratuitous.

This is true.
Do you think it was really the intention of the standard authors to
allow such an implementation?

No, I don't think so. I do believe the special permissions on va_arg are
intended for compatibility only, but I would be surprised if they were
not intended to be permitted in any variadic function call.
 
J

Jack Klein

Hi friends!!

As we know that the input parameters in a function is fixed when the
function is defined

No, we don't know this. You may think you know this, but you are
wrong. If you were right, the *printf() and *scanf() functions would
not be a part of the standard C library. And the <stdarg.h> header
would be useless.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top