Problem: assignment of read-only member

  • Follow


/* test.c */
#include <stdlib.h>

void f() {
struct X { const int x; };
struct X* myx = malloc(sizeof(struct X));
myx->x = 42;
}
/* end of test.c */

$ gcc-4.3 -c test.c
test.c: In function 'f':
test.c:7: error: assignment of read-only member 'x'

I would like x to be immutable once the struct is initialized. How do
I initialize it? I looked in the C FAQ but did not see anything that
addressed this specifically.

Or perhaps I'm going about this the wrong way?

Adam
0
Reply aburry (35) 9/14/2008 5:33:13 PM

On Sun, 14 Sep 2008 10:33:13 -0700, aburry wrote:
> /* test.c */
> #include <stdlib.h>
> 
> void f() {
> struct X { const int x; };
> struct X* myx = malloc(sizeof(struct X));
> myx->x = 42;
> }
> /* end of test.c */
> 
> $ gcc-4.3 -c test.c
> test.c: In function 'f':
> test.c:7: error: assignment of read-only member 'x'
> 
> I would like x to be immutable once the struct is initialized. How do I
> initialize it? I looked in the C FAQ but did not see anything that
> addressed this specifically.

The only way to initialise a dynamically allocated structure is by using 
calloc, and if you do that, you can't choose an initialiser value.

> Or perhaps I'm going about this the wrong way?

The problem is that there is not really a sane right way. You may be able 
to use this:

    struct X { const int x; };
    struct X *myx = malloc(sizeof *myx);
    struct X myx_value = { 42 };
    memcpy(myx, &myx_value, sizeof *myx);

but it's ugly.
0
Reply truedfx (1926) 9/14/2008 5:53:59 PM


On Sep 14, 12:53 pm, Harald van D=A9=A6k <truedfx@gmail.com> wrote:
> The problem is that there is not really a sane right way. You may be able
> to use this:
>
>     struct X { const int x; };
>     struct X *myx =3D malloc(sizeof *myx);
>     struct X myx_value =3D { 42 };
>     memcpy(myx, &myx_value, sizeof *myx);
>
> but it's ugly.

Isn't it possible that the compiler has chosen to store the 'x' member
in some kind of read-only memory, making the call to memcpy() unsafe?

Sebastian

0
Reply s0suk3 (372) 9/14/2008 6:02:06 PM

On Sep 14, 10:33=A0pm, abu...@ieee.org wrote:
> /* test.c */
> #include <stdlib.h>
>
> void f() {
> struct X { const int x; };
> struct X* myx =3D malloc(sizeof(struct X));
> myx->x =3D 42;}
>
> /* end of test.c */
>
> $ gcc-4.3 -c test.c
> test.c: In function 'f':
> test.c:7: error: assignment of read-only member 'x'
>
> I would like x to be immutable once the struct is initialized. How do
> I initialize it? I looked in the C FAQ but did not see anything that
> addressed this specifically.
>
> Or perhaps I'm going about this the wrong way?
>
> Adam

please go through following discussions
http://groups.google.com/group/comp.lang.c/browse_thread/thread/a3e1002be09=
fe22c/0be80f24e3b00a40?lnk=3Dgst&q=3Dconst+member+in+structure#0be80f24e3b0=
0a40

--
vIpIn
0
Reply sh.vipin (57) 9/14/2008 6:03:46 PM

On Sun, 14 Sep 2008 11:02:06 -0700, s0suk3 wrote:
> On Sep 14, 12:53 pm, Harald van Dijk <truedfx@gmail.com> wrote:
>> The problem is that there is not really a sane right way. You may be
>> able to use this:
>>
>>     struct X { const int x; };
>>     struct X *myx = malloc(sizeof *myx);
>>     struct X myx_value = { 42 };
>>     memcpy(myx, &myx_value, sizeof *myx);
>>
>> but it's ugly.
> 
> Isn't it possible that the compiler has chosen to store the 'x' member
> in some kind of read-only memory, making the call to memcpy() unsafe?

