Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Initializing a malloc'ed struct whose fields are const with run-timevalues

Reply
Thread Tools

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

 
 
Noob
Guest
Posts: n/a
 
      01-22-2013
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.
 
Reply With Quote
 
 
 
 
Shao Miller
Guest
Posts: n/a
 
      01-22-2013
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
 
Reply With Quote
 
 
 
 
Ike Naar
Guest
Posts: n/a
 
      01-22-2013
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;
}
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      01-22-2013
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
 
Reply With Quote
 
Ian Collins
Guest
Posts: n/a
 
      01-22-2013
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
 
Reply With Quote
 
Noob
Guest
Posts: n/a
 
      01-22-2013
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.

 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      01-22-2013
Ike Naar <(E-Mail Removed)> 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.
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      01-22-2013
Shao Miller <(E-Mail Removed)> 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.
 
Reply With Quote
 
Noob
Guest
Posts: n/a
 
      01-22-2013
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.

 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      01-22-2013
On 1/22/2013 16:02, Ben Bacarisse wrote:
> Ike Naar <(E-Mail Removed)> 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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Can *common* struct-members of 2 different struct-types, that are thesame for the first common members, be accessed via pointer cast to either struct-type? John Reye C Programming 28 05-08-2012 12:24 AM
print struct fields and its member structs' fields recursively, generically call_me_anything C++ 4 09-30-2007 10:12 PM
const vector<A> vs vector<const A> vs const vector<const A> Javier C++ 2 09-04-2007 08:46 PM
Casting int'** to 'const int * const * const' dosn't work, why? Jonas.Holmsten@gmail.com C Programming 11 07-01-2007 06:16 PM
struct my_struct *p = (struct my_struct *)malloc(sizeof(struct my_struct)); Chris Fogelklou C Programming 36 04-20-2004 08:27 AM



Advertisments