Problem: assignment of read-only member

A

aburry

/* 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
 
H

Harald van Dijk

/* 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.
 
S

s0suk3

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?

Sebastian
 
S

sh.vipin

/* 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

please go through following discussions
http://groups.google.com/group/comp...&q=const+member+in+structure#0be80f24e3b00a40
 
H

Harald van Dijk

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.
 
B

Barry Schwarz

/* 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.
 
V

vippstar

/* 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;
 
A

aburry

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
 
R

Richard

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...&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.
 
V

vippstar

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)
 
A

aburry

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 = { 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
 
R

Richard

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.


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.
 
B

Ben Pfaff

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;
}
 
R

Richard Tobin

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
 
C

Chris Torek

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.)
 
C

Chris Torek

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. :)
 
A

aburry

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


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.)

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

const int x = 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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top