Initializing a malloc'ed struct whose fields are const with run-timevalues

Discussion in 'C Programming' started by Noob, Jan 22, 2013.

  1. Noob

    Noob Guest

    Hello,

    Is it possible, in standard C89 and C99, to initialize a
    struct whose fields are const with values only known at
    run-time?

    For example, consider:

    struct toto { const int i; const float f; const void *p; };

    Is it possible to portably implement:

    struct toto *foo(int i, float f, void *p);

    which allocates space for a "struct toto", initializes it
    with i, f, p and returns the address of this struct?

    The best I could come up with is:

    #include <stdlib.h>
    #include <string.h>
    struct toto *foo(int i, float f, void *p)
    {
    struct toto s = { i, f, p };
    struct toto *res = malloc(sizeof *res);
    if (res != NULL) memcpy(res, &s, sizeof s);
    return res;
    }

    which has several defects:

    1) Apparently, C89 does not allow one to use elements "not computable
    at load time" in an initialization list. However, it is allowed both
    in C99 (right?) and in gnu89.

    2) I'm not sure it is well-defined to memcpy stuff into some const
    fields. Does it tickle the cranky UB gods?

    Regards.
    Noob, Jan 22, 2013
    #1
    1. Advertising

  2. Noob

    Shao Miller Guest

    On 1/22/2013 10:36, Noob wrote:
    > Hello,
    >
    > Is it possible, in standard C89 and C99, to initialize a
    > struct whose fields are const with values only known at
    > run-time?
    >
    > For example, consider:
    >
    > struct toto { const int i; const float f; const void *p; };
    >


    There is probably a better example, since none of the members here are
    non-'const'. You could just have these members non-'const'-qualified
    and return a 'const struct toto *', possibly with a cast. I'll assume
    you have at least one non-'const' member for further discussion.

    > Is it possible to portably implement:
    >
    > struct toto *foo(int i, float f, void *p);
    >
    > which allocates space for a "struct toto", initializes it
    > with i, f, p and returns the address of this struct?
    >
    > The best I could come up with is:
    >
    > #include <stdlib.h>
    > #include <string.h>
    > struct toto *foo(int i, float f, void *p)
    > {
    > struct toto s = { i, f, p };
    > struct toto *res = malloc(sizeof *res);
    > if (res != NULL) memcpy(res, &s, sizeof s);
    > return res;
    > }
    >


    How about:

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

    struct toto {
    const int i;
    const float f;
    const void * p;
    };

    struct toto * foo(int i, float f, void * p) {
    unsigned char * obj;

    obj = malloc(sizeof (struct toto));
    if (obj) {
    *(int *) (obj + offsetof(struct toto, i)) = i;
    *(float *) (obj + offsetof(struct toto, f)) = f;
    *(void **) (obj + offsetof(struct toto, i)) = p;
    }
    return (void *) obj;
    }

    int main(void) {
    struct toto * test = foo(42, 3.14, NULL);
    free(test);
    return 0;
    }

    > which has several defects:
    >
    > 1) Apparently, C89 does not allow one to use elements "not computable
    > at load time" in an initialization list. However, it is allowed both
    > in C99 (right?) and in gnu89.
    >


    The initializers in an initializer list for an aggregate or union must
    be constant expressions, and no, I do not believe that's changed in any
    newer Standard.

    > 2) I'm not sure it is well-defined to memcpy stuff into some const
    > fields.
    >


    You can 'memcpy' into allocated storage just fine. In C >= C99, you
    need to be concerned with effective type, so it's not completely
    straight-forward.

    > Does it tickle the cranky UB gods?
    >


    This is one of the funnier things I've read in comp.lang.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 22, 2013
    #2
    1. Advertising

  3. Noob

    Ike Naar Guest

    Re: Initializing a malloc'ed struct whose fields are const withrun-time values

    On 2013-01-22, Noob <root@127.0.0.1> wrote:
    > #include <stdlib.h>
    > #include <string.h>
    > struct toto *foo(int i, float f, void *p)
    > {
    > struct toto s = { i, f, p };
    > struct toto *res = malloc(sizeof *res);
    > if (res != NULL) memcpy(res, &s, sizeof s);


    Why memcpy instead of an assignment?
    if (res != NULL) *res = s;

    > return res;
    > }


    And how about returning by value?

    struct toto foo(int i, float f, void *p)
    {
    struct toto res = {i, f, p};
    return res;
    }
    Ike Naar, Jan 22, 2013
    #3
  4. Noob

    Shao Miller Guest

    On 1/22/2013 12:59, Ike Naar wrote:
    > On 2013-01-22, Noob <root@127.0.0.1> wrote:
    >> #include <stdlib.h>
    >> #include <string.h>
    >> struct toto *foo(int i, float f, void *p)
    >> {
    >> struct toto s = { i, f, p };
    >> struct toto *res = malloc(sizeof *res);
    >> if (res != NULL) memcpy(res, &s, sizeof s);

    >
    > Why memcpy instead of an assignment?
    > if (res != NULL) *res = s;
    >


    Because they amount to the same thing, behind the scenes, on a few
    implementations? ;) Lots of people still do:

    struct tag s;
    memset(&s, 0, sizeof s);

    instead of:

    struct tag s = { 0 };

    But the latter seems better (when possible).

    >> return res;
    >> }

    >
    > And how about returning by value?
    >
    > struct toto foo(int i, float f, void *p)
    > {
    > struct toto res = {i, f, p};
    > return res;
    > }
    >


    This has the same problem with the initializer list, though.

    I would guess that 'malloc' was being used for lifetime considerations,
    but obviously only Noob knows.

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

    Cheerily," -- Richard Harter
    Shao Miller, Jan 22, 2013
    #4
  5. Noob

    Ian Collins Guest

    Re: Initializing a malloc'ed struct whose fields are const withrun-time values

    Ike Naar wrote:
    > On 2013-01-22, Noob <root@127.0.0.1> wrote:
    >> #include <stdlib.h>
    >> #include <string.h>
    >> struct toto *foo(int i, float f, void *p)
    >> {
    >> struct toto s = { i, f, p };
    >> struct toto *res = malloc(sizeof *res);
    >> if (res != NULL) memcpy(res, &s, sizeof s);

    >
    > Why memcpy instead of an assignment?
    > if (res != NULL) *res = s;
    >
    >> return res;
    >> }

    >
    > And how about returning by value?
    >
    > struct toto foo(int i, float f, void *p)
    > {
    > struct toto res = {i, f, p};
    > return res;
    > }


    Is that legal? struct toto has const members, so can it be returned by
    value from a function?

    --
    Ian Collins
    Ian Collins, Jan 22, 2013
    #5
  6. Noob

    Noob Guest

    Shao Miller wrote:
    > On 1/22/2013 12:59, Ike Naar wrote:
    >> On 2013-01-22, Noob wrote:
    >>> #include <stdlib.h>
    >>> #include <string.h>
    >>> struct toto *foo(int i, float f, void *p)
    >>> {
    >>> struct toto s = { i, f, p };
    >>> struct toto *res = malloc(sizeof *res);
    >>> if (res != NULL) memcpy(res, &s, sizeof s);

    >>
    >> Why memcpy instead of an assignment?
    >> if (res != NULL) *res = s;

    >
    > Because they amount to the same thing, behind the scenes, on a few
    > implementations? ;) Lots of people still do:
    >
    > struct tag s;
    > memset(&s, 0, sizeof s);
    >
    > instead of:
    >
    > struct tag s = { 0 };
    >
    > But the latter seems better (when possible).
    >
    >>> return res;
    >>> }

    >>
    >> And how about returning by value?
    >>
    >> struct toto foo(int i, float f, void *p)
    >> {
    >> struct toto res = {i, f, p};
    >> return res;
    >> }
    >>

    >
    > This has the same problem with the initializer list, though.
    >
    > I would guess that 'malloc' was being used for lifetime considerations,
    > but obviously only Noob knows.


    As I often do, my simplification went a bit too far, so I'll just
    describe the actual use-case.
    (This involves two threads on a POSIX-compliant platform.)

    I have a foo_start function which malloc's space for a "context"
    struct, populates the struct according to the function's parameters,
    then spawns a new thread which is passed this context. (This is why
    dynamic allocation must be used.)

    To make matters more complex, I have a flexible array at the end of
    the struct.

    Basically, struct ctx is defined this way:

    struct ctx {
    int file_idx;
    const char *buf;
    sem_t sem;
    char path[];
    };

    void *foo_run(void *arg) {
    struct ctx *ctx = arg;
    do stuff in an infinite loop, according to ctx
    }

    void foo_start(int param1, int param2, ...)
    {
    struct ctx *ctx = malloc(sizeof *ctx + paramx);
    populate the fields of ctx;
    spawn(foo_run, ctx);
    }

    In my current version, I don't have any const qualifiers in my code.
    I like to look at the assembly code generated by the compiler, and I
    noticed that every time I need some field from ctx, the compiler has
    to reload it, instead of caching the value in a register.

    As far as I understand, this is expected: the address of the struct
    could be stored anywhere, and any function in a different translation
    unit could "pull the rug" from under me. Except that *I* wrote the
    code, and I *know* (by design) that most fields in the struct do NOT
    change after init.

    So I set out to sprinkle a few "const" qualifiers here and there, to
    see if that would convince the compiler that some optimizations are
    indeed possible (I know this looks like a clear case of premature
    optimization, but I figured I might as well learn something new along
    the way!)

    I don't think I can just const-qualify the entire struct, because
    1) the prototype for a thread's entry point is imposed by POSIX
    2) const-qualifying a pointer parameter is only a contract between
    the user and the function's implementer, saying "my function won't
    touch your preciousss struct", it doesn't say that the struct won't
    be changed by something else.

    Whereas, I was under the impression that a const-qualified field
    means "hear, hear, this field SHALL NEVER change!"

    Anyway, I'd be happy to hear the word from the regs, and/or take
    this to comp.unix.programmer at some point (although I do believe
    that the core of my question is a C question, not POSIX).

    Regards.
    Noob, Jan 22, 2013
    #6
  7. Re: Initializing a malloc'ed struct whose fields are const with run-time values

    Ike Naar <> writes:

    > On 2013-01-22, Noob <root@127.0.0.1> wrote:
    >> #include <stdlib.h>
    >> #include <string.h>
    >> struct toto *foo(int i, float f, void *p)
    >> {
    >> struct toto s = { i, f, p };
    >> struct toto *res = malloc(sizeof *res);
    >> if (res != NULL) memcpy(res, &s, sizeof s);

    >
    > Why memcpy instead of an assignment?
    > if (res != NULL) *res = s;


    Because it'll provoke an error! The struct's members are const so the
    assignment is a constraint violation.

    >> return res;
    >> }

    >
    > And how about returning by value?
    >
    > struct toto foo(int i, float f, void *p)
    > {
    > struct toto res = {i, f, p};
    > return res;
    > }


    This limits what you can do with it for the same reason. You can't
    assign a strust toto because of its const members.

    --
    Ben.
    Ben Bacarisse, Jan 22, 2013
    #7
  8. Re: Initializing a malloc'ed struct whose fields are const with run-time values

    Shao Miller <> writes:

    > On 1/22/2013 12:59, Ike Naar wrote:
    >> On 2013-01-22, Noob <root@127.0.0.1> wrote:
    >>> #include <stdlib.h>
    >>> #include <string.h>
    >>> struct toto *foo(int i, float f, void *p)
    >>> {
    >>> struct toto s = { i, f, p };
    >>> struct toto *res = malloc(sizeof *res);
    >>> if (res != NULL) memcpy(res, &s, sizeof s);

    >>
    >> Why memcpy instead of an assignment?
    >> if (res != NULL) *res = s;
    >>

    >
    > Because they amount to the same thing, behind the scenes, on a few
    > implementations? ;)


    But in front of the scenes, they are very different! The assignment is
    a constraint violation.

    <snip>
    --
    Ben.
    Ben Bacarisse, Jan 22, 2013
    #8
  9. Noob

    Noob Guest

    Ike Naar wrote:
    > On 2013-01-22, Noob wrote:
    >> #include <stdlib.h>
    >> #include <string.h>
    >> struct toto *foo(int i, float f, void *p)
    >> {
    >> struct toto s = { i, f, p };
    >> struct toto *res = malloc(sizeof *res);
    >> if (res != NULL) memcpy(res, &s, sizeof s);

    >
    > Why memcpy instead of an assignment?
    > if (res != NULL) *res = s;


    Right... I'll have to try that, and see what gcc thinks.

    > And how about returning by value?
    >
    > struct toto foo(int i, float f, void *p)
    > {
    > struct toto res = {i, f, p};
    > return res;
    > }


    This won't work for me, I have to manage the struct's
    lifetime by hand, using dynamic allocation. (See my reply
    to Shao for a complete description.)

    Regards.
    Noob, Jan 22, 2013
    #9
  10. Noob

    Shao Miller Guest

    On 1/22/2013 16:02, Ben Bacarisse wrote:
    > Ike Naar <> writes:
    >
    >> On 2013-01-22, Noob <root@127.0.0.1> wrote:
    >>> #include <stdlib.h>
    >>> #include <string.h>
    >>> struct toto *foo(int i, float f, void *p)
    >>> {
    >>> struct toto s = { i, f, p };
    >>> struct toto *res = malloc(sizeof *res);
    >>> if (res != NULL) memcpy(res, &s, sizeof s);

    >>
    >> Why memcpy instead of an assignment?
    >> if (res != NULL) *res = s;

    >
    > Because it'll provoke an error! The struct's members are const so the
    > assignment is a constraint violation.
    >
    >>> return res;
    >>> }

    >>
    >> And how about returning by value?
    >>
    >> struct toto foo(int i, float f, void *p)
    >> {
    >> struct toto res = {i, f, p};
    >> return res;
    >> }

    >
    > This limits what you can do with it for the same reason. You can't
    > assign a strust toto because of its const members.
    >


    Are you sure about that? 'res' undergoes lvalue conversion and no
    longer has qualified type.

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

    Cheerily," -- Richard Harter
    Shao Miller, Jan 22, 2013
    #10
  11. Re: Initializing a malloc'ed struct whose fields are const with run-time values

    Noob <root@127.0.0.1> writes:

    > Is it possible, in standard C89 and C99, to initialize a
    > struct whose fields are const with values only known at
    > run-time?
    >
    > For example, consider:
    >
    > struct toto { const int i; const float f; const void *p; };
    >
    > Is it possible to portably implement:
    >
    > struct toto *foo(int i, float f, void *p);
    >
    > which allocates space for a "struct toto", initializes it
    > with i, f, p and returns the address of this struct?
    >
    > The best I could come up with is:
    >
    > #include <stdlib.h>
    > #include <string.h>
    > struct toto *foo(int i, float f, void *p)
    > {
    > struct toto s = { i, f, p };
    > struct toto *res = malloc(sizeof *res);
    > if (res != NULL) memcpy(res, &s, sizeof s);
    > return res;
    > }
    >
    > which has several defects:
    >
    > 1) Apparently, C89 does not allow one to use elements "not computable
    > at load time" in an initialization list. However, it is allowed both
    > in C99 (right?) and in gnu89.


    Yes, it's fine in C99 and later. I'm not sure about gnu89 but I would
    not be surprised.

    BTW, in C99 you don't even need a declaration:

    if (res != NULL) memcpy(res, &(struct toto){i, f, p}, sizeof *res);

    > 2) I'm not sure it is well-defined to memcpy stuff into some const
    > fields. Does it tickle the cranky UB gods?


    No, I think it's fine. You are not modifying anything that is const.
    malloced storage has no declared type and the memcpy probably makes the
    effective type a byte array.

    --
    Ben.
    Ben Bacarisse, Jan 22, 2013
    #11
  12. Noob

    Shao Miller Guest

    On 1/22/2013 16:05, Ben Bacarisse wrote:
    > Shao Miller <> writes:
    >
    >> On 1/22/2013 12:59, Ike Naar wrote:
    >>> On 2013-01-22, Noob <root@127.0.0.1> wrote:
    >>>> #include <stdlib.h>
    >>>> #include <string.h>
    >>>> struct toto *foo(int i, float f, void *p)
    >>>> {
    >>>> struct toto s = { i, f, p };
    >>>> struct toto *res = malloc(sizeof *res);
    >>>> if (res != NULL) memcpy(res, &s, sizeof s);
    >>>
    >>> Why memcpy instead of an assignment?
    >>> if (res != NULL) *res = s;
    >>>

    >>
    >> Because they amount to the same thing, behind the scenes, on a few
    >> implementations? ;)

    >
    > But in front of the scenes, they are very different! The assignment is
    > a constraint violation.
    >
    > <snip>
    >


    Yes, that's quite true. I was teasing about a bad reason for doing it,
    not pointing out a good reason for doing it. :)

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

    Cheerily," -- Richard Harter
    Shao Miller, Jan 22, 2013
    #12
  13. Re: Initializing a malloc'ed struct whose fields are const with run-time values

    Noob <root@localhost> wrote:

    (snip)
    >> This has the same problem with the initializer list, though.


    >> I would guess that 'malloc' was being used for lifetime considerations,
    >> but obviously only Noob knows.


    > As I often do, my simplification went a bit too far, so I'll just
    > describe the actual use-case.
    > (This involves two threads on a POSIX-compliant platform.)


    As far as I know, the constraints on const aren't quite as strict
    as Java's static final, or Fortran's PARAMETER.

    You can get away with some things that you probably shouldn't be
    able to, as the compiler can't check them.

    > I have a foo_start function which malloc's space for a "context"
    > struct, populates the struct according to the function's parameters,
    > then spawns a new thread which is passed this context. (This is why
    > dynamic allocation must be used.)


    > To make matters more complex, I have a flexible array at the end of
    > the struct.


    (snip)

    > In my current version, I don't have any const qualifiers in my code.
    > I like to look at the assembly code generated by the compiler, and I
    > noticed that every time I need some field from ctx, the compiler has
    > to reload it, instead of caching the value in a register.


    Have you tried with an actual const struct (with actual compile time
    constants) to see what the optimizer does? Or passing such a struct
    (or pointer to one) and see what it does?

    > As far as I understand, this is expected: the address of the struct
    > could be stored anywhere, and any function in a different translation
    > unit could "pull the rug" from under me. Except that *I* wrote the
    > code, and I *know* (by design) that most fields in the struct do NOT
    > change after init.


    Well, as described in another thread, there is the 'volatile'
    attribute to tell the compiler, in some sense which I won't
    describe here, that a variable could change.

    On the other hand, normal aliasing rules apply. Often the compiler
    can't assume that two pointers don't point to the same (or nearby)
    variables, and so can't do some optimizations that it might otherwise
    do.

    > So I set out to sprinkle a few "const" qualifiers here and there, to
    > see if that would convince the compiler that some optimizations are
    > indeed possible (I know this looks like a clear case of premature
    > optimization, but I figured I might as well learn something new along
    > the way!)


    Did it help any? Does it change with optimization (-O) level?

    > I don't think I can just const-qualify the entire struct, because
    > 1) the prototype for a thread's entry point is imposed by POSIX
    > 2) const-qualifying a pointer parameter is only a contract between
    > the user and the function's implementer, saying "my function won't
    > touch your preciousss struct", it doesn't say that the struct won't
    > be changed by something else.


    > Whereas, I was under the impression that a const-qualified field
    > means "hear, hear, this field SHALL NEVER change!"


    > Anyway, I'd be happy to hear the word from the regs, and/or take
    > this to comp.unix.programmer at some point (although I do believe
    > that the core of my question is a C question, not POSIX).


    -- glen
    glen herrmannsfeldt, Jan 22, 2013
    #13
  14. Noob

    Shao Miller Guest

    On 1/22/2013 16:13, Ben Bacarisse wrote:
    > Noob <root@127.0.0.1> writes:
    >
    >> Is it possible, in standard C89 and C99, to initialize a
    >> struct whose fields are const with values only known at
    >> run-time?
    >>
    >> For example, consider:
    >>
    >> struct toto { const int i; const float f; const void *p; };
    >>
    >> Is it possible to portably implement:
    >>
    >> struct toto *foo(int i, float f, void *p);
    >>
    >> which allocates space for a "struct toto", initializes it
    >> with i, f, p and returns the address of this struct?
    >>
    >> The best I could come up with is:
    >>
    >> #include <stdlib.h>
    >> #include <string.h>
    >> struct toto *foo(int i, float f, void *p)
    >> {
    >> struct toto s = { i, f, p };
    >> struct toto *res = malloc(sizeof *res);
    >> if (res != NULL) memcpy(res, &s, sizeof s);
    >> return res;
    >> }
    >>
    >> which has several defects:
    >>
    >> 1) Apparently, C89 does not allow one to use elements "not computable
    >> at load time" in an initialization list. However, it is allowed both
    >> in C99 (right?) and in gnu89.

    >
    > Yes, it's fine in C99 and later. I'm not sure about gnu89 but I would
    > not be surprised.
    >


    Oops. To Noob: Sorry about providing the wrong answer for this one,
    earlier. I hope the rest of that post helped.

    > BTW, in C99 you don't even need a declaration:
    >
    > if (res != NULL) memcpy(res, &(struct toto){i, f, p}, sizeof *res);
    >
    >> 2) I'm not sure it is well-defined to memcpy stuff into some const
    >> fields. Does it tickle the cranky UB gods?

    >
    > No, I think it's fine. You are not modifying anything that is const.
    > malloced storage has no declared type and the memcpy probably makes the
    > effective type a byte array.
    >


    To Ben: I think the effective type would be 'struct toto', as 'memcpy'
    is included in 6.5p6.

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

    Cheerily," -- Richard Harter
    Shao Miller, Jan 22, 2013
    #14
  15. Re: Initializing a malloc'ed struct whose fields are const with run-time values

    Shao Miller <> writes:

    > On 1/22/2013 10:36, Noob wrote:

    <snip>
    >> 1) Apparently, C89 does not allow one to use elements "not computable
    >> at load time" in an initialization list. However, it is allowed both
    >> in C99 (right?) and in gnu89.

    >
    > The initializers in an initializer list for an aggregate or union must
    > be constant expressions, and no, I do not believe that's changed in
    > any newer Standard.


    I think it has.

    --
    Ben.
    Ben Bacarisse, Jan 22, 2013
    #15
  16. Noob

    James Kuyper Guest

    Re: Initializing a malloc'ed struct whose fields are const withrun-time values

    On 01/22/2013 04:19 PM, William Ahern wrote:
    > Ian Collins <> wrote:

    ....
    >> Is that legal? struct toto has const members, so can it be returned by
    >> value from a function?

    >
    > What about foo().f? Does the simple return constitute modification of a
    > const-qualified lvalue?
    >
    > I'm still not convinced that "modifying" a const-qualified member through
    > assignment of a non-const-qualified aggregate is undefined behaviour. But I
    > don't know enough about the rules of lvalues to prove it.


    A struct containing a member that is const-qualified is not modifiable
    (6.3.2.1p1). It is a constraint violation for an assignment operator to
    have a left operand which is not a modifiable lvalue (6.5.16p2).
    Arguments passed to a function (6.5.2.2p7), and values returned by a
    function (6.8.6.4p3), are both converted "as if by assignment", and I
    believe are therefore subject to the same constraints.
    James Kuyper, Jan 22, 2013
    #16
  17. Noob

    James Kuyper Guest

    On 01/22/2013 04:26 PM, Ben Bacarisse wrote:
    > Shao Miller <> writes:

    ....
    >> The initializers in an initializer list for an aggregate or union must
    >> be constant expressions, and no, I do not believe that's changed in
    >> any newer Standard.

    >
    > I think it has.


    You think correctly - as of C99, that restriction only applies to
    initializers for objects of static storage duration. As of C2011, it
    also applies to initializers for objects with thread storage duration
    (6.7.9p4); only C90 imposed it on initializers for objects with
    automatic storage duration.
    James Kuyper, Jan 22, 2013
    #17
  18. Noob

    Nick Bowler Guest

    Re: Initializing a malloc'ed struct whose fields are const withrun-time values

    On Tue, 22 Jan 2013 21:13:50 +0000, Ben Bacarisse wrote:
    > BTW, in C99 you don't even need a declaration:
    >
    > if (res != NULL) memcpy(res, &(struct toto){i, f, p}, sizeof *res);


    Danger, Will Robinson!

    Implementations are allowed to define a function-like macro called
    memcpy, and the above line will not work on implementations that do so.
    Macro rguments will be split on the commas in the compound literal,
    which is almost certainly not desirable.

    Therefore, it is neccesary to add parentheses (or to #undef memcpy...),
    either around the compound literal to avoid such splitting, or around
    memcpy to avoid any macro substitution at all. For example:

    if (res != NULL) memcpy(res, (&(struct toto){i, f, p}), sizeof *res);
    if (res != NULL) (memcpy)(res, &(struct toto){i, f, p}, sizeof *res);

    are both OK.

    Cheers,
    Nick
    Nick Bowler, Jan 22, 2013
    #18
  19. Noob

    Ian Collins Guest

    Re: Initializing a malloc'ed struct whose fields are const withrun-time values

    William Ahern wrote:
    > William Ahern <william@wilbur.25thandclement.com> wrote:
    >> Ian Collins <> wrote:
    >>> Ike Naar wrote:

    > <snip>
    >>>> And how about returning by value?
    >>>>
    >>>> struct toto foo(int i, float f, void *p)
    >>>> {
    >>>> struct toto res = {i, f, p};
    >>>> return res;
    >>>> }

    >
    >>> Is that legal? struct toto has const members, so can it be returned by
    >>> value from a function?

    >
    >> What about foo().f? Does the simple return constitute modification of a
    >> const-qualified lvalue?

    >
    >> I'm still not convinced that "modifying" a const-qualified member through
    >> assignment of a non-const-qualified aggregate is undefined behaviour. But I
    >> don't know enough about the rules of lvalues to prove it.

    >
    > Ok, now I am:
    >
    > C99 (N1256) 6.3.2.1
    >
    > A modifiable lvalue is an lvalue that does not have array type, does
    > not have an incomplete type, does not have a const-qualified type,
    > and if it is a structure or union, does not have any member
    > (including, recursively, any member or element of all contained
    > aggregates or unions) with a const-qualified type.
    >
    > But foo().f should still be fine, I think.


    So res above isn't a modifiable lvalue.

    I just tried this code:

    #include <stdio.h>

    struct toto { const int i; const float f; const void *p; };

    struct toto foo(int i, float f, void *p)
    {
    return (struct toto){i, f, p};
    }

    int main(void)
    {
    float f = foo( 1,2,NULL).f;
    }

    and gcc (-Wall -std=c99 -pedantic) compiles it, but Sun c99 (which tends
    to be both strict and conforming) rejects it with one error:

    "x.c", line 7: left operand must be modifiable lvalue: op "="

    where line 7 is the function return.

    --
    Ian Collins
    Ian Collins, Jan 22, 2013
    #19
  20. Noob

    Shao Miller Guest

    Re: Initializing a malloc'ed struct whose fields are const withrun-time values

    On 1/22/2013 16:45, Ian Collins wrote:
    > William Ahern wrote:
    >> William Ahern <william@wilbur.25thandclement.com> wrote:
    >>> Ian Collins <> wrote:
    >>>> Ike Naar wrote:

    >> <snip>
    >>>>> And how about returning by value?
    >>>>>
    >>>>> struct toto foo(int i, float f, void *p)
    >>>>> {
    >>>>> struct toto res = {i, f, p};
    >>>>> return res;
    >>>>> }

    >>
    >>>> Is that legal? struct toto has const members, so can it be returned by
    >>>> value from a function?

    >>
    >>> What about foo().f? Does the simple return constitute modification of a
    >>> const-qualified lvalue?

    >>
    >>> I'm still not convinced that "modifying" a const-qualified member
    >>> through
    >>> assignment of a non-const-qualified aggregate is undefined behaviour.
    >>> But I
    >>> don't know enough about the rules of lvalues to prove it.

    >>
    >> Ok, now I am:
    >>
    >> C99 (N1256) 6.3.2.1
    >>
    >> A modifiable lvalue is an lvalue that does not have array type, does
    >> not have an incomplete type, does not have a const-qualified type,
    >> and if it is a structure or union, does not have any member
    >> (including, recursively, any member or element of all contained
    >> aggregates or unions) with a const-qualified type.
    >>
    >> But foo().f should still be fine, I think.

    >
    > So res above isn't a modifiable lvalue.
    >
    > I just tried this code:
    >
    > #include <stdio.h>
    >
    > struct toto { const int i; const float f; const void *p; };
    >
    > struct toto foo(int i, float f, void *p)
    > {
    > return (struct toto){i, f, p};
    > }
    >
    > int main(void)
    > {
    > float f = foo( 1,2,NULL).f;
    > }
    >
    > and gcc (-Wall -std=c99 -pedantic) compiles it, but Sun c99 (which tends
    > to be both strict and conforming) rejects it with one error:
    >
    > "x.c", line 7: left operand must be modifiable lvalue: op "="
    >
    > where line 7 is the function return.
    >


    That seems a bit odd. 'return' has little to do with an lvalue or object:

    "The properties associated with qualified types are meaningful only
    for expressions that are lvalues.132)"

    So "as if by assignment" should be satisfied by:

    "the left operand has an atomic, qualified, or unqualified version of
    a structure or union type compatible with the type of the right;"

    No?

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

    Cheerily," -- Richard Harter
    Shao Miller, Jan 22, 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. Chris Fogelklou
    Replies:
    36
    Views:
    1,376
    Chris Fogelklou
    Apr 20, 2004
  2. Replies:
    11
    Views:
    1,102
  3. Javier
    Replies:
    2
    Views:
    561
    James Kanze
    Sep 4, 2007
  4. call_me_anything
    Replies:
    4
    Views:
    463
    Pete Becker
    Sep 30, 2007
  5. 0m
    Replies:
    26
    Views:
    1,115
    Tim Rentsch
    Nov 10, 2008
Loading...

Share This Page