Reputation: 13738
I have tried;
void *malloc(unsigned int);
struct deneme {
const int a = 15;
const int b = 16;
};
int main(int argc, const char *argv[])
{
struct deneme *mydeneme = malloc(sizeof(struct deneme));
return 0;
}
And this is the compiler's error:
gereksiz.c:3:17: error: expected ':', ',', ';', '}' or '__attribute__' before '=' token
And, also this;
void *malloc(unsigned int);
struct deneme {
const int a;
const int b;
};
int main(int argc, const char *argv[])
{
struct deneme *mydeneme = malloc(sizeof(struct deneme));
mydeneme->a = 15;
mydeneme->b = 20;
return 0;
}
And this is the compiler's error:
gereksiz.c:10:5: error: assignment of read-only member 'a'
gereksiz.c:11:5: error: assignment of read-only member 'b'
And neither got compiled. Is there any way to initialize a const variable inside a struct when allocation memory with malloc?
Upvotes: 34
Views: 42626
Reputation: 81159
The Standard uses the const
keyword as a weird hybrid between an lvalue qualifier and a storage class, but doesn't make clear which meaning is applicable to struct members.
If one has a structure s
of type struct S
with member m
of type T
, the construct s.foo
takes an lvalue of type struct S
and derives from it an lvalue of type T
. If T
contains a qualifier, that modifier will affect the lvalue thus produced.
The Standard certainly recognizes the possibility that code may take an lvalue which is not const
-qualified, derive from that an lvalue that is const
-qualified, derive from that one which--like the original--is not, and then use the latter lvalue to modify the object. What is unclear is whether the use of a const
modifier on a struct member affects the underlying storage class of an object, or whether it would merely cause a const
modifier to be applied to any lvalue formed using the member-access operator. I think the latter interpretation makes a lot more sense, since the former leads to many ambiguous and unworkable corner cases, but I don't think the Standard makes clear which interpretation is supposed to apply. Since all situations whose behavior would be defined under the former interpretation would be defined identically under the latter, I see no reason the authors of the Standard wouldn't have regarded the latter interpretation as superior, but they might have wanted to leave open the possibility that in some cases, on some implementations, the former interpretation might offer some advantage implementation the Committee hadn't foreseen.
Upvotes: 0
Reputation: 213809
To expand on the answer by @Chris Dodd, I've been reading through the "language-lawyer" details of the standard and it appears that this code is well-defined:
struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));
Alternatively, to dynamically create a complete struct object that's const qualified:
const struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));
const struct deneme *read_only = mydeneme;
Rationale:
The first thing one needs to establish when getting to the bottom of this, is if a so-called lvalue has a type and if so, does that type come with qualifiers or not. This is defined in C11 6.3.2.1/1:
An lvalue is an expression (with an object type other than void) that potentially designates an object; if an lvalue does not designate an object when it is evaluated, the behavior is undefined. When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a constqualified 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 constqualified type.
So clearly a lvalue does not only have a type, but also qualifiers. It is not a modifiable lvalue if it is const-qualified or if it is a struct with const-qualified members.
Moving on to the rules of "strict aliasing" and effective type, C11 6.5/7:
The effective type of an object for an access to its stored value is the declared type of the object, if any.87) If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value. If a value is copied into an object having no declared type using
memcpy
ormemmove
, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.
- Allocated objects have no declared type.
This means that the allocated chunk returned by malloc has no effective type until something is stored inside that memory location through a lvalue write access, either through assignment or memcpy
. It then gets the effective type of the lvalue used in that write access.
Notably, the type of the pointer pointing at that memory location is completely irrelevant. It might as well have been a volatile bananas_t*
since it isn't used to access the lvalue (at least not yet). Only the type used for lvalue access matters.
Now this is where it gets fuzzy: it might matter if this write access is done through a modifiable lvalue or not. The rule of effective type above doesn't mention qualifiers and the "strict aliasing rule" as such doesn't care about if an object is qualified or not ("type" may alias "qualified type" and vice versa).
But there are other cases where it might matter if the effective type is read-only or not: most notably if we later attempt to do a non-qualified lvalue access to an object which effective type is const-qualified. (C11 6.7.3/6 "If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.") From the previously quoted part above lvalues, it makes sense for effective type to have a qualifier, even if the standard doesn't mention that explicitly.
Therefore to be absolutely sure, we have to get the type used for the lvalue access right. If the whole object is meant to be read-only, then the second snippet on top of this post should be used. Otherwise, if it is read/write (but potentially with qualified members), the first snippet should be used. Then it can never go wrong no matter how you read the standard.
Upvotes: 2
Reputation: 126203
You need to cast away the const to initialize the fields of a malloc'ed structure:
struct deneme *mydeneme = malloc(sizeof(struct deneme));
*(int *)&mydeneme->a = 15;
*(int *)&mydeneme->b = 20;
Alternately, you can create an initialized version of the struct and memcpy it:
struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));
You can make deneme_init
static and/or global if you do this a lot (so it only needs to be built once).
Explanation of why this code is not undefined behaviour as suggested by some of the comments, using C11 standard references:
This code does not violate 6.7.3/6 because the space returned by malloc
is not "an object defined with a const-qualified type". The expression mydeneme->a
is not an object, it is an expression. Although it has const
-qualified type, it denotes an object which was not defined with a const-qualified type (in fact, not defined with any type at all).
The strict aliasing rule is never violated by writing into space allocated by malloc
, because the effective type (6.5/6) is updated by each write.
(The strict aliasing rule can be violated by reading from space allocated by malloc
however).
In Chris's code samples, the first one sets the effective type of the integer values to int
, and the second one sets the effective type to const int
, however in both cases going on to read those values through *mydeneme
is correct because the strict-aliasing rule (6.5/7 bullet 2) permits reading an object through an expression which is equally or more qualified than the effective type of the object. Since the expression mydeneme->a
has type const int
, it can be used to read objects of effective type int
and const int
.
Upvotes: 50
Reputation: 4433
I disagree with Christ Dodd's answer, since I think his solution gives Undefined Behaviour according to the standards, as others said.
To "work-around" the const
qualifier in a way that does not invoke undefined behaviour, I propose the following solution:
void*
variable initialized with a malloc()
call. struct deneme
and initialize it in some way that const
qualifier does not complain (that is, in the declaration-line itself). memcpy()
to copy the bits of the struct deneme
object to the void*
object. struct deneme
object and initialize it to the (void*)
variable, previously cast to (struct deneme *)
. So, my code would be:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct deneme {
const int a;
const int b;
};
struct deneme* deneme_init(struct deneme data) {
void *x = malloc(sizeof(struct deneme));
memcpy(x, &data, sizeof(struct deneme));
return (struct deneme*) x;
}
int main(void) {
struct deneme *obj = deneme_init((struct deneme) { 15, 20, } );
printf("obj->a: %d, obj->b: %d.\n", obj->a, obj->b);
return 0;
}
Upvotes: 0
Reputation: 37
Interesting I found this C99 way is working in clang but not in gcc
int main(int argc, const char *argv[])
{
struct deneme *pmydeneme = malloc(sizeof(struct deneme));
*pmydeneme = (struct deneme) {15, 20};
return 0;
}
Upvotes: 2
Reputation: 489
Have you tried to do like this:
int main(int argc, const char *argv[])
{
struct deneme mydeneme = { 15, 20 };
struct deneme *pmydeneme = malloc(sizeof(struct deneme));
memcpy(pmydeneme, &mydeneme , sizeof(mydeneme));
return 0;
}
I have not tested but the code seems correct
Upvotes: 11