Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #163814 > unrolled thread

About struct fields that are assigned once and never changed again

Started byOğuz <oguzismailuysal@gmail.com>
First post2021-12-13 02:49 -0800
Last post2021-12-15 12:38 +0300
Articles 13 — 8 participants

Back to article view | Back to comp.lang.c


Contents

  About struct fields that are assigned once and never changed again Oğuz <oguzismailuysal@gmail.com> - 2021-12-13 02:49 -0800
    Re: About struct fields that are assigned once and never changed again Oğuz <oguzismailuysal@gmail.com> - 2021-12-13 02:50 -0800
    Re: About struct fields that are assigned once and never changed again Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2021-12-13 03:05 -0800
    Re: About struct fields that are assigned once and never changed again David Brown <david.brown@hesbynett.no> - 2021-12-13 12:12 +0100
      Re: About struct fields that are assigned once and never changed again Oğuz <oguzismailuysal@gmail.com> - 2021-12-13 09:01 -0800
        Re: About struct fields that are assigned once and never changed again David Brown <david.brown@hesbynett.no> - 2021-12-13 18:37 +0100
        Re: About struct fields that are assigned once and never changed again scott@slp53.sl.home (Scott Lurndal) - 2021-12-13 18:06 +0000
    Re: About struct fields that are assigned once and never changed again Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-12-13 16:12 +0000
    Re: About struct fields that are assigned once and never changed again James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-12-13 12:09 -0500
    Re: About struct fields that are assigned once and never changed again Guillaume <message@bottle.org> - 2021-12-13 19:15 +0100
      Re: About struct fields that are assigned once and never changed again James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-12-13 13:25 -0500
    Re: About struct fields that are assigned once and never changed again Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-12-15 00:25 -0800
      Re: About struct fields that are assigned once and never changed again Oğuz <oguzismailuysal@gmail.com> - 2021-12-15 12:38 +0300

#163814 — About struct fields that are assigned once and never changed again

FromOğuz <oguzismailuysal@gmail.com>
Date2021-12-13 02:49 -0800
SubjectAbout struct fields that are assigned once and never changed again
Message-ID<56b509db-c5ba-4020-b1e4-6250d41a706cn@googlegroups.com>
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?
ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?

[toc] | [next] | [standalone]


#163815

FromOğuz <oguzismailuysal@gmail.com>
Date2021-12-13 02:50 -0800
Message-ID<f6e5e404-1336-40b6-a38a-a63bc7548fbdn@googlegroups.com>
In reply to#163814
On Monday, December 13, 2021 at 1:49:20 PM UTC+3, Oğuz wrote:
> I have an array of structs as follows: 

Sorry, I meant "a struct", not "an array of structs".

[toc] | [prev] | [next] | [standalone]


#163816

FromMalcolm McLean <malcolm.arthur.mclean@gmail.com>
Date2021-12-13 03:05 -0800
Message-ID<95e8b1c9-b1a4-4743-afb5-478f18b93ea2n@googlegroups.com>
In reply to#163814
On Monday, 13 December 2021 at 10:49:20 UTC, oguzism...@gmail.com wrote:
> 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? 
> ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?
>
i) C doesn't allow that. It doesn't have a concept of a "constructor".
ii) It's unlikely to make any difference to performance. If a "variable" is known at compile time,
then the compiler can propagate it, and often do some significant optimisations. But if it is
read in from a parameter list, then this isn't possible.

[toc] | [prev] | [next] | [standalone]


#163817

FromDavid Brown <david.brown@hesbynett.no>
Date2021-12-13 12:12 +0100
Message-ID<sp79qr$uop$1@dont-email.me>
In reply to#163814
On 13/12/2021 11:49, Oğuz wrote:
> I have an array of structs as follows:
> 
> struct {
>   struct {
>     int value;
>     bool in_use;
>   } values[MAX_INPUTS_SIZE];
>   size_t size;
> } inputs;
> 

(Google's broken interface messes up formatting, especially indentation.
 If you are going to use Usenet a lot then I strongly recommend getting
a proper newsreader client and server.  They are freely available, and
work much better.)

> 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?
> ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?
> 

