printf parameter type wrangling

Discussion in 'C Programming' started by Michael B Allen, Aug 21, 2005.

  1. I'm printing tables of structure members in a terminal and I would like
    to use printf with an arbitrary pointer so that the code is smaller and
    I don't need to switch over ever possible type.

    But the question is - can I pass an arbitrary type (short, double, char *,
    etc) to printf cast as a char * (provided the specified format string is
    appropriate of course)?

    I chose 'char *' because I reasoned it (a pointer) would large enough
    to accommodate any primitive type passed as a function parameter.

    #include <stdlib.h>
    #include <stddef.h>
    #include <stdio.h>

    void
    printf_member(void *obj, size_t off, const char *fmt)
    {
    char **p;

    p = (char **)((char *)obj + off);

    printf(fmt, *p);
    }

    struct foo {
    char c;
    float f;
    int i;
    char *s;
    };

    int
    main(void)
    {
    struct foo f = { 'c', 2.34f, 100, "hello, world" };

    printf_member(&f, offsetof(struct foo, c), "[c]\n");
    printf_member(&f, offsetof(struct foo, f), "[%f]\n");
    printf_member(&f, offsetof(struct foo, i), "[%d]\n");
    printf_member(&f, offsetof(struct foo, s), "[%s]\n");

    return 0;
    }

    Unfortunately this technique does not appear to work as the float member
    was not printed properly. Why?

    $ gcc -Wall -W -ansi -pedantic -o pa pa.c
    $ ./pa
    [c]
    [0.000000]
    [100]
    [hello, world]

    Thanks,
    Mike
    Michael B Allen, Aug 21, 2005
    #1
    1. Advertising

  2. Michael B Allen wrote on 21/08/05 :
    > I'm printing tables of structure members in a terminal and I would like
    > to use printf with an arbitrary pointer so that the code is smaller and
    > $ gcc -Wall -W -ansi -pedantic -o pa pa.c
    > $ ./pa
    > [c]
    > [0.000000]
    > [100]
    > [hello, world]


    Sounds too complicated to me... This is how I do :

    #include <stdlib.h>
    #include <stddef.h>
    #include <stdio.h>

    #define FMT_S "%-5s"

    #define PRT_C(struct_, field_) \
    printf (FMT_S" = '%c'\n" \
    , #struct_ "." #field_ \
    , struct_.field_)

    #define PRT_F(struct_, field_) \
    printf (FMT_S" = %f\n" \
    , #struct_ "." #field_ \
    , struct_.field_)

    #define PRT_I(struct_, field_) \
    printf (FMT_S" = %d\n" \
    , #struct_ "." #field_ \
    , struct_.field_)

    #define PRT_S(struct_, field_) \
    printf (FMT_S" = '%s'\n" \
    , #struct_ "." #field_ \
    , struct_.field_)

    struct foo
    {
    char c;
    float f;
    int i;
    char *s;
    };

    int main (void)
    {
    struct foo f =
    {'c', 2.34f, 100, "hello, world"};

    PRT_C (f, c);
    PRT_F (f, f);
    PRT_I (f, i);
    PRT_S (f, s);

    return 0;
    }

    Output :

    f.c = 'c'
    f.f = 2.340000
    f.i = 100
    f.s = 'hello, world'

    --
    Emmanuel
    The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
    The C-library: http://www.dinkumware.com/refxc.html

    ..sig under repair
    Emmanuel Delahaye, Aug 21, 2005
    #2
    1. Advertising

  3. In article <>,
    Michael B Allen <> wrote:

    >But the question is - can I pass an arbitrary type (short, double, char *,
    >etc) to printf cast as a char * (provided the specified format string is
    >appropriate of course)?


    No.

    >I chose 'char *' because I reasoned it (a pointer) would large enough
    >to accommodate any primitive type passed as a function parameter.


    There are two problems here.

    First, you are accessing a char * at a place in the struct that does
    not necessarily have one. If the object at that location is smaller
    than a char * (a char or short perhaps), then you will get some extra
    rubbish from another part of the struct or perhaps some (arbitrary)
    padding. If it is larger (a double perhaps), you won't get all of it.

    Even if the thing there is the same size as a char *, you might (on
    some architectures) get some bit pattern that causes an error when
    treated as a char * (e.g. when loaded into a address register).
    This doesn't happen on most architectures, but might in theory.

    Second, you are passing a char * to printf when it expects something
    else, and the something else may not be passed in the same way as a
    char *. That is almost certainly the cause of the behaviour you're
    seeing. If a real float were passed to printf, it would be promoted to
    a double, and printf will access a double-sized object at the location
    where you have passed only a char *-sized one.

    Your char * member works (because it *is* a char *). Your int member
    happens to work because int is the same size as char * (and it
    wouldn't have been promoted to anything, being an int). And your char
    member works because you forgot the % sign and the char happens to be
    'c':

    > struct foo f = { 'c', 2.34f, 100, "hello, world" };
    >
    > printf_member(&f, offsetof(struct foo, c), "[c]\n");

    ^ should be %c

    The only way to get this to work is to pass printf an argument of the
    right type. Since you have to pass in something indicating the right
    format for the type, you could instead pass some indication of the
    type (e.g. using an enum) and then have a switch in printf_member that
    covers all the possible cases.

    -- Richard
    Richard Tobin, Aug 21, 2005
    #3
  4. On Sun, 21 Aug 2005 21:09:37 +0000, Richard Tobin wrote:

    > The only way to get this to work is to pass printf an argument of the
    > right type. Since you have to pass in something indicating the right
    > format for the type, you could instead pass some indication of the
    > type (e.g. using an enum) and then have a switch in printf_member that
    > covers all the possible cases.


    Drat! Ok. I'll have to use a big switch. I have the type information in
    a parallel 'cols' array (stored with the format specifier). I was just
    exercising my minimalist instincts.

    Thanks for the quick answers as always,
    Mike
    Michael B Allen, Aug 21, 2005
    #4

  5. > Drat! Ok. I'll have to use a big switch.


    Not really, look at this

    #include <stdio.h>

    struct foo {
    int i; char c; char* s; double f; long l;
    } f = { 848, '~', "Stuff to be said", 4.5, 7474l };

    print_1(char* fmt, int* data) {
    return printf(fmt, *data);
    }

    print_2(char* fmt, double* data) {
    return printf(fmt, *data);
    }
    int main() {
    print_1("%d", &f.i); putchar('\n');
    print_1("%c", &f.c); putchar('\n');
    print_1("%s", &f.s); putchar('\n');
    print_2("%f", &f.f); putchar('\n');
    print_2("%ld", &f.l); putchar('\n');

    return 0;
    }

    The point is that ints,chars,char* and the like are passed in one word
    (on 32 bit machines) and longs, double etc. are passed as two words. So
    you can use one function for the 32 bit data or lesser sized data and
    another for the 64 bit sized data.

    The problem here is float. Float is actually 32 bit, put is passed as a
    double (64 bit). So you can't use either function for floats. Maybe you
    could add a third function and then the size of your switch would be
    reduced quite a bit.
    Pramod Subramanyan, Aug 22, 2005
    #5
  6. In article <>,
    Pramod Subramanyan <> wrote:

    > print_1("%s", &f.s); putchar('\n');
    > print_2("%f", &f.f); putchar('\n');


    That doesn't help much, because you have to know which function to call.

    In any case, it's not usually wise to rely on knowing the size of
    the datatypes. Especially if you're wrong about it:

    >The point is that ints,chars,char* and the like are passed in one word
    >(on 32 bit machines) and longs, double etc. are passed as two words.


    Long is 32 bits on most 32-bit processors.

    In any case, the big switch is not very big. There aren't that many
    built-in datatypes (assuming you don't need to handle pointers other
    than strings).

    -- Richard
    Richard Tobin, Aug 22, 2005
    #6
  7. > That doesn't help much, because you have to know which function to call.

    1) I've heard of an operator called sizeof. Our learned friend here
    perhaps needs to revisit the basics.
    2) Moreover, even though our learned friend is not the person asking
    question, he has however arbitrarily and pre-emptorily appointed him
    sole and final judge of the usefulness of the solution. On what basis,
    may I ask?

    > In any case, it's not usually wise to rely on knowing the size of
    > the datatypes. Especially if you're wrong about it:
    > Long is 32 bits on most 32-bit processors.


    Its 64 bits on Intel based platforms, which form the biggest chunk of
    the market by a LONG (pun unintended) margin, hence the "most". Anyway,
    won't a sizeof solve that problem as well?

    P.S: Richard, you need to do some REAL programming. Portability issues
    aren't as simple as not using the sizes of datatypes. In fact, they're
    one of the simplest problems to solve.
    Pramod Subramanyan, Aug 23, 2005
    #7
  8. "Pramod Subramanyan" <> writes:
    > Richard Tobin <> writes:
    >> In any case, it's not usually wise to rely on knowing the size of
    >> the datatypes. Especially if you're wrong about it:
    >> Long is 32 bits on most 32-bit processors.

    >
    > Its 64 bits on Intel based platforms, which form the biggest chunk of
    > the market by a LONG (pun unintended) margin, hence the "most". Anyway,
    > won't a sizeof solve that problem as well?


    Type long is 32 bits in every implementation I've see for 32-bit Intel
    platforms. I've seen 64-bit longs on 64-bit Intel platforms. (Type
    long long is, of course, required to be at least 64 bits.)

    And, Pramod, please don't snip attributions. I had to manually add
    the "Richard Tobin .. writes" above.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
    Keith Thompson, Aug 23, 2005
    #8
  9. Keith Thompson wrote:
    > "Pramod Subramanyan" <> writes:
    > > Richard Tobin <> writes:
    > >> In any case, it's not usually wise to rely on knowing the size of
    > >> the datatypes. Especially if you're wrong about it:
    > >> Long is 32 bits on most 32-bit processors.

    > >
    > > Its 64 bits on Intel based platforms, which form the biggest chunk of
    > > the market by a LONG (pun unintended) margin, hence the "most". Anyway,
    > > won't a sizeof solve that problem as well?

    >
    > Type long is 32 bits in every implementation I've see for 32-bit Intel
    > platforms. I've seen 64-bit longs on 64-bit Intel platforms. (Type
    > long long is, of course, required to be at least 64 bits.)


    VC++, BC++, LCC and a whole bunch of other platforms on Win32 have 64
    bit longs. Given that Intel has something like 85% of the PC-Processor
    market, and Windows is used on 80% of those, I stick to my stand. [I
    may be wrong about the exact numbers, but they're certainly in the same
    region.] And may I add that you haven't seen too many implementations?

    Moreover, the whole concept 32/64bit processors is ambiguous. What are
    you talking about? Data bus widths, register sizes, memory addressing
    capability, or instruction pointer (or program counter if you like)
    widths? Simply calling a processor 32bit or a platform 64bit does not
    give any real information.
    Pramod Subramanyan, Aug 23, 2005
    #9
  10. "Pramod Subramanyan" <> writes:
    > Keith Thompson wrote:
    >> "Pramod Subramanyan" <> writes:
    >> > Richard Tobin <> writes:
    >> >> In any case, it's not usually wise to rely on knowing the size of
    >> >> the datatypes. Especially if you're wrong about it:
    >> >> Long is 32 bits on most 32-bit processors.
    >> >
    >> > Its 64 bits on Intel based platforms, which form the biggest chunk of
    >> > the market by a LONG (pun unintended) margin, hence the "most". Anyway,
    >> > won't a sizeof solve that problem as well?

    >>
    >> Type long is 32 bits in every implementation I've see for 32-bit Intel
    >> platforms. I've seen 64-bit longs on 64-bit Intel platforms. (Type
    >> long long is, of course, required to be at least 64 bits.)

    >
    > VC++, BC++, LCC and a whole bunch of other platforms on Win32 have 64
    > bit longs. Given that Intel has something like 85% of the PC-Processor
    > market, and Windows is used on 80% of those, I stick to my stand. [I
    > may be wrong about the exact numbers, but they're certainly in the same
    > region.] And may I add that you haven't seen too many implementations?


    I've seen many implementations; few of them run under Windows.
    I believe you're mistaken.

    Under lcc-win32, long is 32 bits. The following program:

    #include <stdio.h>
    #include <limits.h>

    int main(void)
    {
    printf("short is %d bits\n", sizeof(short) * CHAR_BIT);
    printf("int is %d bits\n", sizeof(int) * CHAR_BIT);
    printf("long is %d bits\n", sizeof(long) * CHAR_BIT);
    return 0;
    }

    prints

    short is 16 bits
    int is 32 bits
    long is 32 bits

    I get the same output using gcc under Cygwin under Windows XP.

    The system in question has an Intel Pentium M processor (IA-32).

    Can you demonstrate, using the output of the above program, a C
    implementation on a 32-bit Intel processor (define the term "32-bit"
    as you like) that has 64-bit long? Please specify the platform, the
    compiler, and the actual output of the program.

    > Moreover, the whole concept 32/64bit processors is ambiguous. What are
    > you talking about? Data bus widths, register sizes, memory addressing
    > capability, or instruction pointer (or program counter if you like)
    > widths? Simply calling a processor 32bit or a platform 64bit does not
    > give any real information.


    Agreed.

    These details are only peripherally topical; the point is that
    assuming particular sizes for the predefined types is unwise.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
    Keith Thompson, Aug 23, 2005
    #10
  11. Michael B Allen

    Zoran Cutura Guest

    Pramod Subramanyan <> wrote:
    >
    > Keith Thompson wrote:
    >> "Pramod Subramanyan" <> writes:
    >> > Richard Tobin <> writes:
    >> >> In any case, it's not usually wise to rely on knowing the size of
    >> >> the datatypes. Especially if you're wrong about it:
    >> >> Long is 32 bits on most 32-bit processors.
    >> >
    >> > Its 64 bits on Intel based platforms, which form the biggest chunk of
    >> > the market by a LONG (pun unintended) margin, hence the "most". Anyway,
    >> > won't a sizeof solve that problem as well?

    >>
    >> Type long is 32 bits in every implementation I've see for 32-bit Intel
    >> platforms. I've seen 64-bit longs on 64-bit Intel platforms. (Type
    >> long long is, of course, required to be at least 64 bits.)

    >
    > VC++, BC++, LCC and a whole bunch of other platforms on Win32 have 64
    > bit longs. Given that Intel has something like 85% of the PC-Processor
    > market, and Windows is used on 80% of those, I stick to my stand. [I
    > may be wrong about the exact numbers, but they're certainly in the same
    > region.] And may I add that you haven't seen too many implementations?


    When I compile the following little prog with VSC++ 6.0

    #include <limits.h>
    #include <stdio.h>

    int main(void)
    {

    printf("bits in char == %d\n", CHAR_BIT);
    printf("size of long == %d\n", (int)sizeof(long));
    return 0;
    }

    I get the follwing output:

    bits in char == 8
    size of long == 4

    Which tells me, that long has 4 * 8 Bits which according to Adam Riese would be
    32. But I may be wrong.

    >
    > Moreover, the whole concept 32/64bit processors is ambiguous. What are
    > you talking about? Data bus widths, register sizes, memory addressing
    > capability, or instruction pointer (or program counter if you like)
    > widths? Simply calling a processor 32bit or a platform 64bit does not
    > give any real information.


    You where talking about the size of longs, wheren't you? I suppose that
    even on 64 Bit-Architectures longs may be 32 bits wide.

    --
    Z ()
    "LISP is worth learning for the profound enlightenment experience
    you will have when you finally get it; that experience will make you
    a better programmer for the rest of your days." -- Eric S. Raymond
    Zoran Cutura, Aug 23, 2005
    #11
  12. Michael B Allen

    Flash Gordon Guest

    Pramod Subramanyan wrote:
    > Keith Thompson wrote:
    >
    >>"Pramod Subramanyan" <> writes:
    >>
    >>>Richard Tobin <> writes:
    >>>
    >>>>In any case, it's not usually wise to rely on knowing the size of
    >>>>the datatypes. Especially if you're wrong about it:
    >>>>Long is 32 bits on most 32-bit processors.
    >>>
    >>>Its 64 bits on Intel based platforms, which form the biggest chunk of
    >>>the market by a LONG (pun unintended) margin, hence the "most". Anyway,
    >>>won't a sizeof solve that problem as well?

    >>
    >>Type long is 32 bits in every implementation I've see for 32-bit Intel
    >>platforms. I've seen 64-bit longs on 64-bit Intel platforms. (Type
    >>long long is, of course, required to be at least 64 bits.)

    >
    > VC++, BC++, LCC and a whole bunch of other platforms on Win32 have 64
    > bit longs.


    I can't comment on BC++ or LCC, but you are wrong about VC++. I know
    because I have it installed. To quote from the Fundamental Types page of
    the help:

    | Sizes of Fundamental Types
    |
    | Type Size
    | bool 1 byte
    | char, unsigned char, signed char 1 byte
    | short, unsigned short 2 bytes
    | int, unsigned int 4 bytes __intn 1, 2, 4, or 8
    | bytes depending on the value of
    | n. __intn is Microsoft-specific.
    | long, unsigned long 4 bytes
    | float 4 bytes
    | double 8 bytes
    | long double(1) 8 bytes
    | long long Equivalent to __int64.
    |
    | (1) The representation of long double and double is identical.
    | However, long double and double are separate types.

    So unless you are claiming the CHAR_BIT is something other than 8, long
    is definitely 32 bits.

    > Given that Intel has something like 85% of the PC-Processor
    > market, and Windows is used on 80% of those, I stick to my stand. [I
    > may be wrong about the exact numbers, but they're certainly in the same
    > region.] And may I add that you haven't seen too many implementations?


    I would not be surprised if Keith has seen rather more implementations
    that you.

    > Moreover, the whole concept 32/64bit processors is ambiguous. What are
    > you talking about? Data bus widths, register sizes, memory addressing
    > capability, or instruction pointer (or program counter if you like)
    > widths? Simply calling a processor 32bit or a platform 64bit does not
    > give any real information.


    Since it was Intel processors that were referred to, the most probable
    definition is whether Intel claim them to be 32 or 64 bit processors.
    --
    Flash Gordon
    Living in interesting times.
    Although my email address says spam, it is real and I read it.
    Flash Gordon, Aug 23, 2005
    #12
  13. In article <>,
    Pramod Subramanyan <> wrote:

    >> That doesn't help much, because you have to know which function to call.

    >
    >1) I've heard of an operator called sizeof.


    The original poster seemed to want a single function call that worked
    for all built-in types. Your proposal didn't do that. If you can fix
    it up using sizeof, that might be useful.

    The rest of your post consists of insults and a factual error which
    others have corrected, so I will not address it.

    -- Richard
    Richard Tobin, Aug 23, 2005
    #13
  14. Sorry, I got this wrong.

    Richard, I apologize for the patronizing tone.
    Pramod Subramanyan, Aug 24, 2005
    #14
  15. "Pramod Subramanyan" <> writes:
    > Sorry, I got this wrong.
    >
    > Richard, I apologize for the patronizing tone.


    Since Pramod didn't provide context, I'll mention that what he got
    wrong was his claim that type long is commonly 64 bits on 32-bit Intel
    platforms.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
    Keith Thompson, Aug 24, 2005
    #15
  16. Michael B Allen

    Richard Bos Guest

    "Pramod Subramanyan" <> wrote:

    > Sorry, I got this wrong.
    >
    > Richard, I apologize for the patronizing tone.


    Got _what_ wrong? _What_ patronising tone? I don't recall you being
    patronising to me... oh, wait, maybe you were patronising to Mr.
    Heathfield?

    It _is_ possible to get Google Groups Broken Beta to quote, you know.

    Richard
    Richard Bos, Aug 24, 2005
    #16
    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. winbatch
    Replies:
    5
    Views:
    560
    Winbatch
    Feb 15, 2005
  2. ben
    Replies:
    4
    Views:
    600
    Martin Ambuhl
    Jun 26, 2004
  3. whatluo

    (void) printf vs printf

    whatluo, May 26, 2005, in forum: C Programming
    Replies:
    29
    Views:
    1,224
  4. azza

    printf affects following printf/s

    azza, Oct 17, 2010, in forum: C Programming
    Replies:
    0
    Views:
    423
  5. guru
    Replies:
    8
    Views:
    265
Loading...

Share This Page