[va_list *] Is this legal ?

Discussion in 'C Programming' started by m.labanowicz@gmail.com, Jan 16, 2013.

  1. Guest

    Hello,

    I'm trying to compile following example:

    -----{test:BEG}----------------------------------------
    01: #include <stdio.h>
    02: #include <stdarg.h>
    03: #include <stdlib.h>
    04: int va_get_int(va_list * ap) {
    05: return va_arg(*ap, int);
    06: }
    07: char * va_get_chrptr(va_list * ap) {
    08: return va_arg(*ap, char *);
    09: }
    10: void fooA(va_list ap) {
    11: char const * txt = NULL;
    12: printf("A:int = %d\n", va_arg(ap, int));
    13: txt = va_arg(ap, char *);
    14: printf("A:ptr = %p\n", (void *)txt);
    15: printf("A:ptr = \"%s\"\n", txt);
    16: }
    17: void fooB(va_list ap) {
    18: char const * txt = NULL;
    19: printf("B:int = %d\n", va_get_int(&ap));
    20: txt = va_get_chrptr(&ap);
    21: printf("B:ptr = %p\n", (void *)txt);
    22: printf("B:ptr = \"%s\"\n", txt);
    23: }
    24: void bar(char const * arg0, ...) {
    25: va_list ap;
    26: printf("arg0 = \"%s\"\n", arg0);
    27: va_start(ap, arg0);
    28: fooA(ap);
    29: va_end(ap);
    30: va_start(ap, arg0);
    31: fooB(ap);
    32: va_end(ap);
    33: va_start(ap, arg0);
    34: fooA(ap);
    35: va_end(ap);
    36: }
    37: int main(void) {
    38: bar("koko", 23, "txt");
    39: return EXIT_SUCCESS;
    40: }
    -----{test:EOF}----------------------------------------

    [32 bit machine]
    gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
    $ gcc -W -Wall -ansi -pedantic test.c
    $ ./a.out
    arg0 = "koko"
    A:int = 23
    A:ptr = 0x8048669
    A:ptr = "txt"
    B:int = 23
    B:ptr = 0x8048669
    B:ptr = "txt"
    A:int = 23
    A:ptr = 0x8048669
    A:ptr = "txt"

    [64 bit machine]
    gcc (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2
    $ gcc -W -Wall -ansi -pedantic test.c
    test.c: In function 'fooB':
    test.c:18:3: warning: passing argument 1 of 'va_get_int' from incompatible pointer type [enabled by default]
    test.c:4:5: note: expected 'struct __va_list_tag (*)[1]' but argument is of type 'struct __va_list_tag **'
    test.c:19:3: warning: passing argument 1 of 'va_get_chrptr' from incompatible pointer type [enabled by default]
    test.c:7:8: note: expected 'struct __va_list_tag (*)[1]' but argument is of type 'struct __va_list_tag **'
    $ ./a.out
    arg0 = "koko"
    A:int = 23
    A:ptr = 0x40096d
    A:ptr = "txt"
    B:int = -443987883
    B:ptr = 0x455441ed31455541
    Segmentation fault (core dumped)

    It seems that pointer to 'va_list' can not be used,
    ok, I have changed code to:
    -----{diff:BEG}----------------------------------------
    4,5c4,5
    < int va_get_int(va_list * ap) {
    < return va_arg(*ap, int);
    ---
    > int va_get_int(va_list ap) {
    > return va_arg(ap, int);

    7,8c7,8
    < char * va_get_chrptr(va_list * ap) {
    < return va_arg(*ap, char *);
    ---
    > char * va_get_chrptr(va_list ap) {
    > return va_arg(ap, char *);

    19,20c19,20
    < printf("B:int = %d\n", va_get_int(&ap));
    < txt = va_get_chrptr(&ap);
    ---
    > printf("B:int = %d\n", va_get_int(ap));
    > txt = va_get_chrptr(ap);

    -----{diff:EOF}----------------------------------------

    After that both compilers are silent,
    no errors, no warnings, but:

    [64 bit machine]
    gcc (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2
    $ gcc -W -Wall -ansi -pedantic test2.c
    $ ./a.out
    arg0 = "koko"
    A:int = 23
    A:ptr = 0x40096d
    A:ptr = "txt"
    B:int = 23
    B:ptr = 0x40096d
    B:ptr = "txt"
    A:int = 23
    A:ptr = 0x40096d
    A:ptr = "txt"

    [32 bit machine]
    gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
    $ gcc -W -Wall -ansi -pedantic test2.c
    $ ./a.out
    arg0 = "koko"
    A:int = 23
    A:ptr = 0x8048669
    A:ptr = "txt"
    B:int = 23
    B:ptr = 0x17
    Segmentation fault


    Best Regards

    --
    Maciej Labanowicz
    , Jan 16, 2013
    #1
    1. Advertising

  2. Noob Guest

    Robert Wessel wrote:

    > Mind you that there is no guarantee in C that there is *any* integer
    > type that can actually hold a pointer (although both Posix and Windows
    > require such).


    However, if a given implementation provides intptr_t and/or uintptr_t,
    then one can use either(?) to store the representation of an object pointer.

    Tangential questions:

    - Can uintptr_t store the representation of a function pointer?

    - Why provide signed and unsigned types?

    Regards.
    Noob, Jan 16, 2013
    #2
    1. Advertising

  3. Tim Rentsch Guest

    writes:

    > I'm trying to compile following example:
    >
    > -----{test:BEG}----------------------------------------
    > 01: #include <stdio.h>
    > 02: #include <stdarg.h>
    > 03: #include <stdlib.h>
    > 04: int va_get_int(va_list * ap) {
    > 05: return va_arg(*ap, int);
    > 06: }
    > 07: char * va_get_chrptr(va_list * ap) {
    > 08: return va_arg(*ap, char *);
    > 09: }
    > 10: void fooA(va_list ap) {
    > 11: char const * txt = NULL;
    > 12: printf("A:int = %d\n", va_arg(ap, int));
    > 13: txt = va_arg(ap, char *);
    > 14: printf("A:ptr = %p\n", (void *)txt);
    > 15: printf("A:ptr = \"%s\"\n", txt);
    > 16: }
    > 17: void fooB(va_list ap) {
    > 18: char const * txt = NULL;
    > 19: printf("B:int = %d\n", va_get_int(&ap));
    > 20: txt = va_get_chrptr(&ap);
    > 21: printf("B:ptr = %p\n", (void *)txt);
    > 22: printf("B:ptr = \"%s\"\n", txt);
    > 23: }
    > 24: void bar(char const * arg0, ...) {
    > 25: va_list ap;
    > 26: printf("arg0 = \"%s\"\n", arg0);
    > 27: va_start(ap, arg0);
    > 28: fooA(ap);
    > 29: va_end(ap);
    > 30: va_start(ap, arg0);
    > 31: fooB(ap);
    > 32: va_end(ap);
    > 33: va_start(ap, arg0);
    > 34: fooA(ap);
    > 35: va_end(ap);
    > 36: }
    > 37: int main(void) {
    > 38: bar("koko", 23, "txt");
    > 39: return EXIT_SUCCESS;
    > 40: }
    > -----{test:EOF}----------------------------------------
    >
    > [32 bit machine] [..snip..]
    >
    > [64 bit machine]
    > gcc (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2
    > $ gcc -W -Wall -ansi -pedantic test.c
    > test.c: In function 'fooB':
    > test.c:18:3: warning: passing argument 1 of 'va_get_int' from incompatible pointer type [enabled by default]
    > test.c:4:5: note: expected 'struct __va_list_tag (*)[1]' but argument is of type 'struct __va_list_tag **'
    > test.c:19:3: warning: passing argument 1 of 'va_get_chrptr' from incompatible pointer type [enabled by default]
    > test.c:7:8: note: expected 'struct __va_list_tag (*)[1]' but argument is of type 'struct __va_list_tag **'
    > [..snip..]


    These messages appear not to match the given source file
    (some of the line numbers are off). This makes me
    suspicious that what is being reported may not match what
    really occurred.

    Incidentally, I suggest using -pedantic-errors rather than
    just -pedantic. The conditions being reported above should
    be errors, not warnings; -pedantic-errors should do that.

    Also, I think the problem you're having is not taking into
    account that the va_list type could be an array type. This
    means that a declaration like this

    void
    blah(){
    va_list ap;
    }

    and a declaration like this

    void
    bleah( va_list ap ){
    }

    even though it looks like the same type is involved in both
    cases, actually the two types are different. Try changing
    the function fooB() so its declaration looks like this

    void fooB( va_list *ap ){ /* etc */

    and make other changes as needed (the call to fooB(), and
    the functions that fooB() calls that take this parameter).
    Using the type 'va_list *' should work, _if_ you use it all
    the way along the call chain to the function that declares
    the original 'va_list ap;' local variable, because that
    avoids the problem with having an array type for a parameter
    declaration.
    Tim Rentsch, Jan 16, 2013
    #3
  4. On 2013-01-16, <> wrote:
    > I'm trying to compile following example:
    >
    > -----{test:BEG}----------------------------------------
    > 01: #include <stdio.h>
    > 02: #include <stdarg.h>
    > 03: #include <stdlib.h>
    > 04: int va_get_int(va_list * ap) {
    > 05: return va_arg(*ap, int);
    > 06: }
    > 07: char * va_get_chrptr(va_list * ap) {
    > 08: return va_arg(*ap, char *);
    > 09: }
    > 10: void fooA(va_list ap) {
    > 11: char const * txt = NULL;
    > 12: printf("A:int = %d\n", va_arg(ap, int));
    > 13: txt = va_arg(ap, char *);
    > 14: printf("A:ptr = %p\n", (void *)txt);
    > 15: printf("A:ptr = \"%s\"\n", txt);
    > 16: }
    > 17: void fooB(va_list ap) {
    > 18: char const * txt = NULL;
    > 19: printf("B:int = %d\n", va_get_int(&ap));
    > 20: txt = va_get_chrptr(&ap);
    > 21: printf("B:ptr = %p\n", (void *)txt);
    > 22: printf("B:ptr = \"%s\"\n", txt);
    > 23: }
    > 24: void bar(char const * arg0, ...) {
    > 25: va_list ap;
    > 26: printf("arg0 = \"%s\"\n", arg0);
    > 27: va_start(ap, arg0);
    > 28: fooA(ap);
    > 29: va_end(ap);
    > 30: va_start(ap, arg0);
    > 31: fooB(ap);
    > 32: va_end(ap);
    > 33: va_start(ap, arg0);
    > 34: fooA(ap);
    > 35: va_end(ap);
    > 36: }
    > 37: int main(void) {
    > 38: bar("koko", 23, "txt");
    > 39: return EXIT_SUCCESS;
    > 40: }
    > -----{test:EOF}----------------------------------------

    [...]
    > It seems that pointer to 'va_list' can not be used,
    > ok, I have changed code to:
    > -----{diff:BEG}----------------------------------------
    > 4,5c4,5
    >< int va_get_int(va_list * ap) {
    >< return va_arg(*ap, int);
    > ---
    >> int va_get_int(va_list ap) {
    >> return va_arg(ap, int);

    > 7,8c7,8
    >< char * va_get_chrptr(va_list * ap) {
    >< return va_arg(*ap, char *);
    > ---
    >> char * va_get_chrptr(va_list ap) {
    >> return va_arg(ap, char *);

    > 19,20c19,20
    >< printf("B:int = %d\n", va_get_int(&ap));
    >< txt = va_get_chrptr(&ap);
    > ---
    >> printf("B:int = %d\n", va_get_int(ap));
    >> txt = va_get_chrptr(ap);

    > -----{diff:EOF}----------------------------------------


    Due to C11 7.16p3:

    If access to the varying arguments is desired, the called function
    shall declare an object (generally referred to as ap in this
    subclause) having type va_list. The object ap may be passed as an
    argument to another function; if that function invokes the va_arg
    macro with parameter ap, the value of ap in the calling function is
    indeterminate and shall be passed to the va_end macro prior to any
    further reference to ap.

    FOOTNOTE 253) It is permitted to create a pointer to a va_list and
    pass that pointer to another function, in which case the original
    function may make further use of the original list after the other
    function returns.

    ....the rewritten version seems to be unambiguously illegal. However,
    the footnote seems to slightly suggest that the original code ought to
    be permitted, though admittedly by a strict reading it does not
    guarantee that anything else than the function declaring the va_list may
    form the pointer to it, and only pass it to a single other function.
    Note that if you rewrite it so that fooB itself also takes a "va_list *"
    argument, things (at least on my system) work out right on both 32 and
    64 bits; example pasted to:
    http://sprunge.us/gabU

    (Presumably, and based on the warning messages emitted, the difference
    is due to "[1]" meaning different things in a function parameter
    declaration than elsewhere.)

    --
    Heikki Kallasjoki
    Heikki Kallasjoki, Jan 16, 2013
    #4
  5. Tim Rentsch Guest

    Robert Wessel <> writes:

    > On Wed, 16 Jan 2013 00:15:12 -0800 (PST),
    > wrote:
    >
    >>Hello,
    >>
    >>I'm trying to compile following example:
    >>
    >>-----{test:BEG}----------------------------------------
    >>01: #include <stdio.h>
    >>02: #include <stdarg.h>
    >>03: #include <stdlib.h>
    >>04: int va_get_int(va_list * ap) {
    >>05: return va_arg(*ap, int);
    >>06: }
    >>07: char * va_get_chrptr(va_list * ap) {
    >>08: return va_arg(*ap, char *);
    >>09: }
    >>10: void fooA(va_list ap) {
    >>11: char const * txt = NULL;
    >>12: printf("A:int = %d\n", va_arg(ap, int));
    >>13: txt = va_arg(ap, char *);
    >>14: printf("A:ptr = %p\n", (void *)txt);
    >>15: printf("A:ptr = \"%s\"\n", txt);
    >>16: }
    >>17: void fooB(va_list ap) {
    >>18: char const * txt = NULL;
    >>19: printf("B:int = %d\n", va_get_int(&ap));
    >>20: txt = va_get_chrptr(&ap);
    >>21: printf("B:ptr = %p\n", (void *)txt);
    >>22: printf("B:ptr = \"%s\"\n", txt);
    >>23: }
    >>24: void bar(char const * arg0, ...) {
    >>25: va_list ap;
    >>26: printf("arg0 = \"%s\"\n", arg0);
    >>27: va_start(ap, arg0);
    >>28: fooA(ap);
    >>29: va_end(ap);
    >>30: va_start(ap, arg0);
    >>31: fooB(ap);
    >>32: va_end(ap);
    >>33: va_start(ap, arg0);
    >>34: fooA(ap);
    >>35: va_end(ap);
    >>36: }
    >>37: int main(void) {
    >>38: bar("koko", 23, "txt");
    >>39: return EXIT_SUCCESS;
    >>40: }
    >>-----{test:EOF}----------------------------------------
    >>
    >>[32 bit machine]
    >>gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
    >>$ gcc -W -Wall -ansi -pedantic test.c
    >>$ ./a.out
    >>arg0 = "koko"
    >>A:int = 23
    >>A:ptr = 0x8048669
    >>A:ptr = "txt"
    >>B:int = 23
    >>B:ptr = 0x8048669
    >>B:ptr = "txt"
    >>A:int = 23
    >>A:ptr = 0x8048669
    >>A:ptr = "txt"
    >>
    >>[64 bit machine]
    >>gcc (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2
    >>$ gcc -W -Wall -ansi -pedantic test.c
    >>test.c: In function 'fooB':
    >>test.c:18:3: warning: passing argument 1 of 'va_get_int' from incompatible pointer type [enabled by default]
    >>test.c:4:5: note: expected 'struct __va_list_tag (*)[1]' but argument is of type 'struct __va_list_tag **'
    >>test.c:19:3: warning: passing argument 1 of 'va_get_chrptr' from incompatible pointer type [enabled by default]
    >>test.c:7:8: note: expected 'struct __va_list_tag (*)[1]' but argument is of type 'struct __va_list_tag **'
    >>$ ./a.out
    >>arg0 = "koko"
    >>A:int = 23
    >>A:ptr = 0x40096d
    >>A:ptr = "txt"
    >>B:int = -443987883
    >>B:ptr = 0x455441ed31455541
    >>Segmentation fault (core dumped)
    >>
    >>It seems that pointer to 'va_list' can not be used,
    >>ok, I have changed code to:
    >>-----{diff:BEG}----------------------------------------
    >>4,5c4,5
    >>< int va_get_int(va_list * ap) {
    >>< return va_arg(*ap, int);
    >>---
    >>> int va_get_int(va_list ap) {
    >>> return va_arg(ap, int);

    >>7,8c7,8
    >>< char * va_get_chrptr(va_list * ap) {
    >>< return va_arg(*ap, char *);
    >>---
    >>> char * va_get_chrptr(va_list ap) {
    >>> return va_arg(ap, char *);

    >>19,20c19,20
    >>< printf("B:int = %d\n", va_get_int(&ap));
    >>< txt = va_get_chrptr(&ap);
    >>---
    >>> printf("B:int = %d\n", va_get_int(ap));
    >>> txt = va_get_chrptr(ap);

    >>-----{diff:EOF}----------------------------------------
    >>
    >>After that both compilers are silent,
    >>no errors, no warnings, but:
    >>
    >>[64 bit machine]
    >>gcc (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2
    >>$ gcc -W -Wall -ansi -pedantic test2.c
    >>$ ./a.out
    >>arg0 = "koko"
    >>A:int = 23
    >>A:ptr = 0x40096d
    >>A:ptr = "txt"
    >>B:int = 23
    >>B:ptr = 0x40096d
    >>B:ptr = "txt"
    >>A:int = 23
    >>A:ptr = 0x40096d
    >>A:ptr = "txt"
    >>
    >>[32 bit machine]
    >>gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
    >>$ gcc -W -Wall -ansi -pedantic test2.c
    >>$ ./a.out
    >>arg0 = "koko"
    >>A:int = 23
    >>A:ptr = 0x8048669
    >>A:ptr = "txt"
    >>B:int = 23
    >>B:ptr = 0x17
    >>Segmentation fault

    >
    >
    > On most 64 bit implementations of C, pointers are 64 bits and ints are
    > 32, thus attempting to squeeze a 64 bit address through a 32 bit int
    > is unlikely to be successful.
    >
    > On most 64 bit *nix, long is 64 bits, while on windows long remains 32
    > bits. In both cases explicitly 64 bit types will be provided by the
    > compiler.
    >
    > Mind you that there is no guarantee in C that there is *any* integer
    > type that can actually hold a pointer (although both Posix and Windows
    > require such).


    What makes you think this has anything to do with his
    problem?
    Tim Rentsch, Jan 16, 2013
    #5
  6. Tim Rentsch Guest

    Heikki Kallasjoki <> writes:

    > On 2013-01-16, <> wrote:
    >> I'm trying to compile following example: [snip]

    >
    > Due to C11 7.16p3:
    >
    > If access to the varying arguments is desired, the called function
    > shall declare an object (generally referred to as ap in this
    > subclause) having type va_list. The object ap may be passed as an
    > argument to another function; if that function invokes the va_arg
    > macro with parameter ap, the value of ap in the calling function is
    > indeterminate and shall be passed to the va_end macro prior to any
    > further reference to ap.
    >
    > FOOTNOTE 253) It is permitted to create a pointer to a va_list and
    > pass that pointer to another function, in which case the original
    > function may make further use of the original list after the other
    > function returns.
    >
    > ...the rewritten version seems to be unambiguously illegal. However,
    > the footnote seems to slightly suggest that the original code ought to
    > be permitted,


    The problem is what that code is making is not a pointer to a
    va_list but (in some cases) a pointer to a different type, and
    the result of that is not compatible with a va_list *.

    > though admittedly by a strict reading it does not
    > guarantee that anything else than the function declaring the va_list may
    > form the pointer to it, and only pass it to a single other function.
    > Note that if you rewrite it so that fooB itself also takes a "va_list *"
    > argument, things (at least on my system) work out right on both 32 and
    > 64 bits; [snip]
    > (Presumably, and based on the warning messages emitted, the difference
    > is due to "[1]" meaning different things in a function parameter
    > declaration than elsewhere.)


    Yes. And the moral is, _always_ use 'va_list *' for parameters,
    because the behavior is more well-defined than for plain 'va_list',
    and it won't lead to nasty little surprises like the examples here
    (which do use 'va_list' for parameters) illustrate.
    Tim Rentsch, Jan 16, 2013
    #6
  7. Guest

    > These messages appear not to match the given source file
    > (some of the line numbers are off). This makes me
    > suspicious that what is being reported may not match what
    > really occurred.


    Sorry, my fault, I hate such inconsistency, following there is the corresponding compilation messages to the code from original post:
    ----------------------------------
    test.c: In function ‘fooB’:
    test.c:19:3: warning: passing argument 1 of ‘va_get_int’ from incompatible pointer type [enabled by default]
    test.c:4:5: note: expected ‘struct __va_list_tag (*)[1]’ but argument is of type ‘struct __va_list_tag **’
    test.c:20:3: warning: passing argument 1 of ‘va_get_chrptr’ from incompatible pointer type [enabled by default]
    test.c:7:8: note: expected ‘struct __va_list_tag (*)[1]’ but argument is of type ‘struct __va_list_tag **’
    ----------------------------------

    --
    Maciej Labanowicz
    , Jan 16, 2013
    #7
  8. Shao Miller Guest

    On 1/16/2013 04:05, Noob wrote:
    > Robert Wessel wrote:
    >
    >> Mind you that there is no guarantee in C that there is *any* integer
    >> type that can actually hold a pointer (although both Posix and Windows
    >> require such).

    >
    > However, if a given implementation provides intptr_t and/or uintptr_t,
    > then one can use either(?) to store the representation of an object pointer.
    >
    > Tangential questions:
    >
    > - Can uintptr_t store the representation of a function pointer?
    >


    I don't see a guarantee for that, no.

    > - Why provide signed and unsigned types?
    >


    Since pointer representation is mostly opaque, this strikes me as a
    flexibility for the implementation. Suppose an implementation
    "actually" uses a signed integer value for pointers... Then not
    providing 'uintptr_t' and providing 'intptr_t' might be most natural.
    Does that make sense?

    - Shao Miller


    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
    Shao Miller, Jan 16, 2013
    #8
  9. Shao Miller Guest

    On 1/16/2013 03:15, wrote:
    > Hello,
    >
    > I'm trying to compile following example:
    >


    'va_list' is an array type. A function parameter cannot be declared to
    have an array type, even if it looks like it does. Here are two
    test-cases for you:

    #include <stdio.h>

    typedef char arr42_t[42];

    void func1(arr42_t * arr) {
    (*arr)[0] = 'C';
    (*arr)[1] = '\0';
    }

    void func2(arr42_t arr) {
    func1(&arr);
    }

    int main(void) {
    arr42_t arr;

    func2(arr);
    printf("%s\n", arr);
    return 0;
    }

    ---

    gcc -ansi -pedantic -Wall -Wextra -Werror -o mlaban1 mlaban1.c

    ---

    #include <stdio.h>

    typedef char arr42_t[42];

    void func1(arr42_t * arr) {
    (*arr)[0] = 'C';
    (*arr)[1] = '\0';
    }

    void func2(arr42_t * arr) {
    func1(arr);
    }

    int main(void) {
    arr42_t arr;

    func2(&arr);
    printf("%s\n", arr);
    return 0;
    }

    ---

    gcc -ansi -pedantic -Wall -Wextra -Werror -o mlaban2 mlaban2.c

    ---

    And here is a test-case to demonstrate that the parameter is not the
    same type as the original:

    #include <stdio.h>

    typedef char arr42_t[42];

    size_t sizeof_arr_param(arr42_t param) {
    return sizeof param;
    }

    int main(void) {
    arr42_t arr;

    printf(
    "sizeof arr == %u && sizeof param == %u\n",
    (unsigned int) sizeof arr,
    (unsigned int) sizeof_arr_param(arr)
    );
    return 0;
    }

    ---

    gcc -ansi -pedantic -Wall -Wextra -Werror -o mlaban3 mlaban3.c

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
    Shao Miller, Jan 16, 2013
    #9
  10. Shao Miller <> writes:
    > On 1/16/2013 04:05, Noob wrote:
    >> Robert Wessel wrote:
    >>> Mind you that there is no guarantee in C that there is *any* integer
    >>> type that can actually hold a pointer (although both Posix and Windows
    >>> require such).

    >>
    >> However, if a given implementation provides intptr_t and/or uintptr_t,
    >> then one can use either(?) to store the representation of an object pointer.
    >>
    >> Tangential questions:
    >>
    >> - Can uintptr_t store the representation of a function pointer?

    >
    > I don't see a guarantee for that, no.


    There is no such guarantee. You can convert a void* to intptr_t or
    uintptr_t and back to void*, and get back a result that compares
    equal to the original value. A void* may not be able to hold the
    converted value of a function pointer without loss of information.

    >> - Why provide signed and unsigned types?

    >
    > Since pointer representation is mostly opaque, this strikes me as a
    > flexibility for the implementation. Suppose an implementation
    > "actually" uses a signed integer value for pointers... Then not
    > providing 'uintptr_t' and providing 'intptr_t' might be most natural.
    > Does that make sense?


    Yes, but that doesn't seem to be the way the standard defines it.
    Both intptr_t and uintptr_t behave as described. Both types
    are optional, but there's no guidance for the criteria for
    an implementation to provide them or not. Typically (with
    two's-complement and no padding bits) a signed type and its
    corresponding unsigned type hold exactly the same amount of
    information, and I've never heard of an implementation that provides
    intptr_t but not uintptr_t, or vice versa.

    And if either intptr_t or uintptr_t provides a more natural mapping
    to pointers on a given implementation, I see no way for a program
    to detect that.

    On the other hand, if you're using these types, you're probably
    writing low-level implementation-specific code anyway, so you should
    know (by other means) which type is more useful.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Working, but not speaking, for JetHead Development, Inc.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Jan 16, 2013
    #10
  11. Tim Rentsch Guest

    Keith Thompson <> writes:

    > and I've never heard of an implementation that provides
    > intptr_t but not uintptr_t, or vice versa.


    If one is defined the other must be -- 7.20.1 p1.
    Tim Rentsch, Jan 16, 2013
    #11
  12. Tim Rentsch <> writes:
    > Keith Thompson <> writes:
    >> and I've never heard of an implementation that provides
    >> intptr_t but not uintptr_t, or vice versa.

    >
    > If one is defined the other must be -- 7.20.1 p1.


    Ah, I missed that.

    And the standard provides no guidance about which type might be
    more appropriate for a given system.

    It could matter, in the sense that a system where pointers
    are naturally unsigned might permit an object to span the range
    0x7fffffff..0x80000000, while a system where pointers are naturally
    signed probably wouldn't. And you might want to do meaningful "<"
    and ">" comparisons on intptr_t values.

    But again, if you're writing such low-level code, you're probably
    depending on non-standard information about the characteristics of
    pointers anyway.

    7.20.1p1 also implies that, for example, a ones'-complement
    system that can't provide a conforming int32_t (which must be
    two's-complement) would not be allowed to define uint32_t, even
    if it could do so meaningfully. I think that's a mistake, but not
    one that's rear its head in real life.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Working, but not speaking, for JetHead Development, Inc.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Jan 16, 2013
    #12
  13. Keith Thompson <> wrote:

    (snip, someone wrote)
    >>> - Why provide signed and unsigned types?


    (snip)

    > Yes, but that doesn't seem to be the way the standard defines it.
    > Both intptr_t and uintptr_t behave as described. Both types
    > are optional, but there's no guidance for the criteria for
    > an implementation to provide them or not. Typically (with
    > two's-complement and no padding bits) a signed type and its
    > corresponding unsigned type hold exactly the same amount of
    > information, and I've never heard of an implementation that provides
    > intptr_t but not uintptr_t, or vice versa.


    > And if either intptr_t or uintptr_t provides a more natural mapping
    > to pointers on a given implementation, I see no way for a program
    > to detect that.


    I was about to write that I didn't know any implementations with
    negative addresses, but the VAX addressing space has user space
    allocated up from 0, and system space down from X'FFFFFFFF'.

    It might, then, be natural to consider system space as small negative
    values instead of large unsigned values.

    > On the other hand, if you're using these types, you're probably
    > writing low-level implementation-specific code anyway, so you should
    > know (by other means) which type is more useful.


    -- glen
    glen herrmannsfeldt, Jan 16, 2013
    #13
  14. Keith Thompson <> wrote:
    > Tim Rentsch <> writes:
    >> Keith Thompson <> writes:
    >>> and I've never heard of an implementation that provides
    >>> intptr_t but not uintptr_t, or vice versa.


    >> If one is defined the other must be -- 7.20.1 p1.


    > Ah, I missed that.


    > And the standard provides no guidance about which type might be
    > more appropriate for a given system.


    > It could matter, in the sense that a system where pointers
    > are naturally unsigned might permit an object to span the range
    > 0x7fffffff..0x80000000, while a system where pointers are naturally
    > signed probably wouldn't. And you might want to do meaningful "<"
    > and ">" comparisons on intptr_t values.


    Then again, a system might not allow for either one. As I previously
    mentioned, VAX, for one, defines two (actually four based on the two
    high bits) separate address spaces.

    > But again, if you're writing such low-level code, you're probably
    > depending on non-standard information about the characteristics of
    > pointers anyway.


    > 7.20.1p1 also implies that, for example, a ones'-complement
    > system that can't provide a conforming int32_t (which must be
    > two's-complement) would not be allowed to define uint32_t, even
    > if it could do so meaningfully. I think that's a mistake, but not
    > one that's rear its head in real life.


    All the sign magnitude and ones complement systems that I know about
    aren't 32 bit systems. The CDC machines use 60 bits, though I believe
    that addresses don't use all the bits. The IBM 36 bit machines, such
    as the 7090, are sign magnitude, but not all the bits are used for
    integer arithmetic. I believe they use 15 bits, plus the sign bit,
    but store the value in a 36 bit word.

    There might be at least one of each running, somewhere in the world.

    -- glen
    glen herrmannsfeldt, Jan 16, 2013
    #14
  15. On 1/16/2013 8:47 AM, Keith Thompson wrote:
    >
    > And if either intptr_t or uintptr_t provides a more natural mapping
    > to pointers on a given implementation, I see no way for a program
    > to detect that.
    >
    > On the other hand, if you're using these types, you're probably
    > writing low-level implementation-specific code anyway, so you should
    > know (by other means) which type is more useful.
    >


    Not necessarily low-level. I would 'uintptr_t' type as natural candidate
    for generic "counter" type for entities that can exist in "unlimited"
    quantities in the storage.

    For example, 'size_t' is often used as a natural choice of "counter"
    type for generic arrays. Since the number of bytes in an array object
    does not exceed the range of 'size_t', 'size_t' should always be
    sufficient to index and count any array.

    Similar reasoning can be applied to 'uintptr_t' and non-array
    containers. Since the number of entries in a generic linked list cannot
    exceed the number of bytes in the storage (and it is reasonable to
    expect that to be in 'uintptr_t's range) 'uintptr_t' should be
    sufficient to index and count any linked list (or any other
    non-array-based container).

    In other words, even though the intended semantics of 'uintptr_t' type
    is relatively low-level, it has some pretty reasonable uses within
    higher-level concepts (again, pretty much the way it is with 'size_t').

    --
    Best regards,
    Andrey Tarasevich
    Andrey Tarasevich, Jan 16, 2013
    #15
  16. glen herrmannsfeldt <> writes:
    > Keith Thompson <> wrote:

    [...]
    >> 7.20.1p1 also implies that, for example, a ones'-complement
    >> system that can't provide a conforming int32_t (which must be
    >> two's-complement) would not be allowed to define uint32_t, even
    >> if it could do so meaningfully. I think that's a mistake, but not
    >> one that's rear its head in real life.

    >
    > All the sign magnitude and ones complement systems that I know about
    > aren't 32 bit systems. The CDC machines use 60 bits, though I believe
    > that addresses don't use all the bits. The IBM 36 bit machines, such
    > as the 7090, are sign magnitude, but not all the bits are used for
    > integer arithmetic. I believe they use 15 bits, plus the sign bit,
    > but store the value in a 36 bit word.
    >
    > There might be at least one of each running, somewhere in the world.


    Sure, but I doubt that there's a conforming C99 or C11 implementation
    for them.

    But if there were a conforming implementation for a CDC machine, it
    could probably support uint60_t but not int60_t -- which means they
    wouldn't be allowed to define uint60_t.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Working, but not speaking, for JetHead Development, Inc.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Jan 17, 2013
    #16
  17. Keith Thompson <> wrote:

    (snip, I wrote)
    >> All the sign magnitude and ones complement systems that I know about
    >> aren't 32 bit systems. The CDC machines use 60 bits, though I believe
    >> that addresses don't use all the bits. The IBM 36 bit machines, such
    >> as the 7090, are sign magnitude, but not all the bits are used for
    >> integer arithmetic. I believe they use 15 bits, plus the sign bit,
    >> but store the value in a 36 bit word.


    >> There might be at least one of each running, somewhere in the world.


    > Sure, but I doubt that there's a conforming C99 or C11 implementation
    > for them.


    I wouldn't complain about a C89 implementation.

    > But if there were a conforming implementation for a CDC machine, it
    > could probably support uint60_t but not int60_t -- which means they
    > wouldn't be allowed to define uint60_t.


    I did use the CDC machine some, but not all that much, and not quite
    enough to know the answer to that.

    I seem to remember some machine that would add and subtract all 60 bits,
    but multiply and divide only 48 bits.

    I don't actually know if they do unsigned arithmetic.

    The CDC machines were supposed to be used when you wanted fast floating
    point, and weren't especially useful if you didn't.

    -- glen
    glen herrmannsfeldt, Jan 17, 2013
    #17
  18. Guest

    > 'va_list' is an array type. A function parameter cannot be declared to
    > have an array type, even if it looks like it does.


    You are right.

    I have tried with following code and it seems that it works correctly everywhere:

    ....
    #include <string.h>
    typedef struct {
    va_list ap;
    } va_list_ex_t;
    int va_get_int(va_list ap, va_list_ex_t * vle) {
    int result = va_arg(ap, int);
    memcpy(&(vle->ap), &ap, sizeof(ap));
    return result;
    }
    char * va_get_chrptr(va_list ap, va_list_ex_t * vle) {
    char * result = va_arg(ap, char *);
    memcpy(&(vle->ap), &ap, sizeof(ap));
    return result;
    }
    void fooB(va_list ap) {
    va_list_ex_t vle;
    char const * txt = NULL;
    printf("B:int = %d\n", va_get_int(ap, &vle));
    memcpy(&ap, &(vle.ap), sizeof(ap));
    txt = va_get_chrptr(ap, &vle);
    memcpy(&ap, &(vle.ap), sizeof(ap));
    printf("B:ptr = %p\n", (void *)txt);
    printf("B:ptr = \"%s\"\n", txt);
    }
    ....

    I think there is no possibility to detect va_list type (struct/array) during preprocessing time and final conclusion is:
    'va_list *' type can not be used (it is not portable).

    --
    Maciej Labanowicz
    , Jan 17, 2013
    #18
  19. Guest


    > typedef struct {
    > va_list ap;
    > } va_list_ex_t;


    Above struct is wrong in case va_list for example is declared as:
    typedef char va_list [1];

    In this case there will be segfault during memcpy.

    Fix for that:
    typedef union {
    va_list ap;
    va_list * ptr_to_ap;
    void * ptr_general;
    } va_list_ex_t;

    --
    Maciej Labanowicz
    , Jan 17, 2013
    #19
  20. Shao Miller Guest

    On 1/17/2013 02:43, wrote:
    >> 'va_list' is an array type. A function parameter cannot be declared to
    >> have an array type, even if it looks like it does.

    >
    > You are right.
    >
    > I have tried with following code and it seems that it works correctly everywhere:
    >
    > ...
    > #include <string.h>
    > typedef struct {
    > va_list ap;
    > } va_list_ex_t;
    > int va_get_int(va_list ap, va_list_ex_t * vle) {
    > int result = va_arg(ap, int);
    > memcpy(&(vle->ap), &ap, sizeof(ap));
    > return result;
    > }
    > char * va_get_chrptr(va_list ap, va_list_ex_t * vle) {
    > char * result = va_arg(ap, char *);
    > memcpy(&(vle->ap), &ap, sizeof(ap));
    > return result;
    > }
    > void fooB(va_list ap) {
    > va_list_ex_t vle;
    > char const * txt = NULL;
    > printf("B:int = %d\n", va_get_int(ap, &vle));
    > memcpy(&ap, &(vle.ap), sizeof(ap));
    > txt = va_get_chrptr(ap, &vle);
    > memcpy(&ap, &(vle.ap), sizeof(ap));
    > printf("B:ptr = %p\n", (void *)txt);
    > printf("B:ptr = \"%s\"\n", txt);
    > }
    > ...
    >
    > I think there is no possibility to detect va_list type (struct/array) during preprocessing time and final conclusion is:
    > 'va_list *' type can not be used (it is not portable).
    >


    Did you mean 'va_list' type is not portable, or did you really mean
    'va_list *' is not portable?

    Whether 'va_list' is an array type like it is in your scenario or is
    something else for a given implementation, 'va_list *' is explicitly
    mentioned in the Standard as being usable, and even has some advantages
    which I saw others already mentioned, elsethread.

    The trick is to pass '&ap' from your function with the ellipsis ', ...)'
    ('bar', in your example) and to use '*pap' in any called function that
    has 'va_list * pap' as a parameter.

    int va_get_int(va_list * pap) {
    return va_arg(*pap, int);
    }

    void fooB(va_list * pap) {
    char const * txt = NULL;
    printf("B:int = %d\n", va_get_int(pap));
    txt = va_get_chrptr(pap);
    printf("B:ptr = %p\n", (void *)txt);
    printf("B:ptr = \"%s\"\n", txt);
    }

    void bar(char const * arg0, ...) {
    va_list ap;
    printf("arg0 = \"%s\"\n", arg0);
    va_start(ap, arg0);
    fooA(&ap);
    fooB(&ap);
    fooA(&ap);
    va_end(ap);
    }

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
    Shao Miller, Jan 17, 2013
    #20
    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. va_list in JNI

    , Jun 23, 2005, in forum: Java
    Replies:
    4
    Views:
    2,304
  2. Rich Herrick

    reference to va_list

    Rich Herrick, Jan 16, 2005, in forum: C++
    Replies:
    0
    Views:
    426
    Rich Herrick
    Jan 16, 2005
  3. Peter

    va_list help, please ...

    Peter, Feb 15, 2005, in forum: C++
    Replies:
    6
    Views:
    3,532
    Pete Becker
    Feb 15, 2005
  4. John Guo

    va_list help

    John Guo, Mar 31, 2005, in forum: C++
    Replies:
    5
    Views:
    3,305
    Pete Becker
    Mar 31, 2005
  5. Douwe
    Replies:
    3
    Views:
    734
    Chris Torek
    Aug 30, 2003
Loading...

Share This Page