Checking constant variable's value at the compilation time - c

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.

Related

C - declare and init array inside struct

language: C
i am trying to declare and initialize an array inside a struct and pass it to a pointer, which is itself declared in the struct xD
yes, i know my attempt is... let's say "not correct" :D
but it would be very useful if something similar would exist.
any ideas?
struct structname {
int* section_A;
int* section_B;
};
static const struct structname structvariable_a = {
.section_A = (int[]) {
[0x01] = 1,
[0x02] = 2,
[0x03] = 3
},
.section_B = (int[]) {
[0x33] = 4,
[0x34] = 5
},
};
static const struct structname structvariable_b = {
.section_A = (int[]) {
[0x01] = 10,
[0x02] = 20,
[0x03] = 30
},
.section_B = (int[]) {
[0x33] = 7,
[0x34] = 8
},
};
later, i want to access the values ...
int main()
{
struct structname* structvariablepointer;
if(foo == 1){
structvariablepointer = &structvariable_a;
} else {
structvariablepointer = &structvariable_b;
}
printf("%i", ARRAY_SIZE(structvariablepointer->section_A)); // ARRAY_SIZE(arr) equals sizeof(arr) / sizeof(arr[0]));
int b = 2;
printf("%i", structvariablepointer->section_A[b]);
}
the only error is
./include/linux/build_bug.h:29:45: Fehler: Negative Breite in Bitfeld »<anonym>«
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:(-!!(e)); }))
^
./include/linux/compiler-gcc.h:64:28: Anmerkung: bei Substitution des Makros »BUILD_BUG_ON_ZERO«
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
^~~~~~~~~~~~~~~~~
./include/linux/kernel.h:60:59: Anmerkung: bei Substitution des Makros »__must_be_array«
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
^~~~~~~~~~~~~~~
Once you take a pointer to the first element of an array, you can no longer find the size of the array through that pointer. So you will also need to make variables to hold the array size. (Or use a sentinel value for the end of the array).
One way to solve your problem would be through ugly macros:
#include <stddef.h>
#define ARRAY_SIZE(a) ( (sizeof(a)) / sizeof((a)[0]) )
struct structname {
int* section_A;
size_t A_len;
int* section_B;
size_t B_len;
};
#define A_INIT (int[]) { 0, 1, 2, 3 }
#define B_INIT (int[]) { [0x33] = 1, [0x34] = 2 }
static const struct structname structvariable_a =
{
.section_A = A_INIT,
.A_len = ARRAY_SIZE(A_INIT),
.section_B = B_INIT,
.B_len = ARRAY_SIZE(B_INIT)
};
#undef A_INIT
#undef B_INIT
It would also be possible to define static named int arrays and then use that array's name in the initializers for structvariable_a.
Consider using int const * and int const[] respectively, if you don't intend to change the contents of the arrays at runtime. Note that if this code is in a header, each translation unit will have their own copy of the arrays.
Update (as suggested by comment): using a sentinel value would look like:
struct structname {
int* section_A;
int* section_B;
};
static const struct structname structvariable_a =
{
.section_A = (int[]) { 0, 1, 2, 3, INT_MAX },
.section_B = (int[]) { [0x33] = 1, [0x34] = 2, INT_MAX }
};
and the in main or whatever, you look for INT_MAX to know where the end of the array is, e.g.:
size_t a_len;
for (a_len = 0; structvariable_a.section_A[a_len] != INT_MAX; ++a_len) { }
Obviously this means the range of valid data for the array needs to exclude the sentinel value.
The error you're getting is because ARRAY_SIZE expects an array, and structvariablepointer->section_A is not an array but a pointer.
Since your structs effectively have fixed size arrays, just declare them as arrays instead of pointers:
struct structname {
int section_A[4];
int section_B[0x35];
};
Then initialize like this:
static const struct structname structvariable_a = {
.section_A = {
[0x01] = 1,
[0x02] = 2,
[0x03] = 3
},
.section_B = {
[0x33] = 4,
[0x34] = 5
},
};
i am trying to declare and initialize an array inside a struct and pass it to a pointer, which is itself declared in the struct xD
That doesn't make any sense whatever, but your actual code is almost completely correct in C99 and later. In particular, this:
(int[]) {
[0x01] = 1,
[0x02] = 2,
[0x03] = 3
}
Is neither a declaration of nor (quite) an initialization of an array; rather, it is a compound literal of type array of int. Because arrays decay to pointers in the context given, as in most, such a compound literal can indeed be used to initialize a structure member of type int * (to point to the first element of the array). Having successfully initialized those two struct structname objects, you can certainly obtain their addresses and record those in variables.
HOWEVER, a pointer to an array element does not carry information about the number of elements in the array. If that's all you have, as is the case in your main() function, then you cannot determine the number of array elements from it. You need to be able to determine that from the content (this is why C strings must be null-terminated), or you must have that information from some other source, such as a function argument, a variable, or clairvoyance.
yes, i know my attempt is... let's say "not correct" :D but it would be very useful if something similar would exist.
If you declare the members of struct structname to be bona fide arrays then you can access and use their declared lengths. If you prefer, you can store the number of array elements in additional members of that struct. Both of those approaches are used in the wild, as are approaches based on the contents of the pointed to elements. But I don't foresee C ever gaining a facility for making your ARRAY_SIZE() macro work with pointers as it seems you would like.

