I have an array of structures as a function parameter and the size of the array is dynamic. My coworker said that I'll have to use a double pointer since the values contained in the array of struct will be overwritten.
The parameter that will become a double pointer is the following :
xPIDConfig_t **pxPIDConfig
Here is what the structure looks like for the xPIDConfig_t :
typedef struct
{
ePIDType_t ePIDType;
/* Common fields for the different types of PID */
float fLowerSaturationLimit;
float fUpperSaturationLimit;
float fOldInput;
float fIError;
uint32_t ulDeltaTime;
eBool_t bSaturationEnable;
eBool_t bAntiWindupEnable;
eBool_t bNegativeErrorEmptyIError;
union
{
/* Parallel PID fields */
struct
{
float fProportionalGain;
float fIntegralGain;
float fDerivativeGain;
}xParallelPID;
/* Non-interactive PID fields */
struct
{
float fControllerGain;
uint32_t ulIntegralTime;
uint32_t ulDerivativeTime;
}xNonInteractivePID;
}xUniqueFields;
}xPIDConfig_t;
The size of the array of pxPIDConfig will vary.
But I am not sure how to malloc that double pointer or even how to use the function containing the double pointer.
I was just wondering if anyone had a good example of code of how to use a function with a double pointer array of variating size? and how to properly change the values contained in the array itself inside a function?
Right now this is how I change the values within the function :
pxPIDConfig->ePIDType = ePIDType;
pxPIDConfig->fOldInput = 0;
pxPIDConfig->fIError = 0;
pxPIDConfig->ulDeltaTime = ulDeltaTime;
pxPIDConfig->bSaturationEnable = bIsSaturationEnable;
pxPIDConfig->bAntiWindupEnable = bIsAntiWindupEnable;
pxPIDConfig->bNegativeErrorEmptyIError = bNegativeErrorEmptyIError;
when the pointer is double do I have to use double '->'? This is very confusing for me.
Thank you all for the help
/***************** EDIT ************************************
My function is working right now, but I got told I need to use memory allocation since the size of my arrays varies according to the number of loops I want to implement.
Here are the parameters of my function :
eError_t eControlCascadeInit( uint8_t ucNumberOfLoops, ePIDType_t *pePIDType, xPIDConfig_t **pxPIDConfig, float *pfLowerLimit, float *pfUpperLimit, uint32_t *pulDeltaTime, \
eBool_t *pbIsSaturationEnable, eBool_t *pbIsAntiWindupEnable, eBool_t *pbNegativeErrorEmptyIError, \
float *pfPGain, float *pfIGain, float *pfDGain, float *pfCGain, uint32_t *pulITime, uint32_t *pulDTime )
They're all arrays of size ucNumberOfLoops. All of them are read-only arrays, except for the pxPIDConfig one that is write-only. The function initializes all the xPIDConfig_t present in the array with the parameters passed to the function through array.
array[ 0 ] contains the parameters for the first PID controller being initialized.
array[ 1 ] contains the parameters for the second PID controller being initialized and so on...
It's like that for all the parameters in the function.
Hope it makes my question more clear?
Here you have an example of how to use double-pointer, to change the pointer in the function:
void allocate(xPIDConfig_t **array, size_t size)
{
*array = malloc(sizeof(**array) * size);
/* some examples how to access the struct members vi double pointer -*
(*array) -> ulDeltaTime = 100;
(**array).ulDeltaTime = 100;
(*(array + 5)) -> ulDeltaTime = 100;
array[5] -> ulDeltaTime = 100;
(*array[5]).ulDeltaTime = 100;
}
int main(void)
{
xPIDConfig_t *array;
allocate(&array, 100);
printf("%s\n", array ? "success" : "failure");
free(array);
}
You would only need a double pointer if the function reallocates the array to a different size. If the size isn't changing, you can just pass a pointer to (usually the first) element of the array, along with any size or index required by the function. For example:
extern void frobPidConfig(xPIDConfig_t *);
// 'frob' the xPIDConfig_t array elements from index a to b
void frobSomePidConfigs(xPIDConfig_t *pidconfigs, unsigned int a, unsigned int b)
{
unsigned int i;
for (i = a; i <= b; i++)
{
frobPidConfig(&pidConfigs[i]);
// Example of member access:
pidConfigs[i].ulDeltaTime = 42;
}
}
Example of calling code:
xPIDConfig_t *pidConfigs;
unsigned int n = 10; // or whatever...
pidConfigs = calloc(sizeof *pidConfigs, n);
if (!pidConfigs)
{
// Allocation error
exit(1);
}
/* ... */
frobSomePidConfigs(pidConfigs, 2, 5);
On the other hand, if the function needs to reallocate the array and initialize any new elements, it could be done using a double pointer like this:
extern void initPidConfig(xPIDConfig_t *);
void reallocPidConfigs(xPIDConfig_t **pidConfigs, unsigned int oldSize, unsigned int newSize)
{
unsigned int i;
// Reallocate to new size
xPIDConfig_t *realloced = realloc(*pidConfigs, sizeof **pidConfigs * newSize);
if (newSize && !realloced)
{
// allocation error
exit(EXIT_FAILURE);
}
*pidConfigs = realloced;
// Initialize any additional elements
for (i = oldSize; i < newSize; i++)
{
initPidConfig(*pidConfigs + i); // or: initPidConfig(&(*pidConfigs)[i]);
// Examples of member access:
(*pidConfigs)[i].bSaturationEnable = true;
(*pidConfigs + i)->bAntiWindupEnable = true;
}
}
Example of calling code:
xPIDConfig_t *pidConfigs = NULL;
// Note: realloc of the NULL pointer in *pidConfigs is OK.
reallocPidConfigs(&pidConfigs, 0, 10);
frobSomePidConfigs(pidConfigs, 2, 5);
Limited to addressing assumptions and questions regarding your title question:
"How to use double pointers (pointer to pointer) for an array of structures properly in standard C"
First, just because the function argument might have a double pointer (i.e. xPIDConfig_t **pxPIDConfig) does not mean that the variable need to be allocated memory with a double pointer, i.e. if the function eg is called like this: funcChangeParam(&pxPIDConfig); this often means that the object being passed needs to be changed in some way, requiring that the address of be passed, not the object itself. Also, if the object itself is a pointer, (such as a pointer to several instances of a struct object.) then the function used to pass the object for modification will be prototyped with arguments such as void funcChangeParam(xPIDConfig_t **pxPIDConfig); (Note the double pointer here.)
So with this function prototype Making the allocation of memory look like this:
void funcChangeParam(xPIDConfig_t **pxPIDConfig);
//allocate memory for multiple instances of struct
xPIDConfig_t *pxPIDConfig = malloc(countOfInstances * sizeof(*pxPIDConfig);
if(pxPIDConfig)
{
//use pxPIDConfig
funcChangeParam(&pxPIDConfig);pass pointer to multiple instances of struct
And references to the object members inside the calling function could use the following notation. Eg:
//in a loop or other construct where i is defined from 0 to countOfInstances - 1
(*pxPIDConfig)[i].ePIDType = ePIDType;//modification of assignment per your example
//etc.
//The following is a trivial example for illustration purposes.
//Code here uses a simplified struct, function
//prototype, and simple calling example, the concept
//of which easily translates to what you are
//asking about.
typedef struct {
int num;
}test_s;
void change(test_s **new);
int main(){
test_s *test = malloc(10*sizeof *test);
change(&test);
return 0;
}
void change(test_s **new)
{
for(int i=0;i<10;i++)
{
(*new)[i].num = (i+1)*3; //init all instances to some value
}
}
I would like to compose a structure that possesses a contiguous array of flexible members that are, in turn, aggregation of few data fields. With that code pattern I want to: a) store multiple relevant data fields, b) have an array of such instances and c) destroy them all as simply as free(). The code bellow seems do the job:
struct s_symm_root{
unsigned int size_c;
struct {
unsigned int symm_color;
t_symm_gnrtr symm_gnrtr;
} _[];
} *symm_root;
unsigned int SIZE_C = 10;
symm_root = (struct s_symm_root*)malloc( sizeof(struct s_symm_root) + sizeof(*((struct s_symm_root*)NULL)->_) * SIZE_C );
symm_root->size_c = 1;
symm_root->_[0].symm_color = 2;
printf(" %d, %d, %ul.\n", symm_root->size_c, symm_root->_[0].symm_color, sizeof(*((struct s_symm_root*)NULL)->_));
free(symm_root);
My questions is if I can improve it a bit, specially, get rid of that ugly '_' somehow? I thought about anonymous structures but IDK how to implement them...
Thanks for suggestions!
I'm getting this error:
incompatible pointer types returning 'int [4][2]' from a function with result type 'int *'
When I try to do this
int* getValid(int blank[2]){
int row = blank[0];
int col = blank[1];
static int valid[4][2];
valid[0][0] = row;
valid[0][1] = col-1;
valid[1][0] = row;
valid[1][1] = col+1;
valid[2][0] = row-1;
valid[2][1] = col;
valid[3][0] = row+1;
valid[3][1] = col;
return valid;
}
I'm trying to return a 2D array that I generate inside the function.
I'm mostly code in Python so arrays in C are kicking my butt..
You cannot pass an array to/from a function in C. But you can pass a pointer. And that can point to an array.
So, you need a function like:
#define COLS 2
int (*getValid(int blank[2]))[COLS]
{
static int valid[4][COLS];
...
return valid;
}
Mind the parenthesis. I also use COLS for the innter dimension's length. This avoids magic numbers, i.e. repeating constants throughout your code. Use that macro for all declarations the array is involved, including the definition.
This is a "function without arguments returning a pointer to an 1D array with COLS ints". For details about arrays and pointers in C, please see a good C book and do some research on your own. Just keep in mind that while they are different types, they have a lot in common in practical use (language mechanisms behind are more difficult to understand).
Simply said, if you use return valid with the above declaration of the function, the name of the array is converted to a pointer to the first element. Which is exactly what you shall return.
The caller need to use the same pointer type (pointer to 1D array) for the result:
int (*arr)[COLS] = getValid(...);
The elements are accessed like for the original array:
arr[row][col]
Additional information: You should not use a static array in the function, unless you want to safe state between calls. Better allocate the array dynamically with malloc:
int (*arr)[COLS];
arr = malloc(sizeof(*arr) * 4); // 4 rows of the inner array
Don't forget to check if malloc failed and take appropriate measures.
Also don't forget to release the dynamically allocated array once you're done with it (but not earlier):
free(arr);
In fact, you must let an n-dimension array decay to a pointer to a n-1 dimension array. And as you use a static array, it is safe to return its address.
The only tricky thing would be to declare a function returning a pointer to an array of size 2, but a typedef can really help here:
typedef int Arr2[2];
Arr2* getValid(int blank[2]){
int row = blank[0];
int col = blank[1];
static int valid[4][2];
valid[0][0] = row;
valid[0][1] = col-1;
valid[1][0] = row;
valid[1][1] = col+1;
valid[2][0] = row-1;
valid[2][1] = col;
valid[3][0] = row+1;
valid[3][1] = col;
return valid;
}
And you can safely use it like that:
Arr2 * arr = getValid(blank);
for (i=0; i<4; i++) {
for (j=0; j<2; j++) {
printf(" %d", arr[i][j]);
}
putc('\n', stdout);
}
Compiles and runs fine without any warning.
But beware: it is safe only because valid is declared with static storage. Never return a pointer to an automatic array! (use dynamic arrays (allocated with malloc if you do not want static storage)
In C arrays are second class citizens. In order to pass them around you would need to wrap in a structure.
I.e.:
struct t_array42int{int o[4][2];} getValid(int *blank){
int row = blank[0];
int col = blank[1];
static struct t_array42int valid_container;
int (*valid)[2] = valid_container.o;
valid[0][0] = row;
valid[0][1] = col-1;
valid[1][0] = row;
valid[1][1] = col+1;
valid[2][0] = row-1;
valid[2][1] = col;
valid[3][0] = row+1;
valid[3][1] = col;
return valid_container;
}
Then you can store your function return value like:
struct t_array42int tmp = getValid(something);
Of-course there are ways to simplify this process of "wrapping" but those are often not standard or too obfuscated.
The disadvantage of using structures is that structure types aren't compared by their members layout but instead (normally) by their names. So something like this:
struct { int o[2]; } a;
struct { int o[2]; } b;
a = b; //Error
Is illegal.
You should write:
struct t_array2int { int o[2]; } a;
struct t_array2int b;
a = b; //OK
Instead.
You could also return a pointer to your array instead because it's storage is static (i.e. it won't cease to exist after returning from function):
int (*getValid(int *blank))[2]{
int row = blank[0];
int col = blank[1];
static int valid[4][2];
valid[0][0] = row;
valid[0][1] = col-1;
valid[1][0] = row;
valid[1][1] = col+1;
valid[2][0] = row-1;
valid[2][1] = col;
valid[3][0] = row+1;
valid[3][1] = col;
return valid;
}
The function prototype:
int (*getValid(int *blank))[2]
Means that if you call your function getValid with a single argument of type int * - getValid(something) and dereference it, *getValid(something) you'll get an array that can't be accessed like (*getValid(something))[2] and so have maximum 2 elements of type int.
Simple instance of the above example will be:
int (*tmp)[2] = getValid(something);
And I also suggest to write your array parameters with their real type (which can never be an array - the compiler is only fooling you into thinking that arrays can be passed by value). I.e.: void f(int a[N]) is adjusted to void f(int *a). And int * is not the same as int [N].
For a newcomer I suggest you remember that arrays are not the same as pointers but are mistreated as such very badly. Also I suggest you to never write functions with array parameters. And if you want to use arrays in a different way then to get pointer to their first element to use structures instead - much safer.
The main thing with array parameter is that:
void f(int a[2])
{
sizeof(a) == sizeof(int *); //evaluates to 1
sizeof(a) != sizeof(int [2]); //evaluates to 1
}
I am getting back into using C, but I've been spoiled by generics in other languages. I have made it to the following piece of code in my implementation of a resizable array:
typdef struct {
void** array;
int length;
int capacity;
size_t type_size;
} Vector;
void vector_add(Vector* v, void* entry) {
// ... code for adding to the array and resizing
}
int main() {
Vector* vector = vector_create(5, sizeof(int));
vector_add(vector, 4); // This is erroneous...
// ...
}
In my attempt to make this generic, I'm now unable to add an integer to the vector without storing it in memory somewhere else.
Is there any way to make this work (either as is, or possibly a better approach to generics)?
For my answer I am assuming that you are not familiar with the sections of memory (ie the use of the memory pool).
In my attempt to make this generic, I'm now unable to add an integer to the vector without storing it in memory somewhere else.
If you want to create a generic structure (as you did) then you will need to use void pointers. Consequently, from the use of void pointers you will need to store the values for each field on the memory pool, or uncommonly on the stack. Note, the structure is composed of void pointers and hence only memory addresses are contained within the structure, pointing to other locations in memory where the values are.
Be careful if you declare them on the stack as once your stack frame is popped from the call stack those memory addresses are not considered to be valid and hence may be used by another stack frame (overwriting your existing values within that collection of memory addresses).
Aside: If you migrate to C++ then you can consider the use of C++ templates.
Yes; you can embrace Greenspun's Tenth Rule and develop a full blown dynamic language in C, and in the process, develop a relatively clean C run time that can be used from within C.
In this project I did just that, as have others before me.
In the C run time of this project, a generic number would be created from a C number like this:
val n = num(42);
because of the way val is represented, it takes up only a machine word. A few bits of type tag are used to distinguish a number from a pointer, from a character, etc.
There is also this:
val n = num_fast(42);
which is much faster (a bit manipulation macro) because it doesn't do any special checks that the number 42 fits into the "fixnum" range; it's used for small integers.
A function that adds its argument to every element of a vector could be written (very inefficiently) like this:
val vector_add(val vec, val delta)
{
val iter;
for (iter = zero; lt(iter, length(vec)); iter = plus(iter, one)) {
val *pelem = vecref_l(vec, iter);
*pelem = plus(*pelem, delta);
}
return nil;
}
Since plus is generic, this will work with fixnums, bignums and reals, as well as with characters, since it is possible to add integer displacements to characters via plus.
Type mismatch errors will be caught by the lower level functions and turned into exceptions. For instance if vec isn't something to which length can be applied, length will throw.
Functions with a _l suffix return a location. Wherease vecref(v, i) returns the value at offset i in vector v, vecref_l(v, i) returns a pointer to the val typed location in the vector which stores that value.
It's all C, just with the ISO C rules bent a little bit: you can't make a type like val efficiently in strictly conforming C, but you can do it quite portably to architectures and compilers you care about supporting.
Our vector_add isn't generic enough. It's possible to do better:
val sequence_add(val vec, val delta)
{
val iter;
for (iter = zero; lt(iter, length(vec)); iter = plus(iter, one)) {
val elem = ref(vec, iter);
refset(vec, iter, plus(elem, delta));
}
return nil;
}
By using the generic ref and refset, this now works with lists and strings also, not only vectors. We can do something like:
val str = string(L"abcd");
sequence_add(str, num(2));
The contents of str will change to cdef since a displacement of 2 is added to each character, in place.
Your idea can be done:
int *new_int = (int*)malloc(sizeof(int));
*new_int = 4;
vector_add(vector, new_int);
Naturally, it would be a good idea to do a int *create_int(int x) function or something similar:
int *create_int(int x)
{
int *n = (int*)malloc(sizeof(int));
*n = 4;
return n;
}
//...
vector_add(vector, create_int(4));
If your environment allows it you may consider using a well tested, widely used library that already manages all that, such as Glib. Or even C++.
You can avoid having many many small allocations by storing the data instead of pointers to it, like
typedef struct {
char* array;
int length;
int capacity;
size_t type_size;
} Vector;
bool vector_add(Vector* v, void* entry)
{
if (v->length < v->capacity || vector_expand(v)) {
char* location = v->array + (v->length++)*(v->type_size);
memcpy(location, entry, v->type_size);
return 1;
}
return 0; // didn't fit
}
int main()
{
Vector* vector = vector_create(5, sizeof(int));
int value = 4;
vector_add(vector, &value); // pointer to local is ok because the pointer isn't stored, only used for memcpy
}
Yes, here's an implementation of mine (similar to yours) that may help. It uses macros that can be wrapped with function calls for immediate values.
#ifndef VECTOR_H
# define VECTOR_H
# include <stddef.h>
# include <string.h>
# define VECTOR_HEADROOM 4
/* A simple library for dynamic
* string/array manipulation
*
* Written by: Taylor Holberton
* During: July 2013
*/
struct vector {
void * data;
size_t size, len;
size_t headroom;
};
int vector_init (struct vector *);
size_t vector_addc (struct vector *, int index, char c);
size_t vector_subc (struct vector *, int index);
// these ones are just for strings (I haven't yet generalized them)
size_t vector_adds (struct vector *, int index, int iend, const char * c);
size_t vector_subs (struct vector *, int ibegin, int iend);
size_t vector_addi (struct vector *, int index, int i);
size_t vector_subi (struct vector *, int index);
# define vector_addm(v, index, datatype, element) \
do { \
if (!v) return 0; \
\
if (!v->size){ \
v->data = calloc (v->headroom, sizeof (datatype)); \
v->size = v->headroom; \
} \
\
datatype * p = v->data; \
\
if (v->len >= (v->size - 2)){ \
v->data = realloc (v->data, \
(v->size + v->headroom) * sizeof (datatype)); \
p = v->data; \
memset (&p[v->size], 0, v->headroom * sizeof(datatype));\
v->size += v->headroom; \
} \
\
if ((index < 0) || (index > v->len)){ \
index = v->len; \
} \
\
for (int i = v->len; i >= index; i--){ \
p[i + 1] = p[i]; \
} \
\
p[index] = element; \
\
v->len++; \
\
} while (0)
# define vector_subm(v, index, datatype) \
do { \
if (!v || !v->len){ \
return 0; \
} \
\
if ((index < 0) || (index > (v->len - 1))){ \
index = v->len - 1; \
} \
\
datatype * p = v->data; \
\
for (int i = index; i < v->len; i++){ \
p[i] = p[i + 1]; \
} \
\
v->len--; \
\
if ((v->size - v->len) > v->headroom){ \
v->data = realloc (v->data, ((v->size - v->headroom) + 1) * sizeof (datatype));\
v->size -= v->headroom; \
} \
\
} while (0)
#endif
And I usually wrap them like:
size_t vector_addi (struct vector * v, int index, int i){
vector_addm (v, index, int, i);
return v->len;
}
I haven't had this code-reviewed, but I've been using it in a large program I'm writing and I haven't had any memory errors from them (using valgrind).
The only thing that is really missing (I've been meaning to add) the ability to add and subtract arrays from arrays.
Edit: I believe you can also do this same sort of thing with stdarg.h, but I've never tried it.
You asked for a better approach? Here ist is: https://github.com/m-e-leypold/glitzersachen-demos/tree/master/generix/v0-2011 (Disclosure: This is my code).
Let me explain very shortly:
I wanted type safe generic containers (which in other languages would be provided by proper generics (Ada) or parametric polymorphism (OCaml). This is the the feature that is most missing in C.
Macros just cannot do it (I'm not
going to explain that in detail. Suffice to say: The result of a template expansion or
generic instantiation should be a module in it's own right: In C this means, there are pre
processor symbols exported respectively can be used for module configuration (like
-DUSE_PROCESS_QUEUE_DEBUGCODE) you couldn't do that if you used C macros to generate
instances.
I'm abstracting over element type by moving element size and all relevant operation into a descriptive structure. This will be passed to every invocation of the generic code. Note that the descriptor describes the element type, so a descriptor instance will be needed once per generic instance.
I'm using a template processor to create a thin type safe frontend module to the generic code.
Example:
This is the prototype for the generic code to retrieve an element:
void fifo_get ( fifo_DESCRIPTOR* inst, fifo* , void* var );
This is the descriptor type:
typedef struct fifo_DESCRIPTOR {
size_t maxindex;
size_t element_size;
} fifo_DESCRIPTOR;
This is the template code in the type safe wrapper template:
<<eT>> <<>>get ( <<T>>* f ) {
<<eT>> e; fifo_get( &DESCRIPTOR, (fifo*) f, (void*) &e ); return e;
}
And this is what the template expander (instantiating an generic) produces from the template:
float floatq_get ( floatq* f ) {
float e; fifo_get( &DESCRIPTOR, (fifo*) f, (void*) &e ); return e;
}
All this has a nice make integration, but hardly any type safety in instantiation. Every error only crops up when compiling with cc.
I cannot justify at the moment, why to stick with source text templates in C instead of migrating to C++. For me, it was just an experiment.
Regards.
This approach will probably horrify you, but it can be made to work if you don't need any type-specialized logic:
// vector.h
#ifndef VECTOR_H
#define VECTOR_H
#define VECTOR_IMP(itemType) \
typedef struct { \
itemType * array; \
int length; \
int capacity; \
} itemType##_Vector; \
\
static inline void itemType##_vector_add(itemType##_Vector* v, itemType v) { \
// implementation of adding an itemType object to the array goes here \
} \
\
[... other static-inline generic vector methods would go here ...] \
// Now we can "instantiate" versions of the Vector struct and methods for
// whatever types we want to use.
VECTOR_IMP(int);
VECTOR_IMP(float);
VECTOR_IMP(char);
#endif
... and some example calling code:
#include "vector.h"
int main(int argc, char ** argv)
{
float_Vector fv = {0};
int_Vector iv = {0};
char_Vector cv = {0};
int_vector_add(&iv, 5);
float_vector_add(&fv, 3.14f);
char_vector_add(&cv, 'A');
return 0;
}
Instead of having the vector class store the added object, you could just return a pointer to the location where the caller can store it:
typdef struct {
char *buffer;
size_t length;
size_t capacity;
size_t type_size;
} Vector;
void *vector_add(Vector* v)
{
if (v->length == v->capacity) {
// ... increase capacity by at least one
// ... realloc buffer to capacity * type_size
}
return v->buffer + v->type_size * v->length++;
}
// in main:
*(int*)vector_add(v) = 4;
Using some non-standard GNU C extensions, it is possible to define generic functions with inferred parameter types. This macro defines a nested function in a statement expression and infers the parameter type using typeof:
#include <stdio.h>
#define fib(n1) ({\
typeof(n1) func(typeof(n1) n){\
if (n <= 1)\
return n;\
return func(n-1) + func(n-2);\
}\
func(n1);\
})
int main()
{
printf("%d\n",fib(3));
printf("%f\n",fib(3.0));
return 0;
}
foo.c
#include "main.h"
unsigned char currentBar;
struct foo myFoo[getNumBars()];
void initMyFoo(void)
{
currentBar=(getNumBars()-1);
for(i=0; i<(sizeof(myFoo)/sizeof(myFoo[0])); i++)
{
myFoo[i].we = 1;
myFoo[i].want = 0;
myFoo[i].your = 0;
myFoo[i].soul = 0;
}
}
main.c
#include "foo.h"
unsigned char getNumBars()
{
return getDipSwitchValues();
}
initMyFoo();
(struct foo is declared in foo.h.)
This code has to execute without hard coding a number for Bars, as the number of Bars will change according to whatever the user sets his DIP switches. Right now I'm not able to initialize myFoo; I get the error "constant expression expected in initializer." Do I have to initialize it like:
struct foo myFoo[];
and change it later? If so, how do I make myFoo[] the correct length? I obviously don't have a constant available that corresponds to the desired size. Do I need to dynamically allocate this or something?
I found this similar answer but it wasn't too helpful for me - C++ a class with an array of structs, without knowing how large an array I need
struct foo* myFoo;
unsigned int myFooSize;
void initMyFoo(void)
{
myFooSize = getNumBars();
myFoo = malloc(myFooSize * sizeof(*myFoo));
for (i=0; i<myFooSize; i++) {
/* ... */
}
}
void cleanupMyFoo(void)
{
free(myFoo);
myFoo = NULL;
myFooSize = 0;
}
1 - in C99 you can use variable length arrays, which allow you to create arrays whose lengths are runtime-determined. You can also use them via compiler extensions (GCC supports them for non-C99 C and C++), but that's not a portable solution.
int someUnknownSize = 0;
/* some code that changes someUnknownSize */
struct foo myFoo[someUnknownSize];
2 - Declare a pointer that will be allocated memory at runtime with malloc or calloc.
struct foo *fooPtr = 0; /* null pointer to struct foo */
int sizeToAlloc = 0;
/* determine how much to allocate/modify sizeToAlloc */
fooPtr = malloc(sizeToAlloc * sizeof(*fooPtr));
/* do stuff with the pointer - you can treat it like you would an array using [] notation */
free(fooPtr);
I usually go for an expected maximum array size and if it's needed, just resize it:
type * a = calloc(sizeof(type),exp_array_size);
and upon pushing a new value onto the array (yeak, OK, I treat it as if it was a stack...), I check its current size against the new one:
if (current_size > max_size) {
max_size *= 2;
realloc(a,max_size*sizeof(type));
}