cpointersmisra

Efficient solutions for vector type definitions and data protection in C


How can I efficiently define custom types for dynamic arrays in C, ensuring const qualification for vectors data types without resorting to duplicate structures with constant methods and make_const functions? I am currently using the following implementation:

typedef struct
{
    int *             data;
    unsigned long int len;
    unsigned long int size;
} VecInt;

typedef struct
{
    const int *       data;
    unsigned long int len;
    unsigned long int size;
} CVecInt;

CVecInt make_const_VecInt ( VecInt * src )
{
    return (CVecInt) { .data = src->data, .len = src->len, .size = src->size };
}

Is there a more efficient or elegant solution to achieve the desired const qualification for the vector data types in C?

I want to avoid type casting due to potential risks associated with misalignment.

Note: MISRA-C 2012 drove this design, and solutions should at least take this code standard under consideration.

Clarification after reading the answers:

The application doesn't require dynamic reallocation. The purpose of working with these structures is to avoid messing with the lengths or arrays passed between functions.

Clarification, interfaces:

The software needs to store large files of signals and process them. The way to do it is always using interfaces like this:

CVecInt build_something ( VecInt * dst , CVecInt src );

Where dst comes from pre-allocated array like this:

static int palloc_signal [ SIZE ] = { 0 };
VecInt signal = { .data = palloc_signal , .len = 0 , .size = SIZE };

The build function layout is always like this:

CVecInt build_something ( VecInt * dst , CVecInt src )
{
    /* maths on src and writes to dst */

    return make_const_VecInt(dst);
}

Solution

  • MISRA C encourages the use of "opaque types" (Dir 4.8), so that's usually the way to go.

    That is, your header will have a forward declaration:

    typedef struct VecInt VecInt;
    

    But the struct definition is only placed in the C file. That way the caller won't know anything of the struct members nor can they access them directly. Your class/ADT will have to provide the interface in form of setters/getters etc.

    I recommend to use the above form and not to do like in the MISRA example by hiding a pointer behind a typedef. The caller will be dealing with pointers so you might as well let them know that, or they'll think they can copy the objects with assignment and get a hard copy, or start passing them by reference although they are pointers already.

    In order to deal with allocation and since you can't use malloc, you need to design a simple memory pool. Example. In the link, a centralized memory pool is used and an allocator function is passed as callback to the constructor of the opaque type. The alternative is to place the memory pool inside your ADT.

    Finally, you should seriously consider if you even need to this amount of abstraction for plain arrays. In the kind of applications where you usually encounter MISRA C, "keep it simple" is the key. And everything needs to be deterministic, so the presence of realloc-like behavior is usually a sign of bad design.