Initializing struct containing arrays
Asked Answered
T

2

8

I have a struct in C, which members are float arrays. I want to initialize it during compile time like this:

typedef struct curve {                                                                                         
    float *xs;                                                                                             
    float *ys;                                                                                             
    int    n;                                                                                              
} curve;                                                                                                       

curve mycurve1 = {                                                                                             
    {1, 2, 3},                                                                                                     
    {4, 2, 9},                                                                                                     
    3                                                                                                              
};

curve mycurve2 = {
    {1, 2, 3, 4},
    {0, 0.3, 0.9, 1.5},
    4
};                                                                                                              

But I get compile errors.

One possible solution might be to use arrays and not pointers in the struct. This is the accepted answer of a very similar question from here: https://mcmap.net/q/571278/-declaring-int-array-inside-struct, but the problem with that approach is that I don't know the array size at typedef time. Not only that, I might want to initialize another, bigger curve.

Another approach might be with malloc, but I find that overkill, because I know the array size at compile time and I don't need it to change during run-time.

I don't know another approaches, which might be useful. Maybe casting array to pointer?? - I don't really know how I would approach that.

Terminable answered 12/2, 2020 at 19:58 Comment(4)
I don't profess to be a C or C++ expert, but unless typedefs utilize macros, isn't it all happening at compile-time?Nagey
I guess, I might be using the terms slightly incorrectly. What I meant that I want to populate the array in the code and from that point, I don't need it changed at runtime.Terminable
JonnyRobbie, with "I want to populate the array in the code and from that point, I don't need it changed at runtime", consider making const with const curve mycurve1 = { ...Oconner
What does the const keyword do? (besides not allowing any changes to that array). Is is essentially a compiler hint for optimization? Or does it do someting else? Why should I consider it?Terminable
O
7

You may not initialize a scalar object like a pointer with a braced list that contains several initializers.

But you can use compound literals.

Here is a demonstrative program.

#include <stdio.h>

typedef struct curve {                                                                                         
    float *xs;                                                                                             
    float *ys;                                                                                             
    int    n;                                                                                              
} curve;                                                                                                       

int main(void) 
{
    curve mycurve1 = 
    {                                                                                             
        ( float[] ){ 1,  2, 3 },                                                                                                     
        ( float[] ){ 4,  2, 9 },                                                                                                     
        3
    };

    curve mycurve2 = 
    {
        ( float[] ){ 1, 2, 3, 4 },
        ( float[] ){ 0, 0.3, 0.9, 1.5 },
        4
    };

    for ( int i = 0; i < mycurve1.n; i++ )
    {
        printf( "%.1f ", mycurve1.xs[i] );
    }
    putchar( '\n' );

    for ( int i = 0; i < mycurve2.n; i++ )
    {
        printf( "%.1f ", mycurve2.ys[i] );
    }
    putchar( '\n' );

    return 0;
}

Its output is

1.0 2.0 3.0 
0.0 0.3 0.9 1.5 
Okechuku answered 12/2, 2020 at 20:9 Comment(3)
So, you're essentially casting the braced list as a float array, which is compatible with assigning to a pointer? Is that somewhat correct?Terminable
@Terminable There is no casting. The compound literals create in main arrays with the automatic storage duration and pointers to first elements of this arrays are assigned to data members of the structure.Okechuku
@Terminable It's not a cast, although the syntax is similar; look up "compound literals". It achieves basically the same thing as what I did in my answer (which I deleted because this is better); it's like making a separate array and initializing the member point to point to it, except here the compiler does it for you and doesn't give it a name to clutter your namespace.Cherriecherrita
O
3

A suggested take on @Vlad from Moscow good answer.

Use const when constant

because I know the array size at compile time and I don't need it to change during run-time.

Consider const curve mycurve1 = .... This allows for select optimizations, identifies mis-use and allows passing &mycurve1 to bar(const curve *). Also with const float [... allows passing mycurve1.xs to foo(const float *).

Avoid magic numbers

#define CURVE1_N 3
const curve mycurve1 = {
  ( const float[CURVE1_N] ){ 1,  2, 3 },
  ( const float[CURVE1_N] ){ 4,  2, 9 },  
  CURVE1_N
};
Oconner answered 12/2, 2020 at 20:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.