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.
Related
I have these C structures and function prototypes within my libray.dll. I´m interested in pvData field, which will contain an array of data i need to extract by calling the OnLineGetData function:
typedef struct tagSAFEARRAY {
void * pvData;
} SAFEARRAY;
__declspec(dllexport) long OnLineGetData(SAFEARRAY **pData);
Then, i load the library.dll and create the same structure and function prototype using ctypes:
import ctypes
myLibrary = ctypes.CDLL('library.dll')
class tagSAFEARRAY(ctypes.Structure):
_fields_ = [("pvData", ctypes.c_int16 * 20 )]
OnLineGetData = myLibrary.OnLineGetData
OnLineGetData.argtypes = [ctypes.POINTER(ctypes.POINTER(tagSAFEARRAY)]
OnLineGetData.restype = ctypes.c_long
Then, i create a double pointer to the tagSAFEARRAY class, as it is an argument for the OnLineGetData funtion:
dataStructure = ctypes.POINTER(tagSAFEARRAY)(tagSAFEARRAY())
You'll notice that the pvData field is a 20 short array, it has a fixed size. That's because whenever i try to modify the pvData size i got the same problems as in here and here, but none of its solutions worked for me. I need to resize this array because it will contain the exact amount of values i need to extract from the OnLineGetData function. If i need to extract 10 values that array size needs to be 10, if a need to extract 27 values i need to resize pvData accordingly, and so on.
So, my question is, how could i resize the pvData array contained in the tagSAFEARRAY structure?
I'm not sure how you know how long the pvData field will be, but the answer is still the same...leave the parameter as a c_void_p and cast it to the correct size after return.
For this example I assumed the return value of OnLineGetData returns the number of elements, and cast pvData to a pointer of that array size and dereference it. Below is the C code for demonstration:
#include <stdlib.h>
#include <stdint.h>
typedef struct tagSAFEARRAY {
void * pvData;
} SAFEARRAY;
__declspec(dllexport) long OnLineGetData(SAFEARRAY **pData) {
*pData = malloc(sizeof(SAFEARRAY));
short* p = malloc(10 * sizeof(int16_t));
for(int16_t i = 0; i < 10; ++i)
p[i] = i;
(*pData)->pvData = p;
return 10;
}
test.py:
import ctypes as ct
myLibrary = ct.CDLL('./test')
class SAFEARRAY(ct.Structure):
_fields_ = ("pvData", ct.c_void_p),
OnLineGetData = myLibrary.OnLineGetData
OnLineGetData.argtypes = ct.POINTER(ct.POINTER(SAFEARRAY)),
OnLineGetData.restype = ct.c_long
p = ct.POINTER(SAFEARRAY)()
n = OnLineGetData(ct.byref(p))
# p is a void**
# p.contents is a void*
# cast to a pointer to n-element array:
# dereference to get the n-element array
arr = ct.cast(p.contents.pvData,ct.POINTER(ct.c_int16 * n)).contents
print(arr)
print(list(arr))
Output:
<__main__.c_short_Array_10 object at 0x00000228ED42EF40>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
I cannot find the solution to this. I can initialize an array of struct like this:
typedef struct S_A {
int x;
} T_A;
T_A ta1[3];
ta1[0] = (T_A){0};
ta1[1] = (T_A){1};
ta1[2] = (T_A){2};
T_A ta2[3] = { {0}, {1}, {2} };
But how can I do a one-line initialization after declaration?
T_A ta3[3];
ta3 = (?){ {?}, {?}, {?} };
ta3 = (T_A[3]){ { 0 }, { 1 }, { 2 } }; // error
ta3 = (T_A*) { { 0 }, { 1 }, { 2 } }; // error
Arrays are special in C. You can only once initialize arrays. You can't then "re-initialize" arrays. Assignments on arrays don't work. Array is in most contexts a pointer that you can't assign to. You can't:
int arr[3];
// arr = {1,2,3}; // will not work
// arr = anything; // will not work
You only can memcpy to them with a compound literal:
memcpy(ta3, (T_A[3]){ { 0 }, { 1 }, { 2 } }, sizeof(ta3));
Or without compund literal initialize a temporary variable and memcpy:
const T_A temp[3] = { { 0 }, { 1 }, { 2 } };
memcpy(ta3, temp, sizeof(ta3));
In C, an initialization is something that you do simultaneously as the declaration. You cannot do it afterwards.
This can be seen in the grammar that you can find here: https://www.lysator.liu.se/c/ANSI-C-grammar-y.html
Whenever you use the = after you have finished the declaration, it's an assignment and not an initialization, and assignments have different rules. One of these rules is that the common way of initializing arrays - = {1,2,3} - simply is not allowed. You have to use memcpy or something like that.
When it comes to nonarrays, like int, double etc and their pointers, it is still true that you cannot formally initialize them after declaration, but for those, assignment has the same syntax so it can be confusing.
However, there is a trick that can be used for arrays. Wrap the array in a struct and do like this:
struct wrapper{
int arr[3];
} x;
x = (struct wrapper){{1,2,3}};
Unfortunately, as others already mentioned, one cannot assign anything to an array.
int arr[N];
...
arr = ... ; // --> This will always fail compilation
The only option is such case is either to assign new value to each array entry separately
arr[i] = ...;
Or use memcpy to copy values from other memory location.
The interesting thing is that if one defines a struct of array(s) rather than an array of struct(s):
typedef struct S_S {
int x[3];
} T_S;
then the assignment after the declaration is allowed:
typedef struct S_S {
int x[3];
} T_S;
int main(void)
{
T_S y;
y = (T_S){.x = {1, 2, 3}}; //OK!
return 0;
}
This perfectly compiles (assuming your compiler supports C99 standard)!
Does anyone know if there is a way to initialize a structure containing a variable length array without initializing the array first in a separate variable (and without using malloc)?
My structure looks like this:
struct my_struct {
int *values;
int size;
}
For now in my code I have that:
void my_function (int size) {
int values[size];
struct my_struct mystr = {
.values = values,
.size = size
};
...
}
(Array is initialized first, then the structure. This is working but it looks awkward to declare a separate variable for the array.)
This would probably work as well:
void my_function (int size) {
struct my_struct mystr = {
.values = calloc (size, sizeof (int)),
.size = size
};
...
}
(but I do not want to use mallocs)
But what I would like to write is something like:
void my_function (int size) {
struct my_struct mystr = {
.values = (int[size]){},
.size = size
};
...
}
Any idea?
First of all, note that you cannot use an array from your stack if you want to return your structure.
int values[size];
struct my_struct mystr = {
.values = values,
.size = size
};
return mystr;
This is not going to work since the lifetime of values ends when you return. The same applies if you try to store mystr in a value pointed by a parameter of your function.
Obviously you're not doing that, however I think it's worth to mention anyway.
Answer to your question: it depends on the situation.
Can you be sure that size is small? Or else your stack is going to overflow in int values[size]. Is it small and predictable? Stick with your first solution. If it can be large or dependent on user-input, definitely use malloc.
Are you in some way returning or retaining a persistent pointer to your structure or values? Use malloc (see my first remark).
Alternatively, you can also use the struct hack but then you would have to malloc the entire mystr anyway.
One more thing, you wrote:
(Array is initialized first, then the structure. This is working but
it looks awkward to declare a separate variable for the array.)
I'm not sure what you mean, but the int * is only sizeof(intptr_t), irregardless of the size of the array. So you're not allocating twice the memory for 1 array, if that's what you're thinking.
Initializer are unnamed objects initialized by the initializer list. Outside the body of a function, the object has static storage duration. So it is possible to use the address of such an object. With a little help from variadic macros you can try →
#include <stdio.h>
struct test {
int count;
int *values;
} test[] = {
#define init(...) { .count=sizeof( (int[]) {__VA_ARGS__} )/sizeof(int), .values=(int *)&(int []){__VA_ARGS__} }
init(0,1,2,3,4),
init(2,4,6,8),
init(1,3),
init(42)
};
#define test_size ((int) (sizeof test/sizeof *test))
int main(void)
{
for(int array=0; array<test_size; ++array) {
printf("array %d (%d) : [ ", array+1, test[array].count);
for(int i=0; i<test[array].count; ++i)
printf("%d ", test[array].values[i]);
puts("]");
}
return 0;
}
I'm trying to initialize a const struct with a designated initializer. However, one of the struct elements is a fixed-width array. I already have the contents I would like to initialize the array with in another fixed-width array of appropriate size.
Is there any way to do this with a designated initializer? A simple (failing example) of what I'm trying to accomplish is demonstrated below.
struct foo {
uint8_t array1[4];
uint8_t array2[4];
}
uint8_t array[4] = {
1, 2, 3, 4
};
struct foo const bar = {
.array1 = array, // incompatible pointer to integer conversion
.array2 = { *array } // only copies the first element
};
Short answer: you can't. C does not copy arrays (without the use of (standard library-)functions). The warnings come from the fact that you cannot assign an array as a whole, even when they are static or constant. When an array is used as an r-value in an assignment it decays to a pointer and thus cannot be assigned to another array (as a whole).
The easiest way to go would be to use memcpy, but obviously that must be inside a function.
If bar has global scope, or is declared static, then you won't be able use designated initializers to initialize from non-immediate values, regardless of whether or not the members in question are arrays.
However, if:
bar is declared on the stack of some function, and
Your fixed-size array really does only have 4 elements,
then you might be able to get away with something like this:
#include <stdio.h>
#include <stdint.h>
struct foo {
uint8_t array1[4];
uint8_t array2[4];
};
#define ARRAY_INIT(a) { a[0], a[1], a[2], a[3] }
int main (int argc, char **argv) {
uint8_t arr_init[4] = {
1, 2, 3, 4
};
struct foo const bar = {
.array1 = ARRAY_INIT(arr_init),
.array2 = ARRAY_INIT(arr_init),
};
printf("%d, %d\n", bar.array1[0], bar.array2[3]);
return (0);
}
The initializer array must appear before what is being initialized in the stack frame. Or it may come from a function parameter.
Of course if your array is much bigger than this, then using a macro like this will get very messy indeed.
While you may not be able to initialize the array by copying from another array, it may be helpful to use a preprocessor macro:
#define ARRAY_INIT {1, 2, 3, 4}
struct foo const bar = {
.array1 = ARRAY_INIT,
.array2 = ARRAY_INIT
};
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 *