vector implementation and data member definition - c

In a number of vector implementations which I've seen, the following definition of vector structure is used:
struct vector {
void **data;
int size;
int count;
};
Why do we need pointer-to-pointer member here?

Because it can be a vector of pointer elements which can have any type (including pointers) since void * is convertible to any pointer type in c1. Also, elements will be separated by the size of a pointer making it simpler to work with.
You can then get the actual value by dereferencing the void * pointer to the element after casting to the appropriate pointer type or simply getting a pointer if the elements are pointers.
Sample implementation (far from complete of course2)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct vector
{
void **data;
size_t count;
size_t size;
};
void
vector_new(struct vector *vector, size_t size, size_t nmemb)
{
vector->data = NULL;
vector->count = 0;
vector->size = size;
}
void
vector_push_back(struct vector *vector, void *value)
{
unsigned char **data;
data = realloc(vector->data, sizeof(*data) * (vector->count + 1));
if (data == NULL)
return; // Probably return an error indicator
vector->data = (void *) data;
data[vector->count] = malloc(vector->size);
if (data[vector->count] == NULL)
return; // Probably return an error indicator
memcpy(data[vector->count], value, vector->size);
vector->count++;
}
void *
vector_get(struct vector *vector, size_t index)
{
return (unsigned char *) vector->data[index];
}
int
main(void)
{
struct vector vector;
vector_new(&vector, sizeof(float), 100);
for (int i = 0 ; i < 10 ; ++i)
{
float value;
value = (float) rand() / RAND_MAX;
fprintf(stdout, "vector[%d] %f\n", i, value);
vector_push_back(&vector, &value);
}
fprintf(stdout, "Read them to check!\n");
for (int i = 0 ; i < 10 ; ++i)
{
float value;
value = *(float *) vector_get(&vector, i);
fprintf(stdout, "vector[%d] %f\n", i, value);
}
return 0;
}
1 This kind of construction is generally a bad design choice since it's quite hard to maintain and it's harder to write accessor functions. Nevertheless there are geniune use cases for it of course. The c language should not be used for generic typing, trying to enforce that has usually less benefits than problems.
2It lacks a free/destroy function for example.

See a nice recap of functionality expected of a vector at vector (the link goes to vector in C++, but the information on their functionality applies to vector in general, in C or C++ or any other language for that matter).
Salient points are abstracted below. The functionality gives you clues about why the data structure is the way it is.
Vectors are sequence containers representing arrays that can change in
size.
Just like arrays ... their elements can also be accessed using offsets
on regular pointers to its elements ... But unlike arrays, their size
can change dynamically ...
... vector containers may allocate some extra storage to accommodate
for possible growth ... and thus, vectors do not reallocate each time
an element is added to the container.
struct vector {
void **data; // needs to act like an array of pointers i.e. void *data[]
int size; // actual memory allocated may be in access of count
int count; // current count of elements in data
};

It's quite common. You can treat it as:
char *data[size];
So each element is a string, or any storage type which size is unknown until runtime. For exampleint i=123;
data=malloc(sizeof(int));
memcpy(data, &i, sizeof(i));

Related

How to get the i-th element of vector