In general, yes, but here, no, that's not possible, since it's a member of 
a dynamically allocated structure, and all bytes that malloc's result 
points to must be writeable.
0
Reply truedfx (1926) 9/14/2008 6:06:15 PM

On Sun, 14 Sep 2008 10:33:13 -0700 (PDT), aburry@ieee.org wrote:

>/* test.c */
>#include <stdlib.h>
>
>void f() {
>struct X { const int x; };
>struct X* myx = malloc(sizeof(struct X));
>myx->x = 42;
>}
>/* end of test.c */
>
>$ gcc-4.3 -c test.c
>test.c: In function 'f':
>test.c:7: error: assignment of read-only member 'x'
>
>I would like x to be immutable once the struct is initialized. How do
>I initialize it? I looked in the C FAQ but did not see anything that
>addressed this specifically.
>
>Or perhaps I'm going about this the wrong way?

Since the language doesn't have a "write once" concept (other than
initialization as part of the definition), you have to fake it.  One
approach that might do what you want is

     struct X {int x;};
     const struct X *myptr;
     struct X *ptr_used_only_to_initialize_x = malloc(sizeof *myptr);
     /* obvious error check goes here */
     ptr_used_only_to_initialize_x->x = 42;
     myptr = ptr_used_only_to_initialize_x;

and never use ptr_used_only_to_initialize_x again.  You might even add
something like 

     #define ptr_used_only_to_initialize_x text to cause compile error

after the above code just to catch the any such use.

Naturally this will work only if all the members of the struct are to
be treated as const.
     

-- 
Remove del for email
0
Reply schwarzb3978 (1358) 9/14/2008 6:44:57 PM

On Sep 14, 8:33 pm, abu...@ieee.org wrote:
> /* test.c */
> #include <stdlib.h>
>
> void f() {
> struct X { const int x; };
> struct X* myx = malloc(sizeof(struct X));
> myx->x = 42;}
>
> /* end of test.c */
>
> $ gcc-4.3 -c test.c
> test.c: In function 'f':
> test.c:7: error: assignment of read-only member 'x'
>
> I would like x to be immutable once the struct is initialized. How do
> I initialize it? I looked in the C FAQ but did not see anything that
> addressed this specifically.
>
> Or perhaps I'm going about this the wrong way?

Well, why would you want a const member in a struct?
Here's a way to do this:

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

struct x { const int i; }
struct x *p;
void *q;

p = q = malloc(sizeof *x)
if(p)
    *(int *)((unsigned char *)q + offsetof(struct x, i)) = yourvalue;

free(p);

another more simple one:

*(int *)&p->i = yourvalue;

0
Reply vippstar (1211) 9/14/2008 6:55:17 PM

Thank you for the answers. I did search the group, but did not find
helpful thread vipin found. Here's a solved test case:

/* test.c */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main(int argc, char* argv[] ) {
    struct X { const int x; };
    struct X* myx = malloc(sizeof *myx);
    /* myx->x = 42; */  /* error: assignment of read-only member 'x'
*/

    /* solution 1: */
#if 0
    /* const initialization */
    struct X myx_value = { 42 };
    /* copy the const object */
    memcpy(myx, &myx_value, sizeof *myx);
#endif

    /* solution 2: */
#if 1
    /* const cast */
    *(int*)(&(myx->x)) = 42;
#endif

    printf("%d\n", myx->x);
    return 0;
}
/* test.c */

The const cast was what I wanted, but my attempts at coming up with an
lvalue failed.

Adam
0
Reply aburry (35) 9/14/2008 6:56:32 PM

vippstar@gmail.com writes:

