Compiler question

  • This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn more.
#1
This does what I want, namely, gives a correct string.

Code:
#define DW (pDnsRecord->Data.A.IpAddress) // a DWORD, network order
Sprintf(psz, L"%u.%u.%u.%u", *((BYTE*)&DW) , *((BYTE*)&DW+1) , *((BYTE*)&DW+2) , *((BYTE*)&DW+3));
#undef DW
But I don't quite understand how it works (functions with variable argument lists in general). The compiler can't be simply giving pointers to the 4 bytes in DW to Sprintf() because Sprintf() can't know that I want only bytes. So what's the compiler doing?

Thanks.
 

samintz

Scott Mintz
May 20, 2008
1,203
11
Solon, OH, USA
#2
In a C-style or cdecl function the arguments are pushed onto the stack in
reverse order. In that way the offset within the stack frame of the first
argument is always known. The called function parses the arguments off
the stack as needed. If I pull two bytes from the stack for arg1, then I
need to pull arg2 at offset 2 from that. And so on. Take a look at the
va_arg functions.

Also, in a cdecl function, the caller is responsible for cleaning up the
stack. For example I can call printf("%u",1,2,3,4,5,6,7,8,9); and that is
perfectly ok. The callee (printf) would only use the 1st two arguments
(format string and 1) but the caller knows how many items it pushed and
cleans up the stack correctly. In a stdcall function, the arguments are
pushed in order and the callee cleans up before returning.

Your format string instructs the printf function how to pull the arguments
from the stack. There are also ANSI rules for automatically making things
correct sizes when pushed onto the stack. So, in your example, a BYTE
gets zero extended to WORD and pushed onto the stack. %u expects a WORD
argument.

-Scott

vefatica <> wrote on 06/05/2010 04:36:11 PM:


> This does what I want, namely, gives a correct string.
>
>
> Code:
> ---------
> #define DW (pDnsRecord->Data.A.IpAddress) // a DWORD, network order
> Sprintf(psz, L"%u.%u.%u.%u", *((BYTE*)&DW) , *((BYTE*)&DW+1) ,
> *((BYTE*)&DW+2) , *((BYTE*)&DW+3));
> #undef DW
> ---------
> But I don't quite understand how it works (functions with variable
> argument lists in general). The compiler can't be simply giving
> pointers to the 4 bytes in DW to Sprintf() because Sprintf() can't
> know that I want only bytes. So what's the compiler doing?
>
> Thanks.
>
>
>
>
 
#3
On Sat, 05 Jun 2010 17:08:44 -0400, samintz <> wrote:

|Your format string instructs the printf function how to pull the arguments
|from the stack. There are also ANSI rules for automatically making things
|correct sizes when pushed onto the stack. So, in your example, a BYTE
|gets zero extended to WORD and pushed onto the stack. %u expects a WORD
|argument.

|---Quote---
|> This does what I want, namely, gives a correct string.
|>
|>
|> Code:
|> ---------
|> #define DW (pDnsRecord->Data.A.IpAddress) // a DWORD, network order
|> Sprintf(psz, L"%u.%u.%u.%u", *((BYTE*)&DW) , *((BYTE*)&DW+1) ,
|> *((BYTE*)&DW+2) , *((BYTE*)&DW+3));
|> #undef DW
|> ---------

Actually, "u" means "unsigned" to Sprintf (and AFAICT works with all sizes up to
32). And why would the compiler decide on WORD? I told it BYTE! But it can't
push 1 byte onto the stack; there's nothing signalling Sprintf to pop 1 byte. I
figure the compiler must be pushing a 32-bit value onto the stack and Sprintf
pops it and treats it according to the format element. eh?
--
- Vince
 
#4
>
> Actually, "u" means "unsigned" to Sprintf (and AFAICT works with all sizes
> up to
> 32). And why would the compiler decide on WORD? I told it BYTE! But it
> can't
> push 1 byte onto the stack; there's nothing signalling Sprintf to pop 1
> byte. I
> figure the compiler must be pushing a 32-bit value onto the stack and
> Sprintf
> pops it and treats it according to the format element. eh?
>
>
Sprintf doesn't pop anything, and doesn't know what or how much was pushed,
but instead the caller pushes X items and then pops them back off after
Sprintf returns. According to n1124.pdf (the C standard), 7.1.4, "If an
argument to a function has ... a type (after promotion) not expected by a
function with variable number of arguments, the behavior is undefined." The
"after promotion" is key here. When you call with a char or word or int or
long parameter, the (32-bit) C compiler pushes 32 bits in each case. That's
why your %u works, because %hu and %u and %lu all (in this compiler) use 32
bits from the stack but may represent the output differently.