How to assign a predefined value to a struct member on type definition?

This is a long shot, but maybe there will be some ideas. On a system I programming, I have defined structures to program processor registers. The registers are comprised of several fields of a few bits each, with potentially "reserved" bits in between. When writing to a register, the reserved bits must be written as zeros.
For example:
typedef struct {
uint32_t power : 3;
uint32_t reserved : 24;
uint32_t speed : 5;
} ctrl_t;
void set_ctrl()
{
ctrl_t r = {
.power = 1;
.speed = 22;
.reserved = 0;
}
uint32_t *addr = 0x12345678;
*addr = *((uint32_t *) &r);
return;
}
I want to be able to set the reserved field to a default value (0 in this example), and to spare the need for an explicit assignment (which happens a lot in our system).
Note that if the instantiated object is static, then by default an uninitialized field will be 0. However, in the above example there is no guarantee, and also I need to set any arbitrary value.
Structure type definitions in C cannot express values for structure members. There is no mechanism for it. Structure instance definitions can do.
I want to be able to set the reserved field to a default value (0 in
this example), and to spare the need for an explicit assignment (which
happens a lot in our system).
Note that if the instantiated object is static, then by default an
uninitialized field will be 0. However, in the above example there is
no guarantee, and also I need to set any arbitrary value.
That the default value you want is 0 is fortuitous. You seem to have a misunderstanding, though: you cannot partially initialize a C object. If you provide an initializer in your declaration of a structure object, then any members not explicitly initialized get the same value that they would do if the object had static storage duration and no initializer.
Thus, you can do this:
void set_ctrl() {
ctrl_t r = {
.power = 1,
.speed = 22,
// not needed:
// .reserved = 0
};
// ...
If you want an easy way to initialize the whole structure with a set of default values, some non-zero, then you could consider writing a macro for the initializer:
#define CTRL_INITIALIZER { .power = 1, .speed = 22 }
// ...
void set_other_ctrl() {
ctrl_t r = CTRL_INITIALIZER;
// ...
Similarly, you can define a macro for partial content of an initializer:
#define CTRL_DEFAULTS .power = 1 /* no .speed = 22 */
// ...
void set_other_ctrl() {
ctrl_t r = { CTRL_DEFAULTS, .speed = 22 };
// ...
In this case you can even override the defaults:
ctrl_t r = { CTRL_DEFAULTS, .power = 2, .speed = 22 };
... but it is important to remember to use only designated member initializers, as above, not undesignated values.
It can't be done.
Values don't have "constructors" in the C++ sense in C. There's no way to guarantee that arbitrary code is run whenever a value of a certain type is created, so this can't be done. In fact "creation" of a value is quite a lose concept in C.
Consider this:
char buf[sizeof (ctrl_t)];
ctrl_t * const my_ctrl = (ctrl_t *) buf;
In this code, the pointer assignment would have to also include code to set bits of buf to various defaults, in order for it to work like you want.
In C, "what you see is what you get" often holds and the generated code is typically quite predictable, or better due to optimizations. But that kind of "magic" side-effect is really not how C tends to work.
It is probably better to not expose the "raw" register, but instead abstract out the existance of reserved bits:
void set_ctrl(uint8_t power, uint8_t speed)
{
const uint32_t reg = ((uint32_t) power << 29) | speed;
*(uint32_t *) 0x12345678 = reg;
}
This explicitly computes reg in a way that sets the unused bits to 0. You might of course add asserts to make sure the 3- and 5-bit range limits are not exceeded.

C initialize const struct member with existing const variable

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.

expected expression before '{' token

I am getting: "error: expected expression before '{' token" for the line I've commented before. If the struct is already defined why would it need a "{" before token. Thanks for any help you can provide.
struct sdram_timing {
u32 wrdtr;
u32 clktr;
};
int calibration(void);
unsigned char read_i2c_cal(void);
static unsigned int eepcal[15];
main() {
DQS_autocalibration();
}
int calibration(void)
{
struct sdram_timing scan_list[30];
read_i2c_cal();
if(eepcal[0] == 0){
scan_list = {{eepcal[1], eepcal[2]}, {-1, -1}}; // <-- PROBLEM LINE
}
else {
//foo
}
return 0;
}
unsigned char read_i2c_cal(void) {
eepcal[0] = 0;
eepcal[1] = 02;
eepcal[2] = 03;
}
The error is because you can't assign an array that way, that only works to initialize it.
int arr[4] = {0}; // this works
int arr2[4];
arr2 = {0};// this doesn't and will cause an error
arr2[0] = 0; // that's OK
memset(arr2, 0, 4*sizeof(int)); // that is too
So applying this to your specific example:
struct sdram_timing scan_list[30];
scan_list[0].wrdtr = 0;
scan_list[0].clktr = 0;
or you could use memset the same way, but instead of sizeof(int) you need size of your structure. That doesn't always work... but given your structure, it will.
Arrays in C language are not assignable. You can't assign anything to the entire array, regardless of what syntax you use. In other words, this
scan_list = { { eepcal[1], eepcal[2] }, {-1, -1} };
is not possible.
In C89/90 you'd have to spell out your assignments line by line
scan_list[0].wrdtr = eepcal[1];
scan_list[0].clktr = eepcal[2];
scan_list[1].wrdtr = -1;
scan_list[1].clktr = -1;
In modern C (post-C99) you can use compound literals to assign entire structs
scan_list[0] = (struct sdram_timing) { eepcal[1], eepcal[2] };
scan_list[1] = (struct sdram_timing) { -1, -1 };
Finally, in modern C you can use memcpy and compound literals to copy data to the array
memcpy(scan_list, (struct sdram_timing[]) { { eepcal[1], eepcal[2] }, {-1, -1} },
2 * sizeof *scan_list);
The last variant, albeit not very elegant, is the closest way to "emulate" array assignment.
You can only use an initializer list in the declaration of the variable, not after the fact.
Initializer list can only be used to initialize an array. You cannot use it afterwards.
However if you use GCC, you can use Compound Literal extension:
scan_list = (struct sdram_timing[30]){{eepcal[1], eepcal[2]}, {-1, -1}};
You might need to change scan_list type to be struct sdram_timing *

Detecting mismatched array <-> enum initializers

When doing embedded programming with C, many times I find myself doing maps with enum and array because they are fast and memory efficient.
enum {
ID_DOG = 0,
ID_SPIDER,
ID_WORM,
ID_COUNT
};
int const NumberOfEyes[ID_COUNT] = {
2,
8,
0
};
Problem is that sometimes when adding/removing items, I make mistake and enum and array go out of sync. If initializer list is too long, compiler will detect it, but not other way around.
So is there reliable and portable compile time check that initializer list matches the length of the array?
This is possibly a situation where X macros could be applied.
animals.x
X(DOG, 2)
X(SPIDER, 8)
X(WORM, 0)
foo.c
enum {
#define X(a,b) ID_##a,
#include "animals.x"
#undef X
};
int const numberOfEyes[] = {
#define X(a,b) b,
#include "animals.x"
#undef X
};
This not only guarantees that the lengths match, but also that the orders are always kept in sync.
What about a compile time assertion like the following? (Yes, there are more elaborate CT_ASSERT macros; this is to illustrate the idea.)
#define CT_ASSERT(expr, name) typedef char name[(expr)?1:-1]
enum {
ID_DOG = 0,
ID_SPIDER,
ID_WORM,
ID_COUNT
};
int const NumberOfEyes[] = {
2,
8,
0
};
CT_ASSERT (sizeof NumberOfEyes/sizeof *NumberOfEyes == ID_COUNT, foo);
Now when the NumberOfEyes array has more or less elements than ID_COUNT, this will cause an error along x.c:15: error: size of array 'foo' is negative. Negative array dimensions are a constraint violation that must be diagnosed by any C compiler out there.

Resources