> On Sep 14, 8:33 pm, abu...@ieee.org wrote:
>> /* test.c */
>> #include <stdlib.h>
>>
>> void f() {
>> struct X { const int x; };
>> struct X* myx = malloc(sizeof(struct X));
>> myx->x = 42;}
>>
>> /* end of test.c */
>>
>> $ gcc-4.3 -c test.c
>> test.c: In function 'f':
>> test.c:7: error: assignment of read-only member 'x'
>>
>> I would like x to be immutable once the struct is initialized. How do
>> I initialize it? I looked in the C FAQ but did not see anything that
>> addressed this specifically.
>>
>> Or perhaps I'm going about this the wrong way?
>
> Well, why would you want a const member in a struct?

Why is that your concern? Maybe each struct member has a different const
member value? Did you think about that? His reasons are non of your
concern.

> Here's a way to do this:
>
> #include <stdlib.h>
> #include <stddef.h>
>
> struct x { const int i; }
> struct x *p;
> void *q;
>
> p = q = malloc(sizeof *x)
> if(p)
>     *(int *)((unsigned char *)q + offsetof(struct x, i)) = yourvalue;
>
> free(p);
>
> another more simple one:
>
> *(int *)&p->i = yourvalue;
>

Did you not see the other posts pointing to the solution that you then
reposted? They were posted quite a while before your solution ...

http://groups.google.com/group/comp.lang.c/browse_thread/thread/a3e1002be09fe22c/0be80f24e3b00a40?lnk=gst&q=const+member+in+structure#0be80f24e3b00a40

Interestingly enough you're spot on ....

The thread solution:

*(int*) ((char*)v + offsetof(struct thing, b)) = b; 

Your solution:

*(int *)((unsigned char *)q + offsetof(struct x, i)) = yourvalue;

The reason I mention it is that I think its a travesty to keep
reinventing the wheel and posting the same thing when a good, well
explained solution is already out there.





0
Reply rgrdev (1814) 9/14/2008 7:01:49 PM

