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

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 }

Related

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;

define array element by reference in C

I'm re-writing a function (it works, so I'm apparently eager to break it) that iterates through an 2-D array. The function will read an INI file for the contents of a given key, and either store the value returned to a variable or store a default value to the variable (in my structure, the variable is defined by reference as *pStoreToRef).
Support for storing a default value in my 2-D array is proving difficult. All data types must be accommodated. How to do so?
The 2-D array is typed by a structure:
typedef struct s_defaults {
char *cKeyTitle;
uint16_t nItemNr;
uint8_t nVarType;
int *pDefaultValue;
uint16_t nVarLength;
uint8_t nVarFloatDecimals;
char *pFormatString;
int *pStoreToRef;
int nNextItem; // set to -1 at end
} site_defaults_t;
Here's the initializer:
site_defaults_t site_defaults[] =
{
{"AUTOTUN_TOTAL_CYCLES\0", SD_AUTORUN_TOTAL_CYCLES, VT_INTEGER, 192, 5, 0, "%i\0", (int*)&aAutoRunControl.nTotalCycles,1},
{"AUTORUN_TEST_INTERVAL\0", SD_AUTORUN_TEST_INTERVAL, VT_INTEGER, 60, 5, 0, "%i\0", (int*)&aAutoRunControl.nTestIntervalSecs,2},
{"AUTORUN_TEST_LEN_SECS\0", SD_AUTORUN_TEST_LEN_SECS, VT_INTEGER, 30, 6, 0, "%i\0", (int*)&aAutoRunControl.nTestLengthSecs,3},
{"AUTORUN_TEST_FAN_SPEED\0", SD_AUTORUN_TEST_FAN_SPEED, VT_INTEGER, 85, 3, 0, "%i\0", (int*)&aAutoRunControl.nSpeed,4},
{"AUTORUN_TEST_PLATE_VOLTS\0", SD_AUTORUN_TEST_PLATE_VOLTS, VT_FLOAT, 40, 3, 1, "%2.1f\0", (int*)&aAutoRunControl.vPlate,5},
{"AUTORUN_TEST_SAMPLES_PER_SEC\0", SD_AUTORUN_TEST_SAMPLES_PER_SEC, VT_INTEGER, 10, 2, 0, "%i\0", (int*)&aAutoRunControl.nSamplesPerSecond,6},
{"SEND_FILES_FREQUENCY\0", SD_SEND_FILES_FREQUENCY, VT_INTEGER, 10, 6, 0, "%i\0", (int*)&oSystemInfo.nSendFilesFrequency,7},
{"POWER_60_HZ\0", SD_POWER_60_HZ, VT_BOOLEAN, true, 1, 0, "%i\0", (int*)&aSystemState.Power60Hz,8},
{"UPLOAD_ON_BOOT\0", SD_UPLOAD_ON_BOOT, VT_BOOLEAN, false, 1, 0, "%i\0", (int*)&oSystemInfo.EnableSendFilesOnBoot,9},
{"AUTORUN_SENDFILES\0", SD_AUTORUN_SENDFILES, VT_BOOLEAN, true, 1, 0, "%i\0", (int*)&aAutoRunControl.sendFiles,10},
{"PREAMP_TRIM\0", SD_PREAMP_TRIM, VT_FLOAT, 0, 3, 1, "%2.1f\0", (int*)&aSystemState.nPreampTrim,11},
{"AUTORUN_PCIL_CONVERSION_CONST\0", SD_AUTORUN_PCIL_CONVERSION_CONST, VT_FLOAT, 0.07608, 2, 8, "%2.8f\0", (int*)&aAutoRunControl.pcilMult,12},
{"ENABLE_DVDT_BEEP\0", SD_ENABLE_DVDT_BEEP, VT_BOOLEAN, false, 1, 0, "%i\0", (int*)&oSystemInfo.AllowDvDtBeep,13},
{"AUTORUN_PCIL_CONVERSION_ADJUST\0",SD_AUTORUN_PCIL_CONVERSION_ADJUST, VT_FLOAT, 0.02, 2, 8, "%2.8f\0", (int*)&aAutoRunControl.pcilMultAdjust,14},
{"AUTORUN_DEMO_DURATION\0", SD_AUTORUN_DEMO_DURATION, VT_INTEGER, 30, 5, 0, "%i\0", (int*)&aAutoRunControl.nDemoDuration,-1},
};
The new field, int *pDefaultValue; is proving problematic. My theory was to store a reference to the value, and in this manner the stored pDefaultValue can be any type. That should work if the default value to store could be referenced to an address, but in this case?
Any ideas as to a work-around?
I believe your best choice is to have two members in this structure instead of just pDefaultValue. The first one is the string form of the default value, say, pDefaultValueStr. The other is pDefaultValue, which should be declared void * and to which the default value is stored in its internal form: the one that would be cast when the getter function needs to get one.
You would then execute an initializer population function early in the program execution that populates pDefaultValue given the content of pDefaultValueStr and switch upon nVarType.
The initializer, site_defaults, would have a null pointer given to each element's pDefaultValue and would be replaced by a usable, freeable, value by the initializer population function or it would "die" if the value cannot be converted from a string to its internal representation.
This initializer population function has utility also if the site defaults have to change given some input (string of course) from a user.
The simplest and not so great approach is to union it:
typedef struct s_defaults {
uint8_t nVarType;
union some_union_name_in_case_you_dont_have_c11 {
float f;
int i;
bool b;
} pDefaultValue;
};
struct s_defaults site_defaults[] = {
{ VT_INTEGER, { .i = 192 }, },
{ VT_FLOAT, { .f = 1.92 }, },
{ VT_BOOL, { .b = true }, },
};
This results in many pages long switch-like statements to handle each type.
A way better way would be to use a function pointers and proper interface overloading.
struct s_default_vtable {
void (*assign)(void *to, const void *value, void *cookie);
void (*print)(const void *value, const char *fmt, void *cookie);
};
void assign_float(void *to, const void *value, void *cookie) {
*(float*)to = *(const float*)value;
}
void print_float(const void *value, const char *fmt, void *cookie) {
printf(fmt, *(const float*)value);
}
const struct s_default_vtable s_default_vtable_float = {
.assign = assign_float,
.print = print_float,
};
// similar for int and bool types
typedef struct s_defaults {
const struct s_default_vtable *v; // vtable pointer
void *cookie; // user context to pass to vtable functions
const void *pDefaultValue;
const char *pFormatString; // as this is tied to the type,
// I think it should be moved into vtable
// or just hardcoded into print function
void *pStoreToRef;
} site_defaults_t;
Note that you can still use pages long switch statements by comparing the vtable pointer. Then you can use for example like:
// example for float type
float number_to_store_result_to;
const float float_default_value = 50.0;
struct s_defaults defaults = {
.v = &s_default_vtable_float ,
.cookie = 0,
.default_value = &float_default_value.
.pFormatString = "%f",
.pStoreToRef = &number,
};
// assign default value to pStoreToReg
// no need for switch, no need for anything
defaults.v->assign(defaults.pStoreToReg, defaults.default_value, defaults.cookie);
Notes:
String literals include zero terminating character. There is no need for \0" (unless you really mean to have two zero characters on the end of the string).
The members:
uint16_t nVarLength;
uint8_t nVarFloatDecimals;
char *pFormatString;
rigth now duplicate information in your structure. You need only the format specifier for the type, for example for float you need f. The %<this>.<this> can be build dynamically with a small static buffer.
void print_float(int nVarLength, int nVarFloatDecimals, const char *pFormatString, float *value) {
char buf[100];
snprintf(buf, sizeof(buf), "%%%d.%d%s", nVarLength, nVarFloatDecimals, pFormatString);
printf(buf, *(float*)value);
}
// call like:
print_float(2, 8, "f", 1.0);
Here's one of many possible solutions. Since you're using VARIANTs to describe information, let's use VARIANT.
I've trimmed away other struct members for clarity since this is all about initializing the VARIANT and in this case irrelevant.
First an augmented struct for this example:
typedef struct SiteMeta {
char* name;
VARIANT v;
};
Define a function to initialize a VARIANT.
VARIANT VariantValInit(const VARTYPE vt, const void* value) {
VARIANT v;
VariantInit(&v);
v.vt = vt;
switch (vt) {
case VT_I4:
v.intVal = (int)value;
break;
case VT_BOOL:
v.boolVal = (bool)value;
break;
default:
// throw or something.
break;
}
return v;
}
Of course the function above would require additional cases to support other types.
Now declare your defaults like so:
SiteMeta site_defaults[] =
{
{ "AUTOTUN_TOTAL_CYCLES", VariantValInit(VT_I4, (void*)192) },
{"POWER_60_HZ", VariantValInit(VT_BOOL, (void*)true) }
};
String literals such as "AUTOTUN_TOTAL_CYCLES" implicitly include a null terminator. So "AUTOTUN_TOTAL_CYCLES\0" is actually placing two NULL terminators, one implicit, the other explicit. It's not necessary.
Additionally, because you are initializing the struct with an initializer, the type of the first member could be modified to: const char *cKeyTitle;
Next, The code as is produces an error message to the effect of "unexpected type name "VT_INTEGER" (and similar for other type names) for the struct initializer site_defaults_t site_defaults[]
This is because a C type cannot be used as a value. This would be similar to doing this:
struct {
int int;
float float;
...
Following is a reduced scale replica of your original code for the purpose of showing how you might be able to solve your problem replacing void * for creating a flexible type to using a union:
This is a bare bones example, but includes a main function just so you can build it and experiment to see how the union works...
typedef enum {
SD_AUTORUN_TOTAL_CYCLES,
SD_AUTORUN_TEST_INTERVAL,
SD_AUTORUN_TEST_LEN_SECS,
SD_AUTORUN_TEST_FAN_SPEED,
SD_AUTORUN_TEST_PLATE_VOLTS,
SD_AUTORUN_TEST_SAMPLES_PER_SEC,
SD_MAX //count of items in enum list
}TASK;
typedef enum {
V_INTEGER,//replace VT_INTEGER in initializer
V_FLOAT,
V_BOOLEAN,
}TYPES;
typedef union {
int iVar;
float fVar;
bool bVar
}U_TYPE;
typedef struct s_defaults {
char *cKeyTitle;
uint16_t nItemNr;
uint8_t nVarType;
U_TYPE uVar;//union of types
} site_defaults_t;
site_defaults_t site_defaults[] =
{
{"AUTOTUN_TOTAL_CYCLES", SD_AUTORUN_TOTAL_CYCLES, V_INTEGER, 2},
{"AUTORUN_TEST_INTERVAL", SD_AUTORUN_TEST_INTERVAL, V_INTEGER, 8},
{"AUTORUN_TEST_LEN_SECS", SD_AUTORUN_TEST_LEN_SECS, V_INTEGER, 20},
{"AUTORUN_TEST_FAN_SPEED", SD_AUTORUN_TEST_FAN_SPEED, V_INTEGER, 0},
{"AUTORUN_TEST_PLATE_VOLTS", SD_AUTORUN_TEST_PLATE_VOLTS, V_FLOAT, 123.56},
{"AUTORUN_TEST_SAMPLES_PER_SEC", SD_AUTORUN_TEST_SAMPLES_PER_SEC, V_INTEGER, 15}
};
int main(void)
{
return 0;
}

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.

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 *

Resources