I want to get the i-th element of the void*. I understand that it is void type and I have to give it a certain data type. The idea behind this is that it should be working for different data types. If i am right about the issue, how do I implement that?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_STR_LEN 64
typedef struct Vector {
void *data;
size_t element_size;
size_t size;
size_t capacity;
} Vector;
// Allocate vector to initial capacity (block_size elements),
// Set element_size, size (to 0), capacity
void init_vector(Vector *vector, size_t block_size, size_t element_size){
vector->data = (void*) malloc(block_size * element_size); // i am questioning whether this is correct
vector->element_size = element_size;
vector->size = 0;
vector->capacity = block_size * element_size;
}
void resize(Vector *vector, size_t new_size){
void *data2 = (void*) malloc(new_size * vector->element_size);
int i=0;
memmove(data2,vector->data,new_size * vector->element_size);
vector->data = data2;
if(new_size > vector->size){
for(i=vector->size-1;i<new_size;i++){
vector->data[i]=0; // here is the problem
}
}else{
vector->size = new_size;
}
vector->capacity = new_size*vector->element_size;
}
C is a language for adrenaline junkies. It's always akin to free climbing, sky diving and formula 1 racing.
Defensive programming is not a perfect protection against all kinds of "sabotage" by a caller, but it is better than nothing.
The code below is in that very spirit. It cannot detect all possible things going wrong (like corrupted memory or if the data pointer in the vector is just a random value), but it showcases the least amount of defensive programming one should use if writing in that language.
C has "pointer arithmetic" as a feature. So, if you have e.g. a uint16_t * p = 1000; and you access p + 1, it is accessing 1002 (i.e. p + sizeof(uint16_t) * 1).
And this is the trick, how you can access an element in such a very weakly typed vector. You cast the void pointer to a byte pointer (as an example) and then use pointer arithmetic.
void* at(Vector* v, size_t index) {
// C needs manual sanity checks for the preconditions
if (NULL == v) return NULL;
if (v->size <= index) return NULL;
if (NULL == v->data) return NULL;
// now we can be (reasonably, as much as we can tell) sure, we did not get garbage as arguments...
return ((char*)(v->data)) + index * v->element_size;
}
Your vector functions can not [meaningfully] access the array data using the index or pointer syntax such as:
vector->data[i]=0;
That is because it's a void * pointer but, more importantly, if element_size is (e.g.) 8, does that mean vector->data points to a double or unsigned long long?
Only the caller of your functions can do this:
Vector *vec = calloc(1,sizeof(*vec));
init_vector(vec,100,sizeof(double));
double *ptr = vec->data;
for (size_t idx = 0; idx < vec->size; ++idx)
ptr[idx] = idx;
When you extend the array in resize, you can only mem* functions.
Replace your for loop with:
memset(&vector->data[vector->size * vector->element_size],0,
(new_size - vector->size) * vector->element_size);
UPDATE:
There are some more issues. Although you can have capacity be a byte count, it is more usual for it to be an element count (just like size).
When I create such dynamic array/vector objects/functions, I usually do not have resize do initialization of the elements.
That's because it doesn't [really] know how to initialize the elements. For a c++ vector, the constructor knows.
So, if we wish resize [and init_vector] to do this, we need to provide a function pointer for this.
Here's some refactored code to illustrate:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_STR_LEN 64
typedef struct Vector Vector;
typedef void (*vecinit_p)(Vector *,size_t idx,size_t count);
struct Vector {
void *data; // pointer to array data
size_t element_size; // number of bytes in an array element
size_t capacity; // number of elements allocated
size_t size; // number of elements in use
vecinit_p initfnc; // pointer to init function
};
// vecptr -- get pointer to i'th element
void *
vecptr(Vector *vec,size_t idx)
// idx -- index of desired element
{
void *ptr;
idx *= vec->element_size;
ptr = vec->data;
ptr += idx;
return ptr;
}
// init_data -- initialize data elements
void
init_data(Vector *vec,size_t idx,size_t count)
// idx -- starting index
// count -- number of elements to initialize
{
void *ptr = vecptr(vec,idx + 0);
void *end = vecptr(vec,idx + count);
memset(ptr,0,end - ptr);
}
// Allocate vector to initial capacity (block_size elements),
// Set element_size, size (to 0), capacity
void
init_vector(Vector *vec, size_t block_size, size_t element_size,vecinit_p fnc)
// block_size -- number of elements
// element_size -- number of bytes in a single element
{
size_t new_len = block_size * element_size;
vec->data = calloc(1,new_len);
vec->element_size = element_size;
vec->size = 0;
vec->capacity = block_size;
// provide a "default" constructor
if (fnc == NULL)
fnc = init_data;
vec->initfnc = fnc;
fnc(vec,0,vec->capacity);
}
// resize -- resize the array
void
resize(Vector *vec, size_t new_cap)
// new_cap -- desired new capacity
{
// get byte length
size_t new_len = new_cap * vec->element_size;
void *data2 = malloc(new_len);
if (data2 == NULL) {
perror("malloc");
exit(1);
}
vec->data = data2;
// get old capacity and set new capacity
size_t old_cap = vec->capacity;
vec->capacity = new_cap;
// initialize new elements
if (new_cap > old_cap)
vec->initfnc(vec,old_cap,old_cap - new_cap);
}
// vecpush -- append element to array
// RETURNS: pointer to "pushed" element
void *
vecpush(Vector *vec)
{
// increase array capacity if needed
if (vec->size >= vec->capacity)
resize(vec,vec->capacity + 10);
// point to element
void *ptr = vecptr(vec,vec->size++);
return ptr;
}

