C initialize const struct member with existing const variable - c

I'm using default C under gcc.
My code:
typedef struct _OpcodeEntry OpcodeEntry;
//
struct _OpcodeEntry
{
unsigned char uOpcode;
OpcodeMetadata pMetadata;
};
//
const OpcodeMetadata omCopyBytes1 = { 1, 1, 0, 0, 0, 0, &CopyBytes };
const OpcodeEntry pOpcodeTable[] =
{
{ 0x0, omCopyBytes1 },
};
Errors:
error: initializer element is not constant
error: (near initialization for 'pOpcodeTable[0].pMetadata')
If I change omCopyBytes1 to what it's actually set to in the above line, the code compiles fine. What am I doing wrong?

You cannot use omCopyBytes1 to initialize a member of pOpcodeTable[] array, because omCopyBytes1 is a variable that is run-time constant, not a compile-time constant. Aggregate initializers in C must be compile-time constants, that's why the code from your post does not compile.
As a variable, omCopyBytes1 has its own place in memory, which is initialized to an array of items. You can use such variable by a pointer, like this:
struct _OpcodeEntry {
unsigned char uOpcode;
const OpcodeMetadata *pMetadata;
};
...
const OpcodeEntry pOpcodeTable[] = {
{ 0x0, &omCopyBytes1 }, // This should work
};
Alternatively, you can make it a preprocessor constant:
#define omCopyBytes1 { 1, 1, 0, 0, 0, 0, &CopyBytes }
If defined in this way, the omCopyBytes1 would no longer be a variable: it would be a preprocessor definition that vanishes before the compiler is done. I would recommend against the preprocessor method, but it's there in case you must do it.

In C, initializers for objects of static storage duration must be constant expressions. A const-qualified variable is not a constant expression.

Related

Two dimensional array initilization in Mplab XC16

I have defined the two-dimensional array inside the typedef struct as,
#define MAX_STAGES_IIR 20
typedef struct {
float A[MAX_STAGES_IIR]; // Input Gain
float a[MAX_STAGES_IIR][2]; // input stage coff
float b[MAX_STAGES_IIR][3]; // output stage coff
// float B[MAX_STAGES_IIR]; // output gain
float Xdash[MAX_STAGES_IIR][2];
float iir_k[MAX_STAGES_IIR];//
float iir_r[MAX_STAGES_IIR];//
float iir_outStage[MAX_STAGES_IIR];
}IIRFilter;
When assigning values to the array use this method,
IIRFilter BpfHX_iir1;
BpfHX_iir1.A = { 0.131726 , 0.131726 , 0.12435, 1.0f }; // gain
BpfHX_iir1.a = {{-1.63410, 0.82662},{-1.87089, 0.91410},{-1.6652, 0.7513}}; // a
BpfHX_iir1.b = {{1, 0, -1},{1, 0, -1},{1, 0, -1}}; // b
but the Mplab XC16 can't build this method.
gives the same error message for all three arrays,
expected expression before '{' token
expected expression before '{' token
expected expression before '{' token
what is the reason for that?
is there a correct method to do that in XC16?
Initialize BpfHX_iir1 in the usual way.
Use float constants and not double ones for the float array. (Append an f.)
IIRFilter BpfHX_iir1 = { //
.A = {0.131726f, 0.131726f, 0.12435f, 1.0f}, //
.a = { {-1.63410f, 0.82662f}, {-1.87089f, 0.91410f}, {-1.6652f, 0.7513f}}, //
.b = { {1, 0, -1}, {1, 0, -1}, {1, 0, -1}} //
};
All members of BpfHX_iir1 will be initialized. Those not explicitly coded above will have a zero bit-pattern.
To assign an array *1 via memcpy() at a later time is easy if the compiler is C99 compliant. Use a compound literal.
void foobar(void) {
IIRFilter BpfHX_iir1;
// v-------------------------------- compound literal ---------v
memcpy(BpfHX_iir1.A, (float[MAX_STAGES_IIR]){0.131726f, 0.131726f, 0.12435f, 1.0f}, sizeof BpfHX_iir1.A);
...
}
Or the old fashion way:
const float A0[MAX_STAGES_IIR] = {0.131726f, 0.131726f, 0.12435f, 1.0f};
memcpy(BpfHX_iir1.A, A0, sizeof BpfHX_iir1.A);
*1 Arrays cannot be simply assigned in C. They can be copied via memcpy(), just like any object.

