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.