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;
}
Related
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;
I'm new to c, and currently making chess program.
There's the code.
#include <stdio.h>
int spider[8][2] = {{0, 1}, {1, 1}, {1, 0}, {1, -1},
{0, -1}, {-1, -1}, {1, 0}, {-1, 1}};
int jump[8][2] = {{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-2, -1}, {-1, -2}, {-1, 2}, {-2, 1}};
typedef struct
{
int color;
int type;
char symbol;
int unit[8][2];
} piece;
void add_piece(piece piece, int color, int type, char symbol, int unit[8][2])
{
piece.color = color;
piece.type = type;
piece.symbol = symbol;
for(int i = 0; i < 8; i++)
{
for(int j = 0; j < 2; j++)
{
piece.unit[i][j] = unit[i][j];
}
}
}
int main(void)
{
piece wk;
add_piece(wk, 0, 0, 'K', spider);
printf("%d", wk.color);
return 0;
}
expected output : 0
console output : 8388608
I found that 8GB = 8388608KB.
What's the meaning of that output?
Is there any problem of initializing property of structure or elsewhere?
Can you see some not good habits in this code?
Remember that in C all arguments are passed by value.
That means the value of the argument in the call is copied into the functions argument value.
So in the call
add_piece(wk, 0, 0, 'K', spider);
the value (current uninitialized contents) of the structure object wk will be copied into the argument variable piece inside the add_piece function.
When you modify the local argument variable, for example through an assignment like
piece.color = color;
you only modify the local copy. The original value used in the call will not be modified.
You can solve this in two ways:
Return the structure, and assign to a variable when doing the call; Or
Emulate pass by reference by using pointers.
Option number 2 is the most common it seems, so I will show it here.
First you need to modify the function to take a pointer to the structure object:
void add_piece(piece *piece, int color, int type, char symbol, int unit[8][2])
{
...
}
Then you need to use the "pointer to structure" syntax using the "arrow" to access structure members:
piece->color = color;
piece->type = type;
// And so on for all access to piece members...
Finally, when you call the function you need to pass a pointer to the wk variable, using the "pointer to" or "address of" operator &:
add_piece(&wk, 0, 0, 'K', spider);
I described the pass by reference emulation because it's so common and because you need to learn to recognize it. For your specific case I would rather recommend that you define a new structure object in the function and return it:
piece add_piece(int color, int type, char symbol, int unit[8][2])
{
piece piece;
piece.color = color;
piece.type = type;
// And so on, much like your current function...
// Return the structure
return piece;
}
Now you can use it as such:
piece wk = add_piece(0, 0, 'K', spider);
Note that sometimes it's not possible, or desirable, to return structures like this, and you still need to emulate pass by reference. So you need to learn both ways.
Lastly a note about the output you get: Uninitialized local (non-static) variables really are uninitialized. They will have indeterminate (read: garbage) values.
Since in your current code doesn't really initialize the original wk structure object, all its members will have such indeterminate values.
Depending on type and use, using such indeterminate values could lead to undefined behavior.
struct mystruct s[10] = {{0}};
This appears to initialize an array of structs to 0. How does the nested braces syntax work?
Any fields not specified are initialized to zero. So here you have an array of structs. You're initializing the first element of the array with a structure initializer that initializes the first element of the structure to zero. The rest of the first structure and the rest of the array elements will all be zero too. It's a nice idiom.
As shown?
Basically, you should enclose each compound type - array, structure, etc - inside its own level of braces.
Consider:
struct mystruct
{
int i;
double d;
char s[10];
int a[5];
} s[10] =
{
{ 0, 0.0, "abc", { 1, 2, 3, 4, 5 } },
{ 1, 1.0, "def", { 2, 3 } }, // Partially initialized array
{ 2, 2.0, { 'x', 'y', 'z' }, { 0 } }, // Strings are a shorthand
[9] = { 9, 99, 0, { 9, 8, 7, 6, 5 } }, // C99 - initialize row 9
};
But you can also omit braces if you insist (bad, archaic practice):
struct mystruct t[3] =
{ // You cannot avoid using these outside braces
0, 0.00, "abc", 1, 2, 3, 4, 5, // Omitted braces
1, 1.11, "bcd", 2, 3, 4, 5, 4,
2, 2.34, // Omitted values
};
Any omitted initializers are treated as zeroes.
It is not the nested braces that initializes. The outer braces are indicating that an array is being initialized:
struct mystruct s[10] = { };
Since this is an array of structures, each structures can be initialized with further braces:
struct mystruct { int x, y, z};
struct mystruct s[10] = { {0, 1, 2}, // <-- initializes s[0].x, s[0].y, s[0].z
{1, 2, 3}, // <-- initializes s[1].x, s[1].y, s[1].z
{2, 3, 4} // <-- initializes s[2].x, s[2].y, s[2].z
};
Notice that only first three elements are initialized. According to C standard the rest 7 elements must be initialized to 0. This is what happens to your code too. As xscott mentioned in his reply, everything omitted in initializer list is initialized to 0.
It's useful to note that while the inner braces are optional, the compiler will check to ensure that opening braces only appear where they ought, and that no nested item has too many initializers. Further, one may leave out some fields of a structure and have the compiler automatically zero them, even if the structure is not at the end of the array. Note that implementations vary in their efficiency of handling this; some will divide the initialization record into small pieces, while others will simply insert lots of zeroes in the code. For example, if each struct looked like:
typedef struct {
char name[8];
char more_stuff[1016];
} THINGIE;
THINGIE my_array[] = {{"Fred"},{"George"},{"Mark"}};
some compilers would generate 3K worth of const data, while others would generate three relatively-small 'const-init' records.
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.
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 *