How to initialize a constant array of struct in another dynamically allocated struct

Here is my problem, I try to initialize constant values ​​from a struct, for simple values ​​I do like this: *(int*)&myStruct->myValue = 1; and it works very well but for the array I would have liked to use a similar method more than a 'memcpy' so I did like this:
*(MyOtherStruct*)&myStruct->myArrayOfStructs = {
otherStruct1, otherStruct2,
otherStruct3, otherStruct4
};
But I get this: error: expected expression before ‘{’ token
And I tried a lot of things but either it was totally buggy, or I had other error messages, I can't find the expression that the compiler wants and that works correctly...
Afterwards maybe the use of 'memcpy' is "obligated" but I will find it better without for my code if possible.
Thanks in advance !
EDIT: Here is an abstract but "working" example of what I want to do.
#include <stdlib.h>
typedef struct {
int r, g, b, a;
} Color;
typedef struct {
const int value;
const Color color[4];
} structExample;
int main(void)
{
Color colorWhite = { 0, 0, 0, 255 };
Color colorBlack = { 255, 255, 255, 255 };
Color colorRed = { 255, 0, 0, 255 };
Color colorBlue = { 0, 0, 255, 255 };
structExample* myStruct = malloc(sizeof(structExample));
*(int*)&myStruct->value = 1;
*(Color*)&myStruct->color = {
colorWhite, colorBlack,
colorRed, colorBlue
};
return 0;
}
You cannot assign an array, nor multiple elements of an array with one assignment. You can assign the elements of the array, for which you ought to do the casting correctly:
myStruct->color is an array of 4 Color. Taking its address yields a Color (*)4. Casting that to Color * leads to technical violations of rules required for the behavior to be define by the C standard.
Your goal is to remove const from the element. So simply take the address of an element instead of taking the address of the array:
* (Color *) &myStruct->color[0] = colorWhite;
* (Color *) &myStruct->color[1] = colorBlack;
* (Color *) &myStruct->color[2] = colorRed;
* (Color *) &myStruct->color[3] = colorBlue;
However, this casting away of const makes it irksome to avoid violating rules of the C standard regarding defined behavior. A better approach is:
Remove the const from the member declarations in structExample.
Allocate memory and initialize the structure with no const involved.
Assign the pointer to the memory to a new pointer that does have const.
When you do this, you can also use a compound literal to initialize the structure (even though it contains an array). Here is an example:
#include <stdlib.h>
typedef struct
{
int r, g, b, a;
} Color;
typedef struct
{
int value;
Color color[4];
} structExample;
int main(void)
{
Color colorWhite = { 0, 0, 0, 255 };
Color colorBlack = { 255, 255, 255, 255 };
Color colorRed = { 255, 0, 0, 255 };
Color colorBlue = { 0, 0, 255, 255 };
// Start with pointer to non-const object.
structExample *myStruct = malloc(sizeof *myStruct);
// Assign initial value.
*myStruct =
(structExample) { 1, { colorWhite, colorBlack, colorRed, colorBlue } };
// Convert to pointer to const object.
const structExample *myStructConst = myStruct;
// Use pointer-to-const for further work.
extern void foo(const structExample *);
foo(myStructConst);
/* Inside foo, only the pointer-to-const is seen, so it will get a
diagnostic message if it attempts to modify the structure without
using an explicit cast. foo does not see the original myStruct
pointer.
*/
// Use the original pointer to free.
free(myStruct);
}
You cant assign the arrays. You need to do this manually
myStruct->myArrayOfStructs[0] = otherStruct1;
myStruct->myArrayOfStructs[1] = otherStruct2;
myStruct->myArrayOfStructs[2] = otherStruct3;
myStruct->myArrayOfStructs[3] = otherStruct4;

Can I use scalar initialization in a struct whose member is a pointer to pointer?

I have a struct with a single member. This member is actually a pointer to pointer to integer, in order to represent a 2-dimensional integer array.
Can I use scalar initialization while creating an instance of that struct?
I am trying to create a 2-dimensional array to represent a collection of pixels for an algorithm exercise.
I should represent something like this:
{
{ 0, 0, 1, 0, 0 },
{ 0, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1 }
}
In order to represent a generic abstraction of an image, I tried to create a struct with the following structure:
struct image_t
{
unsigned short int** pixels;
};
And so I try to init an instance of that by using:
int
main()
{
struct image_t image = {
{
{0, 0, 1, 0, 0},
{0, 1, 1, 1, 0},
{1, 1, 1, 1, 1},
}
};
return 0;
}
When I try to compile, the following warnings are given:
gcc -I src src/main.c -o bin/flood-fill.bin
src/main.c: In function ‘main’:
src/main.c:41:5: warning: braces around scalar initializer
{
^
src/main.c:41:5: note: (near initialization for ‘image.pixels’)
src/main.c:42:7: warning: braces around scalar initializer
{0, 1, 0},
^
src/main.c:42:7: note: (near initialization for ‘image.pixels’)
src/main.c:42:11: warning: excess elements in scalar initializer
{0, 1, 0},
^
src/main.c:42:11: note: (near initialization for ‘image.pixels’)
src/main.c:42:14: warning: excess elements in scalar initializer
{0, 1, 0},
After making some research, I realized that, as it is gonna be an image representation, each row will have the same total of columns. Due that, I can just use a single array and store everything in a single block of memory. It resolves my problem.
However, as a curious guy, I would like to know if there is any way to use scalar initialization for such cases - if so, how can I do that?
Most likely I'm missing some critical basic concept from C language, so explanations are more than welcome. I really want to understand better the way it works under the hood.
It probably isn't a good idea, but you can use compound literals to create the structure you want, like this:
struct image_t
{
unsigned short int **pixels;
};
int main(void)
{
struct image_t image =
{
(unsigned short *[]) {
(unsigned short []){ 0, 0, 1, 0, 0 },
(unsigned short []){ 0, 1, 1, 1, 0 },
(unsigned short []){ 1, 1, 1, 1, 1 },
}
};
return image.pixels[1][4];
}
Note that one of the problems with the data structure shown is that there is no information about how many rows are in the array of pointers, nor how many items are in each row (and there's no guarantee that each row is the same length as the other rows). That alone is reason enough to think that the structure is broken (badly designed, at any rate). If you added information about the rows and columns, then it could be OK.
Note that unsigned short **pixels; is not a pointer to a 2D array. It is a pointer to (an array of) pointer(s), which is quite different.

Checking constant variable's value at the compilation time

For efficient code maintenance I need to make sure that the value at index 0 of an array is a specific predefined value. The following code doesn't work:
#define SPECIFIC_ADDR_IDX 0
#define SPECIFIC_ADDR 8
#define NOT_SPECIFIC_ADDR1 12
#define NOT_SPECIFIC_ADDR2 16
typedef struct _struct_s
{
const uint16_t addr; // addresses are constant and are not mutable
uint32_t val;
} struct_s;
struct_s globArr[] =
{
{.addr = SPECIFIC_ADDR, .val = 0},
{.addr = NOT_SPECIFIC_ADDR1, .val = 0},
{.addr = NOT_SPECIFIC_ADDR2, .val = 0},
};
// make sure the address at the SPECIFIC_ADDR_IDX is SPECIFIC_ADDR
_Static_assert(globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR, " Illegal!");
It gives the following compilation error:
error: expression in static assertion is not constant
_Static_assert (globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR, " Illegal!");
~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
The addr is defined as const uint16_t, so I was thinking that its value is known at the compilation time.
Is there an efficient way to perform such check at the compilation time?
Clarification: I understand that in this way I can't use the _Static_assert, const doesn't make a variable's value known at the compilation time. What I am asking is if anyone knows any kind of trick to deal with such issues.
The satisfying solution was proposed by Kamil Cuk. The initialization can be done with specifying the index:
struct_t globArr[] =
{
[SPECIFIC_ADDR_IDX] = { .addr = SPECIFIC_ADDR, .val = 0 },
{.addr = NOT_SPECIFIC_ADDR1, .val = 0},
{.addr = NOT_SPECIFIC_ADDR2, .val = 0},
};
In such case if there would be additional initialization of entry at index [SPECIFIC_ADDR_IDX], the compiler will issue a warning (not guaranteed but most compilers will). Just make sure to compile with warning=error option ON.
You can just specify the intex in initialization:
#define SPECIFIC_ADDR_IDX 0
#define SPECIFIC_ADDR 8
#define NOT_SPECIFIC_ADDR1 12
#define NOT_SPECIFIC_ADDR2 16
typedef struct _struct_s
{
const uint16_t addr; // addresses are constant and are not mutable
uint32_t val;
} struct_t;
struct_t globArr[] =
{
[SPECIFIC_ADDR_IDX] = { .addr = SPECIFIC_ADDR, .val = 0 },
{.addr = NOT_SPECIFIC_ADDR1, .val = 0},
{.addr = NOT_SPECIFIC_ADDR2, .val = 0},
};
Anyway you need to do runtime assertion, so use assert:
void globArr_unittest(void) {
assert(globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR);
}
static_assert needs a constant expression. You can't write static assertions on variables values. Even if you did static const struct_t globArr[] still globArr value is not a constant expression. The C language has no constexpr (or consteval) specifier like C++. So, sadly, you can't do that in C.
const is just a modifier, it says that the variable can't be modified through this handle. const variables can be modified and are not immutable in C.
It's just like you can't do:
#if globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR
the same you can't
static_assert(globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR, "");
What is a constant expression is probably nicely enumerated at cppreference. The result of array subscript [] and member access . operators is not a constant expression, therefore it can't be used in static assertion.

Why can't I assign a struct variable using curly brackets {}?

I have the following code, why does Visual Studio underly the first bracket { in playerObj.cropImg = { 0, 0, 45, 32 }; and return an error error C2059: syntax error : '{' when I compile ?
#include <SDL.h>
#include <stdio.h>
typedef struct obj
{
SDL_Surface *pSprite;
SDL_Texture *pTexture;
SDL_Rect cropImg;
SDL_Rect pos;
} obj;
obj playerObj;
void playerObj_init(unsigned char * filename, SDL_Renderer * pRenderer)
{
playerObj.cropImg = { 0, 0, 45, 32 };
}
Your code attempts assignment.
Initialization is when you provide a value for the variable as part of a declaration.
Assignment is when you provide new values for a variable that already exists; this occurs in a statement (not a declaration).
In C90, brace-enclosed initializers can only be used in declarations. In C99 you can have literals of struct type but you have to specify the type as part of the syntax (it would be too complicated to have the compiler try to deduce it):
playerObj.cropImg = (SDL_Rect) { 0, 0, 45, 32 };
An alternative that works in C90 is to write:
SDL_Rect const newRect = { 0, 0, 45, 32 };
playerObj.cropImg = newRect;
and the compiler should optimize it.
NB. Consider using designated initializers to help with code maintainability. In either situation,
{ .w = 45, .h = 32 }

Resources