Path: csiph.com!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: About struct fields that are assigned once and never changed again Date: Wed, 15 Dec 2021 00:25:33 -0800 Organization: A noiseless patient Spider Lines: 106 Message-ID: <864k7a5l6q.fsf@linuxsc.com> References: <56b509db-c5ba-4020-b1e4-6250d41a706cn@googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Injection-Info: reader02.eternal-september.org; posting-host="87e29f815fda23c6ea6c6dd22211e8ad"; logging-data="12712"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19yczn64wGykgv2vaCuB9Mgy0jKlVOKJT4=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:zudLzYqvzDRotLqhM5zjoNJFAEA= sha1:barRn4gJppeuvELIue4mFoFbI5s= Xref: csiph.com comp.lang.c:163840 Oğuz writes: > I have an array of structs as follows: > > struct { > struct { > int value; > bool in_use; > } values[MAX_INPUTS_SIZE]; > size_t size; > } inputs; > > and a function that initializes it with user-supplied data. After > the function is run, nothing in the program alters `inputs.size' > or `inputs.values[N].value'. Now I have two questions about this > that I can't answer myself: > > i) Is there a way to treat size and value fields as `const' > outside that function? Here is one well-known technique. Step one: write a .h file that defines the struct type and declares an extern pointer variable and a function to do the initializing. Note that the type of the pointer variable says it points to a 'const' struct: /* file provide-struct.h */ #ifndef H_PROVIDE_STRUCT_H #define H_PROVIDE_STRUCT_H #include enum { MAX_INPUTS_SIZE = 10000 }; typedef struct { struct { int value; _Bool in_use; } values[ MAX_INPUTS_SIZE ]; size_t size; } Values_Array_Struct; extern const Values_Array_Struct *const inputs; extern void initialize_inputs_struct( const char * ); #endif/*H_PROVIDE_STRUCT_H*/ Step two: write a .c file that defines a static instance of the struct, along with a definition for the pointer variable and the initializing function. Because the static struct instance is declared (and defined) without using 'const', the initialization function can write its values with no problem: /* file provide-struct.c */ #include "provide-struct.h" static Values_Array_Struct inputs_w; const Values_Array_Struct *const inputs = &inputs_w; void initialize_inputs_struct( const char *data ){ inputs_w.size = 3; inputs_w.values[0].value = 3; inputs_w.values[2].value = 4; inputs_w.values[0].in_use = 1; inputs_w.values[2].in_use = 1; /* ... etc ... */ } Step three: write client code in a different .c file, which can initialize the struct by calling the initialization function, and access the struct using the declared pointer variable. Because the pointer variable's type says it points to a 'const' struct, the struct cannot be modified by client code: /* file provide-struct-client.c */ #include "provide-struct.h" #define inputs (*inputs) unsigned example_client( const char *data ){ unsigned i; unsigned total = 0; initialize_inputs_struct( data ); /* Any attempt to modify a field of 'inputs' will be flagged by the compiler. */ for( i = 0; i < inputs.size; i++ ){ if( inputs.values[i].in_use ){ total += inputs.values[i].value; } } return total; } Incidentally, notice the #define in this file. This macro definition uses an idiom so the struct can be accessed using '.' rather than '->'. Doing that lets the code look a little nicer but isn't an essential element of the basic idea.