I didn't find the specific mention in the standard where the promotions of
arguments to functions are described.

If Sprintf did pop things based on the format string, doing Sprintf("%u %u",
1) or Sprintf("%u %u", 1, 2, 3) would both cause stack faults, and neither
will. The first one is undefined behavior, but will probably just display
left over stack data.

--
Jim Cook
2010 Sundays: 4/4, 6/6, 8/8, 10/10, 12/12 and 5/9, 9/5, 7/11, 11/7.
Next year they're Monday.
 
#5
On Sat, 05 Jun 2010 22:48:22 -0400, Jim Cook <> wrote:

|---Quote---
|>
|> Actually, "u" means "unsigned" to Sprintf (and AFAICT works with all sizes
|> up to
|> 32). And why would the compiler decide on WORD? I told it BYTE! But it
|> can't
|> push 1 byte onto the stack; there's nothing signalling Sprintf to pop 1
|> byte. I
|> figure the compiler must be pushing a 32-bit value onto the stack and
|> Sprintf
|> pops it and treats it according to the format element. eh?
|>
|>
|---End Quote---
|Sprintf doesn't pop anything, and doesn't know what or how much was pushed,
|but instead the caller pushes X items and then pops them back off after
|Sprintf returns. According to n1124.pdf (the C standard), 7.1.4, "If an
|argument to a function has ... a type (after promotion) not expected by a
|function with variable number of arguments, the behavior is undefined." The
|"after promotion" is key here. When you call with a char or word or int or
|long parameter, the (32-bit) C compiler pushes 32 bits in each case. That's
|why your %u works, because %hu and %u and %lu all (in this compiler) use 32
|bits from the stack but may represent the output differently.

