Typecast of one argument to printf impacts other ???

Discussion in 'C Programming' started by nandh_dp@yahoo.co.in, Oct 15, 2005.

  1. Guest

    When the below program is compiled and executed,

    #include <stdio.h>

    #define MASK 0xFFFFFFULL

    main() {
    unsigned long long a;
    unsigned long b, c;

    a = 0x12345678ULL;
    b = a & MASK;
    c = 0x00345678UL;

    printf("%d %d\n", sizeof(unsigned long), sizeof(unsigned long
    long));
    printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    a & MASK, a & MASK, b, b, c, c);

    }

    the result from a BigEndian machine is

    4 8
    0x0 3430008 0x0 3430008 0x345678 3430008

    Surprisingly, the result of 'a & MASK' and 'b' are 0x0, when 0x%x
    format is used.

    And the result from a Little Endian machine is

    4 8
    0x345678 0 0x345678 0 0x345678 3430008

    Again more surprisingly, the results are 0 when %lu is used. Why these
    unexpected results ?

    Also, when the printf is changed like this,

    printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    (unsigned long)(a & MASK), a & MASK, b, b, c, c);

    the result is

    0x345678 0 0x345678 3430008 0x345678 3430008

    in a Big Endian machine and

    0x345678 3430008 0x0 3430008 0x345678 3430008

    in a little endian.

    And with the below

    printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    (unsigned long)(a & MASK), (unsigned long)(a & MASK), b, b,
    c, c);

    the results are as expected

    0x345678 3430008 0x345678 3430008 0x345678 3430008

    in both the machines.

    How does typecasting of one argument affect printed values of others ?
    Or is there anyother reason behind it and what I see is a misconception
    of some hidden fact ?
    , Oct 15, 2005
    #1
    1. Advertising

  2. Alipha Guest

    wrote:
    > When the below program is compiled and executed,
    >
    > #include <stdio.h>
    >
    > #define MASK 0xFFFFFFULL
    >
    > main() {
    > unsigned long long a;
    > unsigned long b, c;
    >
    > a = 0x12345678ULL;
    > b = a & MASK;
    > c = 0x00345678UL;
    >
    > printf("%d %d\n", sizeof(unsigned long), sizeof(unsigned long
    > long));
    > printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    > a & MASK, a & MASK, b, b, c, c);


    For the first vararg parameter, you're passing an unsigned long long
    when the corresponding format specifier, %x, expects an unsigned int.
    For the second vararg parameter, you're passing an unsigned long long
    when %lu expects an unsigned long. Mismatching format specifiers with
    the parameter types is *undefined behavior*. The compiler is free to do
    ANYTHING IT WISHES.

    However, I suppose you want a practical, "what is most-likely happening
    behind the scenes" answer. Okay, here is the most-likely reason:

    In a typical implementation, when you call printf, the parameters are
    pushed onto the stack from right to left, and so, your stack looks like
    this:

    (separated into 4-byte groups -- big endian)
    00345678 c -- bottom of stack
    00345678 c
    00345678 b
    00345678 b
    00345678 a & MASK (least significant 4 bytes)
    00000000 (most significant 4 bytes)
    00345678 a & MASK
    00000000
    ptr_to_format_string -- top of stack (popped first)

    And so, printf pops off the pointer to the format string and starts
    parsing the format string. It encounters an %x, which means to display
    the *unsigned int* parameter in hexadecimal format. And so, since an
    unsigned int is likely to be 4 bytes on your platform, 4 bytes are
    popped off the stack, and so 00000000 is popped and represented in
    hexadecimal. Then %lu is encountered and since it expects an unsigned
    long, which is 4 bytes on your platform, 4 bytes are popped, which is
    00345678 in hex, or 3430008 in decimal. etc.

    Solution: use the right format specifiers: %llx and %llu

    > }
    >
    > the result from a BigEndian machine is
    >
    > 4 8
    > 0x0 3430008 0x0 3430008 0x345678 3430008
    >
    > Surprisingly, the result of 'a & MASK' and 'b' are 0x0, when 0x%x
    > format is used.
    >
    > And the result from a Little Endian machine is
    >
    > 4 8
    > 0x345678 0 0x345678 0 0x345678 3430008
    >
    > Again more surprisingly, the results are 0 when %lu is used. Why these
    > unexpected results ?
    >
    > Also, when the printf is changed like this,
    >
    > printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    > (unsigned long)(a & MASK), a & MASK, b, b, c, c);
    >
    > the result is
    >
    > 0x345678 0 0x345678 3430008 0x345678 3430008
    >
    > in a Big Endian machine and
    >
    > 0x345678 3430008 0x0 3430008 0x345678 3430008
    >
    > in a little endian.
    >
    > And with the below
    >
    > printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    > (unsigned long)(a & MASK), (unsigned long)(a & MASK), b, b,
    > c, c);
    >
    > the results are as expected
    >
    > 0x345678 3430008 0x345678 3430008 0x345678 3430008
    >
    > in both the machines.
    >
    > How does typecasting of one argument affect printed values of others ?
    > Or is there anyother reason behind it and what I see is a misconception
    > of some hidden fact ?
    Alipha, Oct 15, 2005
    #2
    1. Advertising

  3. On 15 Oct 2005 05:19:02 -0700, wrote:

    >When the below program is compiled and executed,
    >
    >#include <stdio.h>
    >
    >#define MASK 0xFFFFFFULL
    >
    >main() {
    > unsigned long long a;
    > unsigned long b, c;
    >
    > a = 0x12345678ULL;
    > b = a & MASK;
    > c = 0x00345678UL;
    >
    > printf("%d %d\n", sizeof(unsigned long), sizeof(unsigned long
    >long));


    sizeof evaluates to a size_t which is an unsigned integer of some
    type. %d requires an int so you have potential undefined behavior
    here. A common recommendation is to use %lu and cast the sizeof to
    unsigned long.

    > printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    > a & MASK, a & MASK, b, b, c, c);


    Here have more undefined behavior.

    %x requires an unsigned int. a & MASK is an unsigned long long.

    %lu requires an unsigned long. a & MASK is still an unsigned long
    long.

    %x requires an unsigned int. b is an unsigned long. So is c.

    >
    >}
    >
    >the result from a BigEndian machine is
    >
    >4 8
    >0x0 3430008 0x0 3430008 0x345678 3430008
    >
    >Surprisingly, the result of 'a & MASK' and 'b' are 0x0, when 0x%x
    >format is used.


    You cannot lie to printf and expect things to work, especially since
    you know that unsigned long and unsigned long long have different
    sizes.

    >
    >And the result from a Little Endian machine is
    >
    >4 8
    >0x345678 0 0x345678 0 0x345678 3430008
    >
    >Again more surprisingly, the results are 0 when %lu is used. Why these
    >unexpected results ?


    You cannot lie to printf and expect things to work, especially since
    you know that unsigned long and unsigned long long have different
    sizes.

    >
    >Also, when the printf is changed like this,
    >
    > printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    > (unsigned long)(a & MASK), a & MASK, b, b, c, c);
    >
    >the result is
    >
    >0x345678 0 0x345678 3430008 0x345678 3430008
    >
    >in a Big Endian machine and
    >
    >0x345678 3430008 0x0 3430008 0x345678 3430008
    >
    >in a little endian.
    >
    > And with the below
    >
    > printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    > (unsigned long)(a & MASK), (unsigned long)(a & MASK), b, b,
    >c, c);
    >
    >the results are as expected
    >
    >0x345678 3430008 0x345678 3430008 0x345678 3430008
    >
    >in both the machines.
    >
    >How does typecasting of one argument affect printed values of others ?
    >Or is there anyother reason behind it and what I see is a misconception
    >of some hidden fact ?


    Welcome to the wonderful world of undefined behavior. One
    manifestation is "do something to really confuse the user."


    <<Remove the del for email>>
    Barry Schwarz, Oct 15, 2005
    #3
  4. wrote:
    > When the below program is compiled and executed,



    /* Your program is hopelessly broken. Learn to use specifiers that
    match your data types. Also learn that sizeof() returns an unsigned
    value. Note the changes below. As a style isssue, using tabs for
    output meant to be human-readable is a mistake. All of your
    questions are related to your errors. */
    #include <stdio.h>

    #define MASK 0xFFFFFFULL

    int main(void)
    {
    unsigned long long a;
    unsigned long b, c;

    a = 0x12345678ULL;
    b = a & MASK;
    c = 0x00345678UL;

    printf("%d %d\n", (int) sizeof(unsigned long),
    (int) sizeof(unsigned long long));
    printf("%#llx %llu; %#lx %lu; %#lx %lu\n", a & MASK, a & MASK, b,
    b, c, c);
    return 0;

    }


    [output]

    4 8
    0x345678 3430008; 0x345678 3430008; 0x345678 3430008

    >
    > #include <stdio.h>
    >
    > #define MASK 0xFFFFFFULL
    >
    > main() {
    > unsigned long long a;
    > unsigned long b, c;
    >
    > a = 0x12345678ULL;
    > b = a & MASK;
    > c = 0x00345678UL;
    >
    > printf("%d %d\n", sizeof(unsigned long), sizeof(unsigned long
    > long));
    > printf("0x%x %lu \t 0x%x %lu \t 0x%x %lu \n",
    > a & MASK, a & MASK, b, b, c, c);
    >
    > }
    Martin Ambuhl, Oct 15, 2005
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ben
    Replies:
    4
    Views:
    614
    Martin Ambuhl
    Jun 26, 2004
  2. S?ren Gammelmark
    Replies:
    1
    Views:
    1,884
    Eric Sosman
    Jan 7, 2005
  3. Harish
    Replies:
    9
    Views:
    611
    Daniel Pitts
    May 9, 2007
  4. Luigi

    Space problems: impacts

    Luigi, Mar 25, 2008, in forum: ASP .Net
    Replies:
    4
    Views:
    267
    Luigi
    Mar 26, 2008
  5. ML
    Replies:
    0
    Views:
    320
Loading...

Share This Page