How to initialize const in a struct in C (with malloc)

后端 未结 6 436
谎友^
谎友^ 2020-11-28 08:46

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         


        
6条回答
  •  时光说笑
    2020-11-28 09:31

    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 or memmove, 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.

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

提交回复
热议问题