OK, that makes sense. What do you suppose happens when an arg is 64-bits (say
ULONGLONG) and the format is "%I64u"? ... a 32-bit pointer is pushed (and
Sprintf acts as if it's getting a pointer)?
--
- Vince
 
#6
On Sat, Jun 5, 2010 at 9:31 PM, vefatica <> wrote:


> OK, that makes sense. What do you suppose happens when an arg is 64-bits
> (say
> ULONGLONG) and the format is "%I64u"? ... a 32-bit pointer is pushed (and
> Sprintf acts as if it's getting a pointer)?
>

I don't believe that Sprintf dereferences any pointers unless %s is passed
in.

First, I the C standard allows for considerable lattitude in exactly what
happens in the details, but imposes requirements on the results, so my
statements here apply to the Microsoft compilers that I'm familiar with.
Certainly don't fall into the trap and expect Sprintf("%u %u", func1(),
func2()) will actually call either function before the other -- that is
specifically left implementation defined. Some compilers and/or switches
will pass things in registers instead of on a stack in cases as well.
Specific knowledge in this case is only of academic interest and the
standard requires that code does not rely on it.

Ok, on to the answer.

It pushes 64 bits in that case. If you have a 64-bit var v64 and do
Sprintf("%08lX%08lX %016I64X", v64, v64) you'll see the same result in both
cases. I've done this in my 16-bit compiler which was unaware of 64-bit
quantities and I had to simulate them.

To be portable, Sprintf and the family use the va_list / va_arg mechanism,
which simply index into the stack to get their arguments. I've written
plenty of routines that use that myself. va_list returns a pointer into
stack memory where the first argument was pushed. Using va_arg dereferences
that pointer after typecasting to what you said you were expecting, then
advances the pointer by the right amount, which is almost always 4 in this
compiler, but may be larger for ULONGLONG or double or directly pushed
structures (as opposed to a pointer to a structure).

--
Jim Cook
2010 Sundays: 4/4, 6/6, 8/8, 10/10, 12/12 and 5/9, 9/5, 7/11, 11/7.
Next year they're Monday.
 
#7
On Sun, 06 Jun 2010 09:27:24 -0400, Jim Cook <> wrote:

|To be portable, Sprintf and the family use the va_list / va_arg mechanism,
|which simply index into the stack to get their arguments. I've written
|plenty of routines that use that myself. va_list returns a pointer into
|stack memory where the first argument was pushed. Using va_arg dereferences
|that pointer after typecasting to what you said you were expecting, then
|advances the pointer by the right amount, which is almost always 4 in this
|compiler, but may be larger for ULONGLONG or double or directly pushed
|structures (as opposed to a pointer to a structure).

Thanks. I think I get it now.
--
- Vince
 

samintz

Scott Mintz
May 20, 2008
1,203
11
Solon, OH, USA
#8
Sorry, my description was from the older 16-bit integer days. Today,
smaller types like 8-bit and 16-bit values get either sign extended or
zero extended depending on whether signed or unsigned and pushed onto the
stack. %u pulls a 32-bit unsigned value from the stack.

-Scott

vefatica <> wrote on 06/05/2010 05:29:09 PM:


> On Sat, 05 Jun 2010 17:08:44 -0400, samintz <> wrote:
>
> |Your format string instructs the printf function how to pull the
arguments

> |from the stack. There are also ANSI rules for automatically making
things

> |correct sizes when pushed onto the stack. So, in your example, a BYTE
> |gets zero extended to WORD and pushed onto the stack. %u expects a
WORD

> |argument.
>
> |---Quote---
> |> This does what I want, namely, gives a correct string.
> |>
> |>
> |> Code:
> |> ---------
> |> #define DW (pDnsRecord->Data.A.IpAddress) // a DWORD, network order
> |> Sprintf(psz, L"%u.%u.%u.%u", *((BYTE*)&DW) , *((BYTE*)&DW+1) ,
> |> *((BYTE*)&DW+2) , *((BYTE*)&DW+3));
> |> #undef DW
> |> ---------
>
> Actually, "u" means "unsigned" to Sprintf (and AFAICT works with all
> sizes up to
> 32). And why would the compiler decide on WORD? I told it BYTE!
> But it can't
> push 1 byte onto the stack; there's nothing signalling Sprintf to
> pop 1 byte. I
> figure the compiler must be pushing a 32-bit value onto the stack and
Sprintf

> pops it and treats it according to the format element. eh?
> --
> - Vince
>
>
>
>
 

samintz

Scott Mintz
May 20, 2008
1,203
11
Solon, OH, USA
#9
I would expect I64 or U64 to push two 32 bit values onto the stack. Those
are native data types from the compiler's perspective. The printf format
specifier will pull a 64 bit value from the stack. You'll notice I said
pull and not pop. The va_arg functions essentially create a pointer into
the stack. And the data is then obtained by dereferencing the pointer.

-Scott

vefatica <> wrote on 06/06/2010 12:31:14 AM:


> On Sat, 05 Jun 2010 22:48:22 -0400, Jim Cook <> wrote:
>
> |---Quote---
> |>
> |> Actually, "u" means "unsigned" to Sprintf (and AFAICT works with all
sizes

> |> up to
> |> 32). And why would the compiler decide on WORD? I told it BYTE! But
it

> |> can't
> |> push 1 byte onto the stack; there's nothing signalling Sprintf to pop
1

> |> byte. I
> |> figure the compiler must be pushing a 32-bit value onto the stack and
> |> Sprintf
> |> pops it and treats it according to the format element. eh?
> |>
> |>
> |---End Quote---
> |Sprintf doesn't pop anything, and doesn't know what or how much was
pushed,

> |but instead the caller pushes X items and then pops them back off after
> |Sprintf returns. According to n1124.pdf (the C standard), 7.1.4, "If an
> |argument to a function has ... a type (after promotion) not expected by
a

> |function with variable number of arguments, the behavior is undefined."
The

> |"after promotion" is key here. When you call with a char or word or int
or

> |long parameter, the (32-bit) C compiler pushes 32 bits in each case.
That's

> |why your %u works, because %hu and %u and %lu all (in this compiler)
use 32

> |bits from the stack but may represent the output differently.
>
> OK, that makes sense. What do you suppose happens when an arg is
64-bits (say

> ULONGLONG) and the format is "%I64u"? ... a 32-bit pointer is pushed
(and

> Sprintf acts as if it's getting a pointer)?
> --
> - Vince
>
>
>
>