inconsistent results for struct with array in c

I'm new to programming and to C, and I just learned about structs. I'm trying to use them to make an array which can change size as required (so, if the array gets full, it creates a new array double the size, copies the old array into the new one and deletes the old one). All I've done so far is create the struct and the functions for setting it up, and already I'm having problems. The main problem is that, sometimes when I run it it does exactly what I expect it to, which is create the struct, return a pointer to said struct, and then print all elements of the contained array. Other times when I run it, it does nothing at all! I don't get how it can work sometimes, and sometimes not! Obviously i'm doing something really wrong, but I can't work out what. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int cap;
int used;
void (*cpy) (int *, const int *, int);
//void (*append) (int);
int array[];
} dynArray;
dynArray * new_dynArray(int *, int);
void copy(int *, const int *, int);
int main(void) {
int start_arr[] = {1,2,3,4,5,6};
// create new dynArray, pass start array and number of elemnts
dynArray *arr = new_dynArray(start_arr, \
sizeof(start_arr) / sizeof(start_arr[0]));
// print all elements of dynArray
for (int i=0; i<(arr->used); i++) {
printf("%d, %d\n", arr->array[i], i);
}
free(arr);
return 0;
}
dynArray * new_dynArray(int init_arr[], int size) {
//printf("%d", size);
// if number of elements >= 4 then dynArray size is double, else 8
int init_cap = (size >= 4) ? 2 * size : 8;
// create pointer with enough space for struct and the actual array
dynArray *arr = (dynArray *) malloc(sizeof(dynArray) + init_cap );
arr->cap = init_cap;
arr->used = size;
// assign address of funciton copy to arr->cpy
arr->cpy = copy;
// call the function, to copy init_arr to arr->array
arr->cpy(arr->array, init_arr, size);
return arr;
}
void copy(int dest[], const int src[], int src_size) {
// just copy initial array to new array
int i;
memcpy(dest, src, src_size*sizeof(int));
/*
for (i=0; i<src_size; i++) {
dest[i] = src[i];
printf("%d\n", dest[i]);
}*/
}
So I call init_dynArray, sending a normal array and the number of elements in the array. init_dynArray uses malloc to create space in memory for the struct + the inintal size of the array, set up everything in the struct and copy the array, and then return a pointer to it. I don't get how it can only work some of the time. Hope yuo guys can help, thanks!
The problem in your code is on this line:
dynArray *arr = (dynArray *) malloc(sizeof(dynArray) + init_cap );
You need to multiply init_cap by sizeof(int)
dynArray *arr = (dynArray *) malloc(sizeof(dynArray) + sizeof(int)*init_cap );
You should also use size_t for the init_cap's type.
Note: Storing a pointer to the copying function inside the struct would be useful if your dynamic array consisted of opaque elements that require non-trivial copying. Since copying ints can be accomplished with a simple memcpy, there is no need to store a function pointer in dynArray.

Allocating memory to my stack dynamically (only if needed)