On Sep 14, 9:56 pm, abu...@ieee.org wrote:
> Thank you for the answers. I did search the group, but did not find
> helpful thread vipin found. Here's a solved test case:
>
> /* test.c */
> #include <stdlib.h>
> #include <string.h>
> #include <stdio.h>
>
> int main(int argc, char* argv[] ) {
>     struct X { const int x; };
>     struct X* myx = malloc(sizeof *myx);
>     /* myx->x = 42; */  /* error: assignment of read-only member 'x'
> */
>
>     /* solution 1: */
> #if 0
>     /* const initialization */
>     struct X myx_value = { 42 };
>     /* copy the const object */
>     memcpy(myx, &myx_value, sizeof *myx);
> #endif

Well, if you don't like the temporary variable, you can do this in
C99:

memcpy(myx, (struct X []){ /* .i = */ 42 }, sizeof *myx);

Or a C89/C99 solution

{ struct X temp_ = { 42 }; memcpy(...); }

(*with* the braces)
0
Reply vippstar (1211) 9/14/2008 7:02:52 PM

On Sep 14, 5:01=A0pm, Richard<rgr...@gmail.com> wrote:
> vipps...@gmail.com writes:
> > On Sep 14, 8:33 pm, abu...@ieee.org wrote:
> > > I would like x to be immutable once the struct is
> > > initialized. How do I initialize it?
> > Well, why would you want a const member in a struct?
>
> Why is that your concern? Maybe each struct member has a
> different const member value? Did you think about that? His
> reasons are non of your concern.

Unless he wanted to learn under what conditions someone would do a
thing like that? Did you think about that?

I actually do this kind of thing a lot. In C++ I can write:

class X {
public:
    X(int val) : x(val) {}
private:
    const int x;
};

And the compiler takes care of casting away the const during the
initialization. Similar to the following C example given elsewhere in
the thread:

struct X { const int x; } x =3D { 42 };

I like const members in situations where I want the object to be
parameterized at run-time, but where I do not expect/want the value to
change. I find that const members reduce the amount of thinking I have
to do. I can tell right away the member is not a variable I have to
really consider because it is a constant. Also, const allows me to say
what I mean; it is like extra documentation, only better.

In the particular case I was looking at today, I had an object that is
potentially shared. I want every object that has a reference to it to
know that their semantics are not going to change under their feet
(because the shared objects are const).

Hope that helps.

> Did you not see the other posts pointing to the solution
> that you then reposted? They were posted quite a while
> before your solution ...
>
> The reason I mention it is that I think its a travesty to
> keep reinventing the wheel and posting the same thing when
> a good, well explained solution is already out there.

Maybe his news server had not synced all the other responses yet. Did
you think about that?

The reason I mention it is that I think it's a travesty to chastise
someone for offering a helpful correct solution.

Adam
0
Reply aburry (35) 9/14/2008 10:14:42 PM

aburry@ieee.org writes:

> On Sep 14, 5:01 pm, Richard<rgr...@gmail.com> wrote:
>> vipps...@gmail.com writes:
>> > On Sep 14, 8:33 pm, abu...@ieee.org wrote:
>> > > I would like x to be immutable once the struct is
>> > > initialized. How do I initialize it?
>> > Well, why would you want a const member in a struct?
>>
>> Why is that your concern? Maybe each struct member has a
>> different const member value? Did you think about that? His
>> reasons are non of your concern.
>
> Unless he wanted to learn under what conditions someone would do a
> thing like that? Did you think about that?

Yes. I gave an example.

However the reason is immaterial IMO in this case.

>
> I actually do this kind of thing a lot. In C++ I can write:
>
> class X {
> public:
>     X(int val) : x(val) {}
> private:
>     const int x;
> };
>
> And the compiler takes care of casting away the const during the
> initialization. Similar to the following C example given elsewhere in
> the thread:
>
> struct X { const int x; } x = { 42 };
>
> I like const members in situations where I want the object to be
> parameterized at run-time, but where I do not expect/want the value to
> change.

Yes. As I said. I agree. Hence I said:

,----
| Maybe each struct member has a
| different const member value?
`----


> I find that const members reduce the amount of thinking I have
> to do. I can tell right away the member is not a variable I have to
> really consider because it is a constant. Also, const allows me to say
> what I mean; it is like extra documentation, only better.
>
> In the particular case I was looking at today, I had an object that is
> potentially shared. I want every object that has a reference to it to
> know that their semantics are not going to change under their feet
> (because the shared objects are const).
>
> Hope that helps.
>
>> Did you not see the other posts pointing to the solution
>> that you then reposted? They were posted quite a while
>> before your solution ...
>>
>> The reason I mention it is that I think its a travesty to
>> keep reinventing the wheel and posting the same thing when
>> a good, well explained solution is already out there.
>
> Maybe his news server had not synced all the other responses yet. Did
> you think about that?

Maybe. It seems certain c.l.c members have really slow news servers. Strange.

>
> The reason I mention it is that I think it's a travesty to chastise
> someone for offering a helpful correct solution.
>
> Adam

I did not chastise him.

I stand by my statement that repeated answers are not a good
thing. Frankly I have my suspicions but there you go. c.l.c has caused
me to be suspicious of peoples motives. Not a good thing I know and
possibly I need to learn to be more forgiving.

0
Reply rgrdev (1814) 9/14/2008 10:37:22 PM

aburry@ieee.org writes:

> void f() {
> struct X { const int x; };
> struct X* myx = malloc(sizeof(struct X));
> myx->x = 42;
> }

[...]

> I would like x to be immutable once the struct is initialized. How do
> I initialize it? I looked in the C FAQ but did not see anything that
> addressed this specifically.

Are you sure that you really want to make the member immutable?
If it is good enough to make the whole structure immutable, you
can do something like this:

struct X { int x; };

const struct X *make_X(int value)
{
  struct X *myx = malloc(sizeof *myx);
  myx->x = value;
  return myx;
}
-- 
char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa67f6aaa,0xaa9aa9f6,0x11f6},*p
=b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
0
Reply blp (3953) 9/15/2008 2:34:52 AM

In article <3cbadb38-6319-484c-b61f-d75c9f4338b7@2g2000hsn.googlegroups.com>,
 <aburry@ieee.org> wrote:

>struct X { const int x; };
>struct X* myx = malloc(sizeof(struct X));
>myx->x = 42;

What you want here is a struct with a member which is non-const until
you have set its value, and const thereafter.  A fairly natural approach
would be to declare a modifiable version of the struct, and then
use a cast to get the non-modifiable version:

struct X { const int x; };

struct X *f(void)
{
    struct modifiable_X { int x; };
    struct modifiable_X *myx = malloc(sizeof(*myx));
    myx->x = 42;
    return (struct X *)myx;
}

What is the opinion of the group on the legality of this?  Are the
two versions of the struct guaranteed to have the same representation?
Do the type-based aliasing rules make it undefined behaviour?

-- Richard
-- 
Please remember to mention me / in tapes you leave behind.
0
Reply richard91 (3683) 9/15/2008 11:54:33 AM

In article <066e34c7-6070-4386-bba8-3cbed74ead5d@z72g2000hsb.googlegroups.com>
<aburry@ieee.org> wrote:
>I like const members in situations where I want the object to be
>parameterized at run-time, but where I do not expect/want the value to
>change. ...

Unfortunately, as you have seen, const-qualified members are not
as well-supported in C as in other languages.

>In the particular case I was looking at today, I had an object that is
>potentially shared. I want every object that has a reference to it to
>know that their semantics are not going to change under their feet
>(because the shared objects are const).

In the general case, "const"-qualification does not tell you that
the object will not actually change.  For instance, consider:

    #include <stdio.h>

    void f(int *ip, const int *xp) {

        printf("*xp is %d\n", *xp);
        *ip = 42;
        printf("*xp is %d\n", *xp);
    }

You might expect this to print the same value every time, but
in fact, the line:

        *ip = 42;

is allowed to change *xp to 42, and does so in:

    int main(void) {
        int x = 0;
        f(&x, &x);
        return 0;
    }

To make the general case work, we need C99's "restrict" qualifier
as well: a "const int *restrict xp" cannot have both ip and xp
pointing to main()'s "x", in f().

(In this particular case, though, if "xp" had type "pointer to
struct X", where "struct X" has a const-qualified member C, the
compiler *is* allowed to assume that xp->C does not change at any
time.  So it would do what you wanted, if you could do what you
wanted in the first place.  This is mostly just another way to say
that specific cases can be less general than general cases.)
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40�39.22'N, 111�50.29'W)  +1 801 277 2603
email: gmail (figure it out)      http://web.torek.net/torek/index.html
0
Reply nospam252 (1722) 9/15/2008 12:29:58 PM

In article <galidp$hda$1@pc-news.cogsci.ed.ac.uk>
Richard Tobin <richard@cogsci.ed.ac.uk> wrote:
>What you want here is a struct with a member which is non-const until
>you have set its value, and const thereafter.  A fairly natural approach
>would be to declare a modifiable version of the struct, and then
>use a cast to get the non-modifiable version:
>
>struct X { const int x; };
>
>struct X *f(void)
>{
>    struct modifiable_X { int x; };
>    struct modifiable_X *myx = malloc(sizeof(*myx));
>    myx->x = 42;
>    return (struct X *)myx;
>}
>
>What is the opinion of the group on the legality of this?  Are the
>two versions of the struct guaranteed to have the same representation?
>Do the type-based aliasing rules make it undefined behaviour?

I think that there is enough wiggle room in the standard for an
"evil compiler" (DS9000 C) to cause it to fail, but I think that
it will actually work on all real implementations.

The main problem with this is that it is easy to goof up the
definition of the "struct modifiable_X", with negative consequences.
One can work around that by defining a macro for the contents of
"struct X":

    /* this part probably goes in a header somewhere */
    #define CONTENTS_OF_X \
        int nonconst_int; \
        char *nonconst_str; \
        CONST int const_int; \
        int another_int; \
        double and_a_double;
    #define CONST const

    struct X {
        CONTENTS_OF_X
    };

    /* while this part goes in x.c */
    struct X new_x(... params ...) {
        #undef CONST
        #define CONST /*empty*/
        struct modifiable_X { CONTENTS_OF_X };
        ... malloc and initialize and "return" as above ...
    }

This has the advantage of continuing to work when the contents of
a "struct X" are modified, but the disadvantage of being quite
ugly ... almost as ugly as that other not-quite-C language. :-)
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40�39.22'N, 111�50.29'W)  +1 801 277 2603
email: gmail (figure it out)      http://web.torek.net/torek/index.html
0
Reply nospam252 (1722) 9/15/2008 12:38:40 PM

On Sep 15, 10:29=A0am, Chris Torek <nos...@torek.net> wrote:
> In article <066e34c7-6070-4386-bba8-3cbed74ea...@z72g2000hsb.googlegroups=
..com>
>
> Unfortunately, as you have seen, const-qualified members are not
> as well-supported in C as in other languages.
>
> >In the particular case I was looking at today, I had an object that is
> >potentially shared. I want every object that has a reference to it to
> >know that their semantics are not going to change under their feet
> >(because the shared objects are const).
>
> In the general case, "const"-qualification does not tell you that
> the object will not actually change. =A0For instance, consider:
>
> =A0 =A0 #include <stdio.h>
>
> =A0 =A0 void f(int *ip, const int *xp) {
>
> =A0 =A0 =A0 =A0 printf("*xp is %d\n", *xp);
> =A0 =A0 =A0 =A0 *ip =3D 42;
> =A0 =A0 =A0 =A0 printf("*xp is %d\n", *xp);
> =A0 =A0 }
>
> You might expect this to print the same value every time, but
> in fact, the line:
>
> =A0 =A0 =A0 =A0 *ip =3D 42;
>
> is allowed to change *xp to 42, and does so in:
>
> =A0 =A0 int main(void) {
> =A0 =A0 =A0 =A0 int x =3D 0;
> =A0 =A0 =A0 =A0 f(&x, &x);
> =A0 =A0 =A0 =A0 return 0;
> =A0 =A0 }
>
> To make the general case work, we need C99's "restrict" qualifier
> as well: a "const int *restrict xp" cannot have both ip and xp
> pointing to main()'s "x", in f().
>
> (In this particular case, though, if "xp" had type "pointer to
> struct X", where "struct X" has a const-qualified member C, the
> compiler *is* allowed to assume that xp->C does not change at any
> time. =A0So it would do what you wanted, if you could do what you
> wanted in the first place. =A0This is mostly just another way to say
> that specific cases can be less general than general cases.)

Your example works exactly as I would expect. If I wanted x to be
immutable, in main() I would write:

    const int x =3D 0; /* note the const */

Now the call to f() generates a warning (in GCC 4.3).

The fact that *xp can be modified through ip in your example is not a
surprise at all. I would say you put your const in the wrong place.
That
is why I wanted the struct members to be const rather than just
using const pointers in the referers.

As an aside, I didn't want to make the entire struct const because I
may
add reference counting or something later.

But this is a bit off-topic now. And just to reiterate, the thread
that
vipin pointed out had the const cast syntax I was looking for, so the
original problem is solved. What I did in the end was create a
CONST_CAST
macro that I could use in my struct factory methods.

Adam
0
Reply aburry (35) 9/15/2008 4:30:11 PM

16 Replies
42 Views

(page loaded in 0.213 seconds)


Reply: