Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c make a copy of an array of const structs

I have an array of structs and some of the struct members are constant I would like to make a deep copy of the array. The copy will also have the same members of the struct be constants. How do I initialize the values into the new array without violating the const directive.

Below is an excerpt of my code. The actual program is quite long but I think I've included all the relevant code. In this example keys is declared as a global variable that I would like to make new instances from. I'm treating it sort of like a template with preset values.

When I try to compile this in Debian with gcc I get the following error:

main.c:216:9: error: assignment of read-only location ‘*(new_keys + (unsigned int)((unsigned int)index * 540u))’make: *** [main.o] Error 1

The makefile is using the following qualifier:

CFLAGS=-c -g -std=gnu99 -D_XOPEN_SOURCE=700

Interestingly. I can compile the same code in Xcode without an error or warning even with Xcode set for C Language Dialect GNU99[-std=gnu99]

I can make my code work simply by getting rid of the const keywords. But these values truly should be const. Once these arrays are initialized they will never change. I would like to understand how to do this properly and also I would like to know why this works in Xcode and not gcc.

Header File

typedef struct {
    char * name;
    char * alias;
    int number;
} pair_int_t;

typedef struct {
    const char * const name;
    const kind_t kind; // kind is enum type
    const pair_int_t * const enum_array; 
    bool received; 
    const char * const alias; 
} key_descriptor_t;

typedef key_descriptor_t keys_descriptor_t[_END_OF_KEYS+1];

body of .c program before main()

const pair_int_t command_list[] = {
    {.name = "numeric data response",        .alias = "num",         .number = RES_NUMERIC_DATA},
    {.name = "read attribute",               .alias = "readat",      .number = CMD_READ_ATTR},
    //..
}

// declare a global variable *keys* with constants assigned to it
key_descriptor_t keys[] = {
    [_COMMAND]          = {.name = "command",           .kind = ENUMERATED, .alias = "com",    .enum_array = command_list},
    [_RESPONSE]         = {.name = "response",          .kind = ENUMERATED, .alias = "res",    .enum_array = command_list},
    [_UNIT]             = {.name = "unit number",       .kind = NUMBER,     .alias = "uni",    .enum_array = NULL},
//..
}

int initialize_new_keys(keys_descriptor_t new_keys) {
    int index;
    for (index = _FIRST_KEY; index <= _END_OF_KEYS; index++){
        new_keys[index] = keys[index];      // line 216, keys is a global variable  
    }
    return index;
}

main program

int main(int argc, const char * argv[]){
    keys_descriptor_t 2nd_set_of_keys;
    initialize_new_keys(2nd_set_of_keys);
}
like image 437
Timothy Vann Avatar asked Nov 26 '25 03:11

Timothy Vann


1 Answers

You can only initialize const variables at the time you define them. Creating an array with const members, sending it to a function, and then trying to assign to those members signifies that you didn't really mean const in the first place. There's no syntax in C for "const except for the first assignment".

A reasonable alternative is to make an opaque type, encapsulate the definition in a separate translation unit, and then access all the members purely through an interface of functions. That way, even though things may not be const, they still can't be changed (except through deliberate subversion, which is at least no better than not calling things const in the first place) because no code that uses them has access to the definition of the struct, or therefore to the members.

EDIT: In response to the question in the comment, "is there a way to create and initialize one complex constant variable to the value of another existing constant variable of the same type?", there sure is - just initialize it normally:

struct mystruct {
    const int a;
    const int b;
};

static const struct mystruct m = {1, 2};

int main(void) {
    struct mystruct n = m;
    return 0;
}

Obviously the more complex your struct is, the more complex this may become.

like image 155
Crowman Avatar answered Nov 27 '25 22:11

Crowman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!