I need to allocate memory to an array inside my struct, this array has no defined size at the beginning when i define the struct:
typedef struct stacks {
int size; // Stores the size of my -values- array
int sp; //points to the top of the stack, my stackpointer
int *values;
} STACKS;
So, to initialize my struct i wrote this function, that allocates (using calloc?) memory to my array, and i put inside SIZE variable, the new size of my array .
#define MAXIMUM 10
int initStacks(STACKS *s){
s->values = calloc(MAXIMUM,sizeof(int));
s->size = MAXIMUM;
s->sp = 0;
return 0;
}
Now, if i want to push something to the top of the stack (LIFO) i use this:
int pushs(STACKS *s, int x){
if (s->sp==s->size) {
realloc(s->values, MAXIMUM * sizeof(int));
s->size*=2;
}
s->values[s->sp]=x;
s->sp++;
}
Is this the correct way of doing this?
Is realloc working as it should in my function?
Thank you very much for your help!
EDIT:
would this make more sense? This way, i really don't need to declare the value of the array, being that defined with #define maximum 10
typedef struct stacks {
int size; // guarda o tamanho do array valores
int sp;
int *values;
} STACKS;
int initStacks(STACKS *s){
s->values = calloc(1,sizeof(int));
s->size = 1;
s->sp = 0;
return 0;
}
int isEmptys(STACKS *s){
return((s->sp)==0);
}
int pushs(STACKS *s, int x){
s->size++;
realloc(s->values, s->size * sizeof(int));
s->values[s->sp]=x;
s->sp++;
}
Assuming you have an original size factor (the name capacity would be as-appropriate, if not more so), your original code lacks several things:
Compares the size against a constant, rather than the current sp against the stack current size.
Does not save nor test the return result of realloc
Does not actually double the allocation (you're missing the 2x in the realloc expression.
Declares an int return result, but no such return exists.
Has no way of communicating back to the caller the push result (success or not). That missing return result would be ideal for this, btw.
Addressing all of these:
int pushs(STACKS *s, int x)
{
if (s->sp == s->size)
{
void *pv = realloc(s->values, 2 * s->size * sizeof *(s->values));
if (pv != NULL)
{
s->values = pv;
s->size *= 2;
}
else
{
fprintf(stderr, "Failed to resize stack\n");
return -1;
}
}
s->values[s->sp++] = x;
return 0;
}
Untested, but hopefully close enough.
Best of luck
Although not directly an answer to the actual question, but more to the general problem, I post this as it does not fit into a comment.
If you expect excessive push/pop operations and memory usage, the following might be an alternative:
typedef struct SubStack_s {
struct SubStack_s *prev;
int data[ENTRIES_PER_SEGMENT];
} SubStack;
typedef struct {
SubStack *tos; // init to NULL
size_t sp; // init to 0
} Stack;
The basic idea is to push elements onto each substack until full (as you already do). If the current one is full, you alloc a new one, chain them (new->prev = old) and continue with the new one (storing new to Stack.tos)
Pop works similar, free'ing each substack once it is not used anymore.
That concept is called "fragmented stack". It is much more efficient than the realloc-approach (it avoids copying) and does not fragment RAM as all block are of equal size. Oh, and it allows to have pointers into the stack, which the realloc-varaint does not, because the address of the stack can change.

C: adding element to dynamically allocated array

I've tried to search out a solution via Google: I couldn't find anything that helped; it even seemed as if I was doing this correctly. The only pages I could find regarding sending my dynamically allocated array through a function dealt with the array being inside a struct, which is scalar of course, so behaves differently. I don't want to use a struct right now -- I'm trying to learn about DAM and working with pointers and functions.
That said, I'm sure it's very elementary, but I'm stuck. The code compiles, but it freezes up when I run the executable. (I'm using minGW gcc, if that matters. And I'm not clear at all, right now, on how to use gdb.)
Here's the code (eventually, I want the entire code to be an ArrayList-like data structure):
#include <stdio.h>
#include <stdlib.h>
void add( int element, int *vector);
void display_vector( int *vector );
void initialize_vector( int *vector );
int elements = 0;
int size = 10;
int main(void)
{
int *vector = 0;
initialize_vector(vector);
add(1, vector);
//add(2, vector);
//add(3, vector);
//add(4, vector);
//add(5, vector);
//add(6, vector);
//add(7, vector);
//add(8, vector);
//add(9, vector);
//add(10, vector);
//add(11, vector);
display_vector(vector);
return 0;
}
void add( int element, int *vector)
{
vector[elements++] = element;
return;
}
void display_vector( int *vector )
{
int i;
for( i = 0; i < elements; i++)
{
printf("%2d\t", vector[i]);
if( (i + 1) % 5 == 0 )
printf("\n");
}
printf("\n");
return;
}
void initialize_vector( int *vector )
{
vector = (int *)malloc(sizeof(int) * size);
}
Edited to make a little bit more clear.
The problem is your init routine is working with a copy of "vector" and is malloc'ing into that copy rather than the original vector pointer. You loose the pointer to the memory block on the return from the initialize.
Change parameter for vector to a handle (pointer to pointer) in this function
void initialize_vector( int **vector )
{
*vector = (int *)malloc(sizeof(int) * size);
}
Then change the call to init to this
initialize_vector(&vector);
I didn't compile this, but it should fix the code.
In C, function arguments are passed by value, which means there is a local copy for every arguments you passed to a function, if you change an argument in a function, you only change the local copy of that argument. So if you want to change the value of an argument in a function, you need to pass its address to that function, derefer that address and assign to the result in that function.
Enough for the theory, here is how to fix your code:
void initialize_vector( int **vector );
initialize_vector(&vector);
void initialize_vector( int **vector )
{
*vector = (int *)malloc(sizeof(int) * size);
}
In addition of other replies, I would suggest another approach.
Assuming at least C99 compliant compiler, I would rather suggest to keep the allocated size in a member of a structure ending with a flexible array member (see also this) like:
typedef struct vector_st {
unsigned count; // used length
unsigned size; // allocated size, always >= length
int vectarr[];
} Vector;
Then you would construct such a vector with
Vector* make_vector (unsigned size) {
Vector* v = malloc(sizeof(Vector)+size*sizeof(int));
if (!v) { perror("malloc vector"); exit (EXIT_FAILURE); };
memset (v->vectarr, 0, size*sizeof(int));
v->count = 0;
v->size = size;
}
To add an element into a vector, returning the original vector or a grown one:
Vector* append_vector (Vector*vec, int elem) {
assert (vec != NULL);
unsigned oldcount = vec->count;
if (oldcount < vec->size) {
vec->vectarr[vec->count++] = elem;
return vec;
} else {
unsigned newsize = ((4*oldcount/3)|7) + 1;
Vector* oldvec = vec;
vec = malloc(sizeof(Vector)+newsize*sizeof(int));
if (!vec) { perror("vector grow"); exit(EXIT_FAILURE); };
memcpy (vec->vectarr, oldvec->vectarr, oldcount*sizeof(int));
memset (vec->vectarr + oldcount, 0,
(newsize-oldcount) * sizeof(int));
vec->vectarr[oldcount] = elem;
vec->count = oldcount+1;
vec->size = newsize;
free (oldvec);
return vec;
}
}
and you could code:
Vector* myvec = make_vector(100);
myvec = append_vector(myvec, 35);
myvec = append_vector(myvec, 17);
for (int i=0; i<150; i++)
myvec = append_vector(myvec, i*2);
To release such a vector, just use free(myvec);
If you really don't want to use any struct you should keep in separate variables the used length of your vector, the allocated size of your vector, the pointer to your dynamically allocated array:
unsigned used_count; // useful "length"
unsigned allocated_size; // allocated size, always not less than used_count
int *dynamic_array; // the pointer to the dynamically allocated array
If you want to be able to manage several vectors, then either pack together the above useful length, allocated size and dynamic array into some struct dynamic_array_st (whose pointer you would pass to appropriate routines like make_dynamic_vector(struct dynamic_array_st*), append_dynamic_vector(struct dynamic_array_st*, int), etc ....) or else pass them as three separate formals to similar routines, and then you'll need to pass their address because the routines would change them, e.g. create_dynamic_vector(unsigned *countptr, unsigned *sizeptr, int**vectarrptr) that you would invoke as create_dynamic_vector(&mycount, &mysize, &myvectarr); etc.
I do think that a flexible array member is still the cleanest approach.

Array of POINTERS to Multiple Types, C

Is it possible to have an array of multiple types by using malloc?
EDIT:
Currently I have:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define int(x) *((int *) x)
int main() {
void *a[10];
a[0] = malloc(sizeof(int));
int(a[0]) = 4;
char *b = "yola.";
a[1] = malloc(strlen(b)*sizeof(char));
a[1] = b;
printf("%d\n", int(a[0]));
printf("%s\n", a[1]);
}
But it's messy. Other ways?
EDIT: Cleaned it up a bit.
You can't have an array of different types, exactly. But you can achieve a similar effect (for some purposes at least) in a number of different ways.
If you just want a few values of different types packaged together, but the number and types of values don't change, you just need a struct and can access them by name:
struct s_item {
int number;
char str[100];
} item;
item.number = 5;
strcpy(item.str,"String less than 100 chars");
If you know what types you might use, you can create a union, or a struct containing a union so you can tag it with the type. You can then create an array of those. The type member lets you check to see what you stored in each array element later.
enum ElementType { et_str, et_int, et_dbl };
struct Element {
ElementType type;
union {
char *str;
int i;
double d;
}
};
struct Element *arr = malloc(sizeof(struct Element) * 3);
arr[0].type = et_str;
arr[0].str = strdup("String value"); /* remember to free arr[0].str */
arr[1].type = et_int;
arr[1].i = 5;
arr[2].type = et_dbl;
arr[2].d = 27.3;
/* access the values.. */
for (int i = 0; i < 3; i++) {
switch(arr[i].type) {
case et_str: printf("String: %s\n",arr[i].str); break;
case et_int: printf("Integer: %d\n",arr[i].i); break;
case et_dbl: printf("Double: %f\n",arr[i].d); break;
}
}
/* The strings are dynamically allocated, so free the strings */
for (int i = 0; i < 3; i++)
if (arr[0].type == et_str) free(arr[0].str);
/* free the malloc'ed array */
free(arr);
/* etc., etc. */
This approach may waste space because:
Each element has an extra value to keep track of the type of data it holds
The struct may have extra padding between its members
The types in the union may be different sizes, in which case the union will be as large as the largest type
If you have another way of knowing what type you've stored in each element, you can use just the bare union without the struct wrapping it. This is a little more compact, but each element will still be at least as large as the largest type in the union.
You can also create an array of void * values. If you do this, you'll have to allocate the items somehow and assign their addresses to the array elements. Then you'll need to cast them to the appropriate pointer type to access the items. C doesn't provide any runtime type information, so there's no way to find out what type of data each element points at from the pointer itself -- you must keep track of that on your own. This approach is a lot more compact than the others when the types you're storing are large and their sizes vary a lot, since each is allocated separately from the array and can be given only the space needed for that type. For simple types, you don't really gain anything over using a union.
void **arr = malloc(3 * sizeof(void *));
arr[0] = strdup("Some string"); /* is a pointer already */
arr[1] = malloc(sizeof(int));
*((int *)(arr[1])) = 5;
arr[2] = malloc(sizeof(double));
*((double *)(arr[2])) = 27.3;
/* access the values.. */
printf( "String: %s\n", (char *)(arr[0]) );
printf( "Integer: %d\n", *((int *)(arr[1])) );
printf( "Double: %f\n", *((double *)(arr[2])) );
/* ALL values were dynamically allocated, so we free every one */
for (int i = 0; i < 3; i++)
free(arr[i]);
/* free the malloc'ed array */
free(arr);
If you need to keep track of the type in the array, you can also use a struct to store the type along with the pointer, similar to the earlier example with the union. This, again, is only really useful when the types being stored are large and vary a lot in size.
enum ElementType { et_str, et_int, et_dbl };
struct Element {
ElementType type;
void *data;
};
struct Element *arr = malloc(sizeof(struct Element) * 3);
arr[0].type = et_str;
arr[0].data = strdup("String value");
arr[1].type = et_int;
arr[1].data = malloc(sizeof(int));
*((int *)(arr[1].data)) = 5;
arr[2].type = et_dbl;
arr[2].data = malloc(sizeof(double));
*((double *)(arr[2].data)) = 27.3;
/* access the values.. */
for (int i = 0; i < 3; i++) {
switch(arr[i].type) {
case et_str: printf( "String: %s\n", (char *)(arr[0].data) ); break;
case et_int: printf( "Integer: %d\n", *((int *)(arr[1].data)) ); break;
case et_dbl: printf( "Double: %f\n", *((double *)(arr[2].data)) ); break;
}
}
/* again, ALL data was dynamically allocated, so free each item's data */
for (int i = 0; i < 3; i++)
free(arr[i].data);
/* then free the malloc'ed array */
free(arr);
You can easily have an array of pointers that point to different types. Of course for it to be very useful, you'd need to have some way of recording or determining what type is currently referenced by each element.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// This implicitly allocates memory to store 10 pointers
void *a[10];
// The first element will be a pointer to an int
// Allocate the memory it points to, then assign it a value.
a[0] = malloc(sizeof(int));
*( (int *)a[0] ) = 4;
// The second element will be a pointer to char; for simplicity,
// I'm hardcoding the length of the string + 1 for the null byte.
a[1] = malloc( 6*sizeof(char) );
strncpy( a[1], "hello", 5 );
printf( "%d\n", *( (int *)a[0] ) );
printf( "%s\n", a[1] );
}
No, all the elements have to be of the same type. You might get away with an array of structures.
struct mixed {
enum {
INTEGER,
STRING,
} type;
union {
int num;
char *str;
} value;
};
struct mixed v[10];
v[0].type = INTEGER;
v[0].value.num = 10;
I myself would never do such a thing (seems messy). But your array-of-void* approach is similar: you have to store the information on the type somewhere.
I'm not sure what you want to achieve but there are two possibilities:
1 - You don't actually want an array but a struct:
struct {
int number;
char *string;
} a;
In this case you can access the number as a.number and the string as a.string.
2 - You want an array of variant type. In C, you can use unions (preferably tagged) for variant types:
struct Variant {
int type;
union {
int number;
char *string;
}
}
Then you can encode your type with 0 for number and 1 for string. Using an enum instead of integer for the type would be a better way of course.
It's because you're trying to store a value into a slot which is expecting a pointer. Try the following (error checking omitted for brevity)
int* pIntTemp = malloc(sizeof(int));
*pIntTemp = 4;
a[0] = pIntTemp;
The bigest issue is getting the C compiler to treat each element of the array differently.
Might I suggest a hybrid approach.
Set aside several pointers, each with their appropriate structure definitions.
When you decide which kind of element you want, use that pointer to malloc and setup, then later use.
Then copy the value of that pointer into the array of pointers.
Later, when you want to use that element, copy the array element into it's aproprate pointer to make the compiler happy.
Please keep in mind, this is only an example, it has some short commings like difficulty of sorting or inserting a node in the middle, but...
For example:
struct this_type {
char mod_kind[20];
int this_int;
};
struct that_type {
char mod_kind[20];
char that_string[20];
};
void *list_o_pointers[10];
struct this_type *p_this;
struct that_type *p_that;
p_this = malloc(sizeof(struct this_type));
list_o_pointers[0] = p_this;
strcpy(p_this->mod_kind, "this kind"); // or whatever you want to use to differentate different types
p_that = malloc(sizeof(struct that_type));
list_o_pointers[0] = p_that;
strcpy(p_that->mod_kind, "that kind");
// later
p_this = list_o_pointers[0];
p_that = list_o_pointers[0];
if (strstr(p_this->mod_kind, "this kind")) { /* do this stuff */ }
if (strstr(p_that->mod_kind, "that kind")) { /* do that stuff */}
it solves the ulgyness of stuff like having to cast *((double *)(arr[2].data)) = and also helps with readability.
This may break down if you have many different node structures.
It is a bit brute force, but (IMHO) it's a little easier on the brain. The array is a simple array and each node is simple. The nodes have no need for a "next" pointer like a linked list has.
Mark.

Resources