Your friend here is the online compiler at <https://godbolt.org>.  You
can pick your language, your compiler, your flag set (for experimenting
with gcc, I'd recommend something like "-std=c11 -Wall -Wextra
-Wpedantic .O2").  Then you put in your test code and see the effect and
the error messages immediately.  It's great for trying out different things.

In C, you can think of two kinds of "const".  One is objects that are
defined as "const" - these must be given initial values, and you are not
allowed to change them later.  That is not suitable here, as you don't
want to write out an initialiser for the whole inputs, you want to fill
it with a loop.

The other is to use a const-qualified lvalue (such as a "pointer to
const T") to access the data.  This will not let the compiler do much
extra in the way of optimisation, but it can reduce the risk of mistakes
in your code (or rather, it can increase the likelihood of certain
mistakes being caught by the compiler.


As for performance, this all depends on what you are doing with the
data.  If you hare using it heavily and might be doing so from different
cpu cores, then it can make sense to separate the fixed data from the
the variable data in different arrays.  This will make it possible to
have shared read-only cache lines for the read-only data, rather than
more costly exclusive read-write cache lines.  But don't worry about
that unless you are actually measuring performance and see that it is
slower than you want.

[toc] | [prev] | [next] | [standalone]


#163820

FromOğuz <oguzismailuysal@gmail.com>
Date2021-12-13 09:01 -0800
Message-ID<221ac97e-0500-45dc-867a-b469f3816712n@googlegroups.com>
In reply to#163817
On Monday, December 13, 2021 at 2:12:39 PM UTC+3, David Brown wrote:
> (Google's broken interface messes up formatting, especially indentation. 
> If you are going to use Usenet a lot then I strongly recommend getting 
> a proper newsreader client and server. They are freely available, and 
> work much better.)

Sorry. I tried Pan before but didn't like it. Which one do you use?

[toc] | [prev] | [next] | [standalone]


#163824

FromDavid Brown <david.brown@hesbynett.no>
Date2021-12-13 18:37 +0100
Message-ID<sp80dm$v96$1@dont-email.me>
In reply to#163820
On 13/12/2021 18:01, Oğuz wrote:
> On Monday, December 13, 2021 at 2:12:39 PM UTC+3, David Brown wrote:
>> (Google's broken interface messes up formatting, especially indentation. 
>> If you are going to use Usenet a lot then I strongly recommend getting 
>> a proper newsreader client and server. They are freely available, and 
>> work much better.)
> 
> Sorry. I tried Pan before but didn't like it. Which one do you use?
> 

I don't want to start yet another thread or branch about newsreaders -
if my answer here doesn't suit you, there are many previous ones in this
group and others.  (And if there is one thing google groups is good for,
it is searching old posts.)  My email is also valid.

I use Thunderbird for news (and email, and calender), on Windows and
Linux.  I don't know if it is the "best" Usenet client - that will
depend on personal preferences.  But it has been fine for me since about
version 1.  I use news.eternal-semptember.org as the server - it's free,
and has all the newsgroups I need.  (I also use news.gmane.org as an
alternative interface to a number of mailing lists.)

[toc] | [prev] | [next] | [standalone]


#163825

Fromscott@slp53.sl.home (Scott Lurndal)
Date2021-12-13 18:06 +0000
Message-ID<wYLtJ.26162$G996.16069@fx31.iad>
In reply to#163820
=?UTF-8?B?T8SfdXo=?= <oguzismailuysal@gmail.com> writes:
>On Monday, December 13, 2021 at 2:12:39 PM UTC+3, David Brown wrote:
>> (Google's broken interface messes up formatting, especially indentation. 
>> If you are going to use Usenet a lot then I strongly recommend getting 
>> a proper newsreader client and server. They are freely available, and 
>> work much better.)
>
>Sorry. I tried Pan before but didn't like it. Which one do you use?

What's not to like about PAN, particularly when compared with GG?

I use 'xrn'.

[toc] | [prev] | [next] | [standalone]


#163818

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2021-12-13 16:12 +0000
Message-ID<87fsqw32mi.fsf@bsb.me.uk>
In reply to#163814
Oğuz <oguzismailuysal@gmail.com> writes:

> I have a struct as follows:
(corrected)
>
> 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?

Hmm...  You could have two versions, a public one and a private one (the
pre-processor is your friend here).  The public one has const on value
and size, the private one, visible to the function that sets up the
data, does not.

I have a feeling this is not 100% permitted, but I don't have time to
check.

> ii) If there is, would it make any difference performance-wise? Would
> it allow the compiler to optimize more?

Pass.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#163822

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-12-13 12:09 -0500
Message-ID<sp7uo8$spv$2@dont-email.me>
In reply to#163814
On 12/13/21 5:49 AM, Oğuz wrote:
> 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?

Sort-of. What you can do is use access functions to give a const
interface to the data, with the file containing the initialization
routine and access functions being the only place which has non-const
access to the data. The key to making this work is to declaring an
opaque struct type for use outside of that, with those functions being
the only ones that know the actual struct layout:

mystruct.h:
#ifndef H_MYSTRUCT
    #define H_MYSTRUCT

    struct mystruct;

    int get_value(const struct mystruct*, size_t);
    size_t get_size(const struct mystruct*);
    const struct mystruct *initialize_mystruct
        (size_t size, int[static size]);
#endif

mystruct.c:
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "mystruct.h"

// Since this approach requires that mystruct objects be dynamically
// allocated, I decided to save some space by moving size to the
// beginning, allowing values to be a flexible array member. As a result
// you don't have to impose any arbitrary upper limit on the size;
// you're  limited only by the amount of availale memory. You don't have
// to do this.

struct mystruct {
    size_t size;
    struct {
        int value;
        bool in_use;
    } values[];
};

int get_value(const struct mystruct *inputs, size_t index)
{
    // I'm not sure how you want to use in_use, but it should probably
    // have some role to play in this function.

    if(inputs == NULL || index >= inputs->size)
    {
         // Error handling
         return 0;
    }
    return inputs->values[index].value;
}

size_t get_size(const struct mystruct *inputs)
{
    if(inputs == NULL)
    {
        // Error handling
        return 0;
    }
    return inputs->size;
}

const struct mystruct *initialize_mystruct(size_t size, int values[size])
{
    struct mystruct *inputs = malloc(sizeof(*inputs) +
        size * sizeof(inputs->values[0]));
    if(inputs == NULL)
    {
        // Error handling
        return NULL;
    }
    inputs->size = size;
    memcpy(inputs->values, values, size*sizeof(values[0]));
    for(size_t val=0; val<size; val++)
        inputs->values[val].in_use = false;
    return inputs;
}

The above code compiles on my desktop without error messages, but I have
not tested it. My apologies for any errors I might have made.

Note that the members of mystruct are completely unknown outside of
mystruct.c, and code outside of mystruct.c never sees anything other
than const-qualified pointers to struct mystruct.

> ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?

I wouldn't expect major benefits from this approach other than making
the contents of struct mystruct inaccessible.

[toc] | [prev] | [next] | [standalone]


#163826

FromGuillaume <message@bottle.org>
Date2021-12-13 19:15 +0100
Message-ID<sp82kn$66h$1@gioia.aioe.org>
In reply to#163814
Le 13/12/2021 à 11:49, Oğuz a écrit :
> 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?

There is an 'ugly' way of doing it.

You can qualify the struct itself (if it's going to be entirely const), 
or any member of it 'const'.

Now, to initialize members at run-time, you would use pointers and 
casts. For instance:

*(size_t *)(&inputs.size) = 10;

This will "override" the const qualifier. Clunky way of accessing struct 
members, but it "works".

Note that this is likely undefined behavior, strictly speaking (I'll 
have to check in the standard.) Depending on implementation, anything 
qualified "const" may not even be put in a writable memory area. (I 
think it's more likely to happen if the whole struct is qualified 
"const" rather than individual members, but those are just assumptions...)

> ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?

I doubt it. Maybe in some specific cases.

But anyway, the only right way of initializing "const" objects is to 
initialize them at compile time (meaning, with initializers.)

If you want to "protect" struct members outside of specific functions, 
nothing strictly conforming in C will work. You may try the above 
approach, which is kind of messy and not guaranteed to be correct except 
if you're lucky.

[toc] | [prev] | [next] | [standalone]


#163827

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-12-13 13:25 -0500
Message-ID<sp836b$m7i$1@dont-email.me>
In reply to#163826
On 12/13/21 1:15 PM, Guillaume wrote:
...
> You can qualify the struct itself (if it's going to be entirely const), 
> or any member of it 'const'.
> 
> Now, to initialize members at run-time, you would use pointers and 
> casts. For instance:
> 
> *(size_t *)(&inputs.size) = 10;
> 
> This will "override" the const qualifier. Clunky way of accessing struct 
> members, but it "works".
> 
> Note that this is likely undefined behavior, strictly speaking (I'll 
> have to check in the standard.)

Correct:
"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." (6.7.3p6).

*(size_t*)(&inputs.size) creates an lvalue of type "size_t", which is
not const-qualified. Also note that attempting to modify an object
through an lvalues that IS const-qualified is a constraint violation.

Depending on implementation, anything
> qualified "const" may not even be put in a writable memory area. (I 
> think it's more likely to happen if the whole struct is qualified 
> "const" rather than individual members, but those are just assumptions...)

[toc] | [prev] | [next] | [standalone]


#163840

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-12-15 00:25 -0800
Message-ID<864k7a5l6q.fsf@linuxsc.com>
In reply to#163814
Oğuz <oguzismailuysal@gmail.com> 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 <stddef.h>

    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.

[toc] | [prev] | [next] | [standalone]


#163843

FromOğuz <oguzismailuysal@gmail.com>
Date2021-12-15 12:38 +0300
Message-ID<spcd24$5i3$1@oguzismail.eternal-september.org>
In reply to#163840
On 12/15/21 11:25 AM, Tim Rentsch wrote:
> 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 <stddef.h>
> 
>      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.
> 

This and others all worked fine and I learned a lot from this thread, 
thank you all. And for the record, neither making those fields const nor 
separating `inputs' into two arrays made the program any faster than it 
already was :/

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web