I have a program that reads a "raw" list of in-game entities, and I intend to make an array holding an index number (int) of an indeterminate number of entities, for processing various things. I would like to avoid using too much memory or CPU for keeping such indexes...
A quick and dirty solution I use so far is to declare, in the main processing function (local focus) the array with a size of the maximum game entities, and another integer to keep track of how many have been added to the list.
This isn't satisfactory, as every list holds 3000+ arrays, which isn't that much, but feels like a waste, since I'll possible use the solution for 6-7 lists for varying functions.
I haven't found any C (not C++ or C#) specific solutions to achieve this. I can use pointers, but I am a bit afraid of using them (unless it's the only possible way).
The arrays do not leave the local function scope (they are to be passed to a function, then discarded), in case that changes things.
If pointers are the only solution, how can I keep track of them to avoid leaks?
I can use pointers, but I am a bit afraid of using them.
If you need a dynamic array, you can't escape pointers. Why are you afraid though? They won't bite (as long as you're careful, that is). There's no built-in dynamic array in C, you'll just have to write one yourself. In C++, you can use the built-in std::vector class. C# and just about every other high-level language also have some similar class that manages dynamic arrays for you.
If you do plan to write your own, here's something to get you started: most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array. As you can see in the example below, it's not very difficult at all: (I've omitted safety checks for brevity)
typedef struct {
int *array;
size_t used;
size_t size;
} Array;
void initArray(Array *a, size_t initialSize) {
a->array = malloc(initialSize * sizeof(int));
a->used = 0;
a->size = initialSize;
}
void insertArray(Array *a, int element) {
// a->used is the number of used entries, because a->array[a->used++] updates a->used only *after* the array has been accessed.
// Therefore a->used can go up to a->size
if (a->used == a->size) {
a->size *= 2;
a->array = realloc(a->array, a->size * sizeof(int));
}
a->array[a->used++] = element;
}
void freeArray(Array *a) {
free(a->array);
a->array = NULL;
a->used = a->size = 0;
}
Using it is just as simple:
Array a;
int i;
initArray(&a, 5); // initially 5 elements
for (i = 0; i < 100; i++)
insertArray(&a, i); // automatically resizes as necessary
printf("%d\n", a.array[9]); // print 10th element
printf("%d\n", a.used); // print number of elements
freeArray(&a);
One simple solution involves mmap. This is great if you can tolerate a POSIX solution. Just map a whole page and guard against overflows, since realloc would fail for such values anyway. Modern OSes won't commit to the whole lot until you use it, and you can truncate files if you want.
Alternatively, there's realloc. As with everything that seems scarier at first than it was later, the best way to get over the initial fear is to immerse yourself into the discomfort of the unknown! It is at times like that which we learn the most, after all.
Unfortunately, there are limitations. While you're still learning to use a function, you shouldn't assume the role of a teacher, for example. I often read answers from those who seemingly don't know how to use realloc (i.e. the currently accepted answer!) telling others how to use it incorrectly, occasionally under the guise that they've omitted error handling, even though this is a common pitfall which needs mention. Here's an answer explaining how to use realloc correctly. Take note that the answer is storing the return value into a different variable in order to perform error checking.
Every time you call a function, and every time you use an array, you are using a pointer. The conversions are occurring implicitly, which if anything should be even scarier, as it's the things we don't see which often cause the most problems. For example, memory leaks...
Array operators are pointer operators. array[x] is really a shortcut for *(array + x), which can be broken down into: * and (array + x). It's most likely that the * is what confuses you. We can further eliminate the addition from the problem by assuming x to be 0, thus, array[0] becomes *array because adding 0 won't change the value...
... and thus we can see that *array is equivalent to array[0]. You can use one where you want to use the other, and vice versa. Array operators are pointer operators.
malloc, realloc and friends don't invent the concept of a pointer which you've been using all along; they merely use this to implement some other feature, which is a different form of storage duration, most suitable when you desire drastic, dynamic changes in size.
It is a shame that the currently accepted answer also goes against the grain of some other very well-founded advice on StackOverflow, and at the same time, misses an opportunity to introduce a little-known feature which shines for exactly this usecase: flexible array members! That's actually a pretty broken answer... :(
When you define your struct, declare your array at the end of the structure, without any upper bound. For example:
struct int_list {
size_t size;
int value[];
};
This will allow you to unite your array of int into the same allocation as your count, and having them bound like this can be very handy!
sizeof (struct int_list) will act as though value has a size of 0, so it'll tell you the size of the structure with an empty list. You still need to add to the size passed to realloc to specify the size of your list.
Another handy tip is to remember that realloc(NULL, x) is equivalent to malloc(x), and we can use this to simplify our code. For example:
int push_back(struct int_list **fubar, int value) {
size_t x = *fubar ? fubar[0]->size : 0
, y = x + 1;
if ((x & y) == 0) {
void *temp = realloc(*fubar, sizeof **fubar
+ (x + y) * sizeof fubar[0]->value[0]);
if (!temp) { return 1; }
*fubar = temp; // or, if you like, `fubar[0] = temp;`
}
fubar[0]->value[x] = value;
fubar[0]->size = y;
return 0;
}
struct int_list *array = NULL;
The reason I chose to use struct int_list ** as the first argument may not seem immediately obvious, but if you think about the second argument, any changes made to value from within push_back would not be visible to the function we're calling from, right? The same goes for the first argument, and we need to be able to modify our array, not just here but possibly also in any other function/s we pass it to...
array starts off pointing at nothing; it is an empty list. Initialising it is the same as adding to it. For example:
struct int_list *array = NULL;
if (!push_back(&array, 42)) {
// success!
}
P.S. Remember to free(array); when you're done with it!
There are a couple of options I can think of.
Linked List. You can use a linked list to make a dynamically growing array like thing. But you won't be able to do array[100] without having to walk through 1-99 first. And it might not be that handy for you to use either.
Large array. Simply create an array with more than enough space for everything
Resizing array. Recreate the array once you know the size and/or create a new array every time you run out of space with some margin and copy all the data to the new array.
Linked List Array combination. Simply use an array with a fixed size and once you run out of space, create a new array and link to that (it would be wise to keep track of the array and the link to the next array in a struct).
It is hard to say what option would be best in your situation. Simply creating a large array is ofcourse one of the easiest solutions and shouldn't give you much problems unless it's really large.
Building on Matteo Furlans design, when he said "most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array". The difference in the "work in progress" below is that it doesn't double in size, it aims at using only what is required. I have also omitted safety checks for simplicity...Also building on brimboriums idea, I have tried to add a delete function to the code...
The storage.h file looks like this...
#ifndef STORAGE_H
#define STORAGE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
int *array;
size_t size;
} Array;
void Array_Init(Array *array);
void Array_Add(Array *array, int item);
void Array_Delete(Array *array, int index);
void Array_Free(Array *array);
#ifdef __cplusplus
}
#endif
#endif /* STORAGE_H */
The storage.c file looks like this...
#include <stdio.h>
#include <stdlib.h>
#include "storage.h"
/* Initialise an empty array */
void Array_Init(Array *array)
{
int *int_pointer;
int_pointer = (int *)malloc(sizeof(int));
if (int_pointer == NULL)
{
printf("Unable to allocate memory, exiting.\n");
free(int_pointer);
exit(0);
}
else
{
array->array = int_pointer;
array->size = 0;
}
}
/* Dynamically add to end of an array */
void Array_Add(Array *array, int item)
{
int *int_pointer;
array->size += 1;
int_pointer = (int *)realloc(array->array, array->size * sizeof(int));
if (int_pointer == NULL)
{
printf("Unable to reallocate memory, exiting.\n");
free(int_pointer);
exit(0);
}
else
{
array->array = int_pointer;
array->array[array->size-1] = item;
}
}
/* Delete from a dynamic array */
void Array_Delete(Array *array, int index)
{
int i;
Array temp;
int *int_pointer;
Array_Init(&temp);
for(i=index; i<array->size; i++)
{
array->array[i] = array->array[i + 1];
}
array->size -= 1;
for (i = 0; i < array->size; i++)
{
Array_Add(&temp, array->array[i]);
}
int_pointer = (int *)realloc(temp.array, temp.size * sizeof(int));
if (int_pointer == NULL)
{
printf("Unable to reallocate memory, exiting.\n");
free(int_pointer);
exit(0);
}
else
{
array->array = int_pointer;
}
}
/* Free an array */
void Array_Free(Array *array)
{
free(array->array);
array->array = NULL;
array->size = 0;
}
The main.c looks like this...
#include <stdio.h>
#include <stdlib.h>
#include "storage.h"
int main(int argc, char** argv)
{
Array pointers;
int i;
Array_Init(&pointers);
for (i = 0; i < 60; i++)
{
Array_Add(&pointers, i);
}
Array_Delete(&pointers, 3);
Array_Delete(&pointers, 6);
Array_Delete(&pointers, 30);
for (i = 0; i < pointers.size; i++)
{
printf("Value: %d Size:%d \n", pointers.array[i], pointers.size);
}
Array_Free(&pointers);
return (EXIT_SUCCESS);
}
Look forward to the constructive criticism to follow...
When you're saying
make an array holding an index number (int) of an indeterminate number of entities
you're basically saying you're using "pointers", but one which is a array-wide local pointer instead of memory-wide pointer. Since you're conceptually already using "pointers" (i.e. id numbers that refers to an element in an array), why don't you just use regular pointers (i.e. id numbers that refers to an element in the biggest array: the whole memory).
Instead of your objects storing a resource id numbers, you can make them store a pointer instead. Basically the same thing, but much more efficient since we avoid turning "array + index" into a "pointer".
Pointers are not scary if you think of them as array index for the whole memory (which is what they actually are)
To create an array of unlimited items of any sort of type:
typedef struct STRUCT_SS_VECTOR {
size_t size;
void** items;
} ss_vector;
ss_vector* ss_init_vector(size_t item_size) {
ss_vector* vector;
vector = malloc(sizeof(ss_vector));
vector->size = 0;
vector->items = calloc(0, item_size);
return vector;
}
void ss_vector_append(ss_vector* vec, void* item) {
vec->size++;
vec->items = realloc(vec->items, vec->size * sizeof(item));
vec->items[vec->size - 1] = item;
};
void ss_vector_free(ss_vector* vec) {
for (int i = 0; i < vec->size; i++)
free(vec->items[i]);
free(vec->items);
free(vec);
}
and how to use it:
// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT {
int id;
} apple;
apple* init_apple(int id) {
apple* a;
a = malloc(sizeof(apple));
a-> id = id;
return a;
};
int main(int argc, char* argv[]) {
ss_vector* vector = ss_init_vector(sizeof(apple));
// inserting some items
for (int i = 0; i < 10; i++)
ss_vector_append(vector, init_apple(i));
// dont forget to free it
ss_vector_free(vector);
return 0;
}
This vector/array can hold any type of item and it is completely dynamic in size.
Well, I guess if you need to remove an element you will make a copy of the array despising the element to be excluded.
// inserting some items
void* element_2_remove = getElement2BRemove();
for (int i = 0; i < vector->size; i++){
if(vector[i]!=element_2_remove) copy2TempVector(vector[i]);
}
free(vector->items);
free(vector);
fillFromTempVector(vector);
//
Assume that getElement2BRemove(), copy2TempVector( void* ...) and fillFromTempVector(...) are auxiliary methods to handle the temp vector.
These posts apparently are in the wrong order! This is #1 in a series of 3 posts. Sorry.
In attempting to use Lie Ryan's code, I had problems retrieving stored information. The vector's elements are not stored contiguously,as you can see by "cheating" a bit and storing the pointer to each element's address (which of course defeats the purpose of the dynamic array concept) and examining them.
With a bit of tinkering, via:
ss_vector* vector; // pull this out to be a global vector
// Then add the following to attempt to recover stored values.
int return_id_value(int i,apple* aa) // given ptr to component,return data item
{ printf("showing apple[%i].id = %i and other_id=%i\n",i,aa->id,aa->other_id);
return(aa->id);
}
int Test(void) // Used to be "main" in the example
{ apple* aa[10]; // stored array element addresses
vector = ss_init_vector(sizeof(apple));
// inserting some items
for (int i = 0; i < 10; i++)
{ aa[i]=init_apple(i);
printf("apple id=%i and other_id=%i\n",aa[i]->id,aa[i]->other_id);
ss_vector_append(vector, aa[i]);
}
// report the number of components
printf("nmbr of components in vector = %i\n",(int)vector->size);
printf(".*.*array access.*.component[5] = %i\n",return_id_value(5,aa[5]));
printf("components of size %i\n",(int)sizeof(apple));
printf("\n....pointer initial access...component[0] = %i\n",return_id_value(0,(apple *)&vector[0]));
//.............etc..., followed by
for (int i = 0; i < 10; i++)
{ printf("apple[%i].id = %i at address %i, delta=%i\n",i, return_id_value(i,aa[i]) ,(int)aa[i],(int)(aa[i]-aa[i+1]));
}
// don't forget to free it
ss_vector_free(vector);
return 0;
}
It's possible to access each array element without problems, as long as you know its address, so I guess I'll try adding a "next" element and use this as a linked list. Surely there are better options, though. Please advise.
These posts apparently are in the wrong order! This is #3 in a series of 3 posts. Sorry.
I've "taken a few MORE liberties" with Lie Ryan's code. The linked list admittedly was time-consuming to access individual
elements due to search overhead, i.e. walking down the list until you find the right element. I have now cured this by
maintaining an address vector containing subscripts 0 through whatever paired with memory addresses. This works
because the address vector is allocated all-at-once, thus contiguous in memory. Since the linked-list is no longer required,
I've ripped out its associated code and structure.
This approach is not quite as efficient as a plain-and-simple static array would be, but at least you don't have to "walk the list"
searching for the proper item. You can now access the elements by using a subscript. To enable this, I have had
to add code to handle cases where elements are removed and the "actual" subscripts wouldn't be reflected in the
pointer vector's subscripts. This may or may not be important to users. For me, it IS important, so
I've made re-numbering of subscripts optional. If renumbering is not used, program flow goes to a dummy
"missing" element which returns an error code, which users can choose to ignore or to act on as required.
From here, I'd advise users to code the "elements" portion to fit their needs and make sure that it runs correctly. If your
added elements are arrays, carefully code subroutines to access them, seeing as how there's extra array structure
that wasn't needed with static arrays. Enjoy!
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// Code from https://stackoverflow.com/questions/3536153/c-dynamically-growing-array
// For pointer-to-pointer info see:
// https://stackoverflow.com/questions/897366/how-do-pointer-to-pointers-work-in-c-and-when-might-you-use-them
typedef struct STRUCT_SS_VECTOR
{ size_t size; // # of vector elements
void** items; // makes up one vector element's component contents
int subscript; // this element's subscript nmbr, 0 thru whatever
// struct STRUCT_SS_VECTOR* this_element; // linked list via this ptr
// struct STRUCT_SS_VECTOR* next_element; // and next ptr
} ss_vector;
ss_vector* vector; // ptr to vector of components
ss_vector* missing_element(int subscript) // intercepts missing elements
{ printf("missing element at subscript %i\n",subscript);
return NULL;
}
typedef struct TRACKER_VECTOR
{ int subscript;
ss_vector* vector_ptr;
} tracker_vector; // up to 20 or so, max suggested
tracker_vector* tracker;
int max_tracker=0; // max allowable # of elements in "tracker_vector"
int tracker_count=0; // current # of elements in "tracker_vector"
int tracker_increment=5; // # of elements to add at each expansion
void bump_tracker_vector(int new_tracker_count)
{ //init or lengthen tracker vector
if(max_tracker==0) // not yet initialized
{ tracker=calloc(tracker_increment, sizeof(tracker_vector));
max_tracker=tracker_increment;
printf("initialized %i-element tracker vector of size %lu at %lu\n",max_tracker,sizeof(tracker_vector),(size_t)tracker);
tracker_count++;
return;
}
else if (max_tracker<=tracker_count) // append to existing tracker vector by writing a new one, copying old one
{ tracker_vector* temp_tracker=calloc(max_tracker+tracker_increment,sizeof(tracker_vector));
for(int i=0;(i<max_tracker);i++){ temp_tracker[i]=tracker[i];} // copy old tracker to new
max_tracker=max_tracker+tracker_increment;
free(tracker);
tracker=temp_tracker;
printf(" re-initialized %i-element tracker vector of size %lu at %lu\n",max_tracker,sizeof(tracker_vector),(size_t)tracker);
tracker_count++;
return;
} // else if
// fall through for most "bumps"
tracker_count++;
return;
} // bump_tracker_vector()
ss_vector* ss_init_vector(size_t item_size) // item_size is size of one array member
{ ss_vector* vector= malloc(sizeof(ss_vector));
vector->size = 0; // initialize count of vector component elements
vector->items = calloc(1, item_size); // allocate & zero out memory for one linked list element
vector->subscript=0;
bump_tracker_vector(0); // init/store the tracker vector
tracker[0].subscript=0;
tracker[0].vector_ptr=vector;
return vector; //->this_element;
} // ss_init_vector()
ss_vector* ss_vector_append( int i) // ptr to this element, element nmbr
{ ss_vector* local_vec_element=0;
local_vec_element= calloc(1,sizeof(ss_vector)); // memory for one component
local_vec_element->subscript=i; //vec_element->size;
local_vec_element->size=i; // increment # of vector components
bump_tracker_vector(i); // increment/store tracker vector
tracker[i].subscript=i;
tracker[i].vector_ptr=local_vec_element; //->this_element;
return local_vec_element;
} // ss_vector_append()
void bubble_sort(void)
{ // bubble sort
struct TRACKER_VECTOR local_tracker;
int i=0;
while(i<tracker_count-1)
{ if(tracker[i].subscript>tracker[i+1].subscript)
{ local_tracker.subscript=tracker[i].subscript; // swap tracker elements
local_tracker.vector_ptr=tracker[i].vector_ptr;
tracker[i].subscript=tracker[i+1].subscript;
tracker[i].vector_ptr=tracker[i+1].vector_ptr;
tracker[i+1].subscript=local_tracker.subscript;
tracker[i+1].vector_ptr=local_tracker.vector_ptr;
if(i>0) i--; // step back and go again
}
else
{ if(i<tracker_count-1) i++;
}
} // while()
} // void bubble_sort()
void move_toward_zero(int target_subscript) // toward zero
{ struct TRACKER_VECTOR local_tracker;
// Target to be moved must range from 1 to max_tracker
if((target_subscript<1)||(target_subscript>tracker_count)) return; // outside range
// swap target_subscript ptr and target_subscript-1 ptr
local_tracker.vector_ptr=tracker[target_subscript].vector_ptr;
tracker[target_subscript].vector_ptr=tracker[target_subscript-1].vector_ptr;
tracker[target_subscript-1].vector_ptr=local_tracker.vector_ptr;
}
void renumber_all_subscripts(gboolean arbitrary)
{ // assumes tracker_count has been fixed and tracker[tracker_count+1]has been zeroed out
if(arbitrary) // arbitrary renumber, ignoring "true" subscripts
{ for(int i=0;i<tracker_count;i++)
{ tracker[i].subscript=i;}
}
else // use "true" subscripts, holes and all
{ for(int i=0;i<tracker_count;i++)
{ if ((size_t)tracker[i].vector_ptr!=0) // renumbering "true" subscript tracker & vector_element
{ tracker[i].subscript=tracker[i].vector_ptr->subscript;}
else // renumbering "true" subscript tracker & NULL vector_element
{ tracker[i].subscript=-1;}
} // for()
bubble_sort();
} // if(arbitrary) ELSE
} // renumber_all_subscripts()
void collapse_tracker_higher_elements(int target_subscript)
{ // Fix tracker vector by collapsing higher subscripts toward 0.
// Assumes last tracker element entry is discarded.
int j;
for(j=target_subscript;(j<tracker_count-1);j++)
{ tracker[j].subscript=tracker[j+1].subscript;
tracker[j].vector_ptr=tracker[j+1].vector_ptr;
}
// Discard last tracker element and adjust count
tracker_count--;
tracker[tracker_count].subscript=0;
tracker[tracker_count].vector_ptr=(size_t)0;
} // void collapse_tracker_higher_elements()
void ss_vector_free_one_element(int target_subscript, gboolean Keep_subscripts)
{ // Free requested element contents.
// Adjust subscripts if desired; otherwise, mark NULL.
// ----special case: vector[0]
if(target_subscript==0) // knock out zeroth element no matter what
{ free(tracker[0].vector_ptr);}
// ----if not zeroth, start looking at other elements
else if(tracker_count<target_subscript-1)
{ printf("vector element not found\n");return;}
// Requested subscript okay. Freeit.
else
{ free(tracker[target_subscript].vector_ptr);} // free element ptr
// done with removal.
if(Keep_subscripts) // adjust subscripts if required.
{ tracker[target_subscript].vector_ptr=missing_element(target_subscript);} // point to "0" vector
else // NOT keeping subscripts intact, i.e. collapsing/renumbering all subscripts toward zero
{ collapse_tracker_higher_elements(target_subscript);
renumber_all_subscripts(TRUE); // gboolean arbitrary means as-is, FALSE means by "true" subscripts
} // if (target_subscript==0) else
// show the new list
// for(int i=0;i<tracker_count;i++){printf(" remaining element[%i] at %lu\n",tracker[i].subscript,(size_t)tracker[i].vector_ptr);}
} // void ss_vector_free_one_element()
void ss_vector_free_all_elements(void)
{ // Start at "tracker[0]". Walk the entire list, free each element's contents,
// then free that element, then move to the next one.
// Then free the "tracker" vector.
for(int i=tracker_count;i>=0;i--)
{ // Modify your code to free vector element "items" here
if(tracker[i].subscript>=0) free(tracker[i].vector_ptr);
}
free(tracker);
tracker_count=0;
} // void ss_vector_free_all_elements()
// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT
{ int id; // one of the data in the component
int other_id; // etc
struct APPLE_STRUCT* next_element;
} apple; // description of component
apple* init_apple(int id) // make a single component
{ apple* a; // ptr to component
a = malloc(sizeof(apple)); // memory for one component
a->id = id; // populate with data
a->other_id=id+10;
a->next_element=NULL;
// don't mess with aa->last_rec here
return a; // return pointer to component
}
int return_id_value(int i,apple* aa) // given ptr to component, return single data item
{ printf("was inserted as apple[%i].id = %i ",i,aa->id);
return(aa->id);
}
ss_vector* return_address_given_subscript(int i)
{ return tracker[i].vector_ptr;}
int Test(void) // was "main" in the example
{ int i;
ss_vector* local_vector;
local_vector=ss_init_vector(sizeof(apple)); // element "0"
for (i = 1; i < 10; i++) // inserting items "1" thru whatever
{local_vector=ss_vector_append(i);} // finished ss_vector_append()
// list all tracker vector entries
for(i=0;(i<tracker_count);i++) {printf("tracker element [%i] has address %lu\n",tracker[i].subscript, (size_t)tracker[i].vector_ptr);}
// ---test search function
printf("\n NEXT, test search for address given subscript\n");
local_vector=return_address_given_subscript(5);
printf("finished return_address_given_subscript(5) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(0);
printf("finished return_address_given_subscript(0) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(9);
printf("finished return_address_given_subscript(9) with vector at %lu\n",(size_t)local_vector);
// ---test single-element removal
printf("\nNEXT, test single element removal\n");
ss_vector_free_one_element(5,TRUE); // keep subscripts; install dummy error element
printf("finished ss_vector_free_one_element(5)\n");
ss_vector_free_one_element(3,FALSE);
printf("finished ss_vector_free_one_element(3)\n");
ss_vector_free_one_element(0,FALSE);
// ---test moving elements
printf("\n Test moving a few elements up\n");
move_toward_zero(5);
move_toward_zero(4);
move_toward_zero(3);
// show the new list
printf("New list:\n");
for(int i=0;i<tracker_count;i++){printf(" %i:element[%i] at %lu\n",i,tracker[i].subscript,(size_t)tracker[i].vector_ptr);}
// ---plant some bogus subscripts for the next subscript test
tracker[3].vector_ptr->subscript=7;
tracker[3].subscript=5;
tracker[7].vector_ptr->subscript=17;
tracker[3].subscript=55;
printf("\n RENUMBER to use \"actual\" subscripts\n");
renumber_all_subscripts(FALSE);
printf("Sorted list:\n");
for(int i=0;i<tracker_count;i++)
{ if ((size_t)tracker[i].vector_ptr!=0)
{ printf(" %i:element[%i] or [%i]at %lu\n",i,tracker[i].subscript,tracker[i].vector_ptr->subscript,(size_t)tracker[i].vector_ptr);
}
else
{ printf(" %i:element[%i] at 0\n",i,tracker[i].subscript);
}
}
printf("\nBubble sort to get TRUE order back\n");
bubble_sort();
printf("Sorted list:\n");
for(int i=0;i<tracker_count;i++)
{ if ((size_t)tracker[i].vector_ptr!=0)
{printf(" %i:element[%i] or [%i]at %lu\n",i,tracker[i].subscript,tracker[i].vector_ptr->subscript,(size_t)tracker[i].vector_ptr);}
else {printf(" %i:element[%i] at 0\n",i,tracker[i].subscript);}
}
// END TEST SECTION
// don't forget to free everything
ss_vector_free_all_elements();
return 0;
}
int main(int argc, char *argv[])
{ char cmd[5],main_buffer[50]; // Intentionally big for "other" I/O purposes
cmd[0]=32; // blank = ASCII 32
// while(cmd!="R"&&cmd!="W" &&cmd!="E" &&cmd!=" ")
while(cmd[0]!=82&&cmd[0]!=87&&cmd[0]!=69)//&&cmd[0]!=32)
{ memset(cmd, '\0', sizeof(cmd));
memset(main_buffer, '\0', sizeof(main_buffer));
// default back to the cmd loop
cmd[0]=32; // blank = ASCII 32
printf("REad, TEst, WRITe, EDIt, or EXIt? ");
fscanf(stdin, "%s", main_buffer);
strncpy(cmd,main_buffer,4);
for(int i=0;i<4;i++)cmd[i]=toupper(cmd[i]);
cmd[4]='\0';
printf("%s received\n ",cmd);
// process top level commands
if(cmd[0]==82) {printf("READ accepted\n");} //Read
else if(cmd[0]==87) {printf("WRITe accepted\n");} // Write
else if(cmd[0]==84)
{ printf("TESt accepted\n");// TESt
Test();
}
else if(cmd[0]==69) // "E"
{ if(cmd[1]==68) {printf("EDITing\n");} // eDit
else if(cmd[1]==88) {printf("EXITing\n");exit(0);} // eXit
else printf(" unknown E command %c%c\n",cmd[0],cmd[1]);
}
else printf(" unknown command\n");
cmd[0]=32; // blank = ASCII 32
} // while()
// default back to the cmd loop
} // main()
These posts may be in the wrong order! This is #2 in a series of 3 posts. Sorry.
I've "taken a few liberties" with Lie Ryan's code, implementing a linked list so individual elements of his vector can be accessed via a linked list. This allows access, but admittedly it is time-consuming to access individual elements due to search overhead, i.e. walking down the list until you find the right element. I'll cure this by maintaining an address vector containing subscripts 0 through whatever paired with memory addresses. This is still not as efficient as a plain-and-simple array would be, but at least you don't have to "walk the list" searching for the proper item.
// Based on code from https://stackoverflow.com/questions/3536153/c-dynamically-growing-array
typedef struct STRUCT_SS_VECTOR
{ size_t size; // # of vector elements
void** items; // makes up one vector element's component contents
int subscript; // this element's subscript nmbr, 0 thru whatever
struct STRUCT_SS_VECTOR* this_element; // linked list via this ptr
struct STRUCT_SS_VECTOR* next_element; // and next ptr
} ss_vector;
ss_vector* vector; // ptr to vector of components
ss_vector* ss_init_vector(size_t item_size) // item_size is size of one array member
{ vector= malloc(sizeof(ss_vector));
vector->this_element = vector;
vector->size = 0; // initialize count of vector component elements
vector->items = calloc(1, item_size); // allocate & zero out memory for one linked list element
vector->subscript=0;
vector->next_element=NULL;
// If there's an array of element addresses/subscripts, install it now.
return vector->this_element;
}
ss_vector* ss_vector_append(ss_vector* vec_element, int i)
// ^--ptr to this element ^--element nmbr
{ ss_vector* local_vec_element=0;
// If there is already a next element, recurse to end-of-linked-list
if(vec_element->next_element!=(size_t)0)
{ local_vec_element= ss_vector_append(vec_element->next_element,i); // recurse to end of list
return local_vec_element;
}
// vec_element is NULL, so make a new element and add at end of list
local_vec_element= calloc(1,sizeof(ss_vector)); // memory for one component
local_vec_element->this_element=local_vec_element; // save the address
local_vec_element->next_element=0;
vec_element->next_element=local_vec_element->this_element;
local_vec_element->subscript=i; //vec_element->size;
local_vec_element->size=i; // increment # of vector components
// If there's an array of element addresses/subscripts, update it now.
return local_vec_element;
}
void ss_vector_free_one_element(int i,gboolean Update_subscripts)
{ // Walk the entire linked list to the specified element, patch up
// the element ptrs before/next, then free its contents, then free it.
// Walk the rest of the list, updating subscripts, if requested.
// If there's an array of element addresses/subscripts, shift it along the way.
ss_vector* vec_element;
struct STRUCT_SS_VECTOR* this_one;
struct STRUCT_SS_VECTOR* next_one;
vec_element=vector;
while((vec_element->this_element->subscript!=i)&&(vec_element->next_element!=(size_t) 0)) // skip
{ this_one=vec_element->this_element; // trailing ptr
next_one=vec_element->next_element; // will become current ptr
vec_element=next_one;
}
// now at either target element or end-of-list
if(vec_element->this_element->subscript!=i)
{ printf("vector element not found\n");return;}
// free this one
this_one->next_element=next_one->next_element;// previous element points to element after current one
printf("freeing element[%i] at %lu",next_one->subscript,(size_t)next_one);
printf(" between %lu and %lu\n",(size_t)this_one,(size_t)next_one->next_element);
vec_element=next_one->next_element;
free(next_one); // free the current element
// renumber if requested
if(Update_subscripts)
{ i=0;
vec_element=vector;
while(vec_element!=(size_t) 0)
{ vec_element->subscript=i;
i++;
vec_element=vec_element->next_element;
}
}
// If there's an array of element addresses/subscripts, update it now.
/* // Check: temporarily show the new list
vec_element=vector;
while(vec_element!=(size_t) 0)
{ printf(" remaining element[%i] at %lu\n",vec_element->subscript,(size_t)vec_element->this_element);
vec_element=vec_element->next_element;
} */
return;
} // void ss_vector_free_one_element()
void ss_vector_insert_one_element(ss_vector* vec_element,int place)
{ // Walk the entire linked list to specified element "place", patch up
// the element ptrs before/next, then calloc an element and store its contents at "place".
// Increment all the following subscripts.
// If there's an array of element addresses/subscripts, make a bigger one,
// copy the old one, then shift appropriate members.
// ***Not yet implemented***
} // void ss_vector_insert_one_element()
void ss_vector_free_all_elements(void)
{ // Start at "vector".Walk the entire linked list, free each element's contents,
// free that element, then move to the next one.
// If there's an array of element addresses/subscripts, free it.
ss_vector* vec_element;
struct STRUCT_SS_VECTOR* next_one;
vec_element=vector;
while(vec_element->next_element!=(size_t) 0)
{ next_one=vec_element->next_element;
// free(vec_element->items) // don't forget to free these
free(vec_element->this_element);
vec_element=next_one;
next_one=vec_element->this_element;
}
// get rid of the last one.
// free(vec_element->items)
free(vec_element);
vector=NULL;
// If there's an array of element addresses/subscripts, free it now.
printf("\nall vector elements & contents freed\n");
} // void ss_vector_free_all_elements()
// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT
{ int id; // one of the data in the component
int other_id; // etc
struct APPLE_STRUCT* next_element;
} apple; // description of component
apple* init_apple(int id) // make a single component
{ apple* a; // ptr to component
a = malloc(sizeof(apple)); // memory for one component
a->id = id; // populate with data
a->other_id=id+10;
a->next_element=NULL;
// don't mess with aa->last_rec here
return a; // return pointer to component
};
int return_id_value(int i,apple* aa) // given ptr to component, return single data item
{ printf("was inserted as apple[%i].id = %i ",i,aa->id);
return(aa->id);
}
ss_vector* return_address_given_subscript(ss_vector* vec_element,int i)
// always make the first call to this subroutine with global vbl "vector"
{ ss_vector* local_vec_element=0;
// If there is a next element, recurse toward end-of-linked-list
if(vec_element->next_element!=(size_t)0)
{ if((vec_element->this_element->subscript==i))
{ return vec_element->this_element;}
local_vec_element= return_address_given_subscript(vec_element->next_element,i); // recurse to end of list
return local_vec_element;
}
else
{ if((vec_element->this_element->subscript==i)) // last element
{ return vec_element->this_element;}
// otherwise, none match
printf("reached end of list without match\n");
return (size_t) 0;
}
} // return_address_given_subscript()
int Test(void) // was "main" in the original example
{ ss_vector* local_vector;
local_vector=ss_init_vector(sizeof(apple)); // element "0"
for (int i = 1; i < 10; i++) // inserting items "1" thru whatever
{ local_vector=ss_vector_append(vector,i);}
// test search function
printf("\n NEXT, test search for address given subscript\n");
local_vector=return_address_given_subscript(vector,5);
printf("finished return_address_given_subscript(5) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(vector,0);
printf("finished return_address_given_subscript(0) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(vector,9);
printf("finished return_address_given_subscript(9) with vector at %lu\n",(size_t)local_vector);
// test single-element removal
printf("\nNEXT, test single element removal\n");
ss_vector_free_one_element(5,FALSE); // without renumbering subscripts
ss_vector_free_one_element(3,TRUE);// WITH renumbering subscripts
// ---end of program---
// don't forget to free everything
ss_vector_free_all_elements();
return 0;
}
Related
I'm facing a problem that seems complicated to me so i'll be very grateful to who can help me.
I'm sort of trying to replicate the array of string behavior only with structures
so instead of having for example
char **mys={"hello","this is a long message"};
I'm trying to have an array with structures (each structure containing an array of variable length). I'm not quite sure if I exposed my problem very well so i hope the code is going to indicate what i'm trying to do:
my two structures :
typedef struct
{
int *iTab;
int iTail;
}strDynArr;
typedef struct
{
strDynArr **iTab;
int iTailStruct;
int iTail;
}strDynDynArr;
All the functions related to those two structures :
void initArray(strDynArr *ar)
{
ar->iTab=malloc(sizeof(int));
ar->iTail=1;
}
void pushArray(int iElement,strDynArr *ar)
{
ar->iTab[ar->iTail-1]=iElement;
realloc(ar->iTab,(ar->iTail++)*sizeof(int));
}
void freeDynArray(strDynArr *ar)
{
free(*ar->iTab);
}
void initDynDynArray(strDynDynArr *ar)
{
ar->iTab=malloc(sizeof(int));
ar->iTail=1;
ar->iTailStruct=1;
}
//the problem
void pushDynDynArray(strDynArr *daElement,strDynDynArr *ar)
{
//ar->iTab[ar->iTail-1]=daElement;
ar->iTailStruct+=(daElement->iTail+1);
realloc(ar->iTab,(ar->iTailStruct)*sizeof(int));
ar->iTab[ar->iTailStruct-(daElement->iTail+1)]=daElement;
//realloc(ar->iTab,(ar->iTail++)*sizeof(int));
}
void freeDynDynDynArray(strDynDynArr *ar)
{
free(*ar->iTab);
}
And the one function where i'm stuck is pushDynDynArray so far i've attempted two things either just using the pointer that points on à structure strDynArr but i don't know how space is managed at all so i tryed to allocate the size of the all the structures contained in the array of the structure strDynDynArr.
So what i would like to know is how to allocate space for my array (iTab) for a structure of type strDynDynArr.
In other words what is the way for me to store several structures containing for example :
//content of structure 1
int *iTab={2,3,4};
int iTail=3;
//content of structure 2
int *iTab={5,8,9,10,54,8,2};
int iTail=7;
All help is welcome and thanks à lot for it !
For simplicity, lets call each array of "strings" a table (of arrays), and each "string" an array of items. In your case, the item type seems to be int:
typedef int item;
typedef struct {
size_t size; /* Number of items allocated for */
size_t used; /* Number of items in item[] */
item *item;
} array;
typedef struct {
size_t size; /* Number of array (pointers) allocated for */
size_t used; /* Number of arrays in array[] */
array *array;
} table;
It is common to define initializer macros and functions for such types:
#define ARRAY_INIT { 0, 0, NULL }
#define TABLE_INIT { 0, 0, NULL }
static inline void array_init(array *ref)
{
if (!ref) {
fprintf(stderr, "array_init(): NULL array!\n");
exit(EXIT_FAILURE);
}
ref->size = 0;
ref->used = 0;
ref->item = NULL;
}
static inline void table_init(table *ref)
{
if (!ref) {
fprintf(stderr, "table_init(): NULL table!\n");
exit(EXIT_FAILURE);
}
ref->size = 0;
ref->used = 0;
ref->array = NULL;
}
Also, free functions are obviously useful:
static inline void array_free(array *ref)
{
if (!ref) {
fprintf(stderr, "array_free(): NULL array!\n");
exit(EXIT_FAILURE);
}
free(ref->item); /* Note: free(NULL) is safe; it does nothing. */
ref->size = 0;
ref->used = 0;
ref->item = NULL;
}
static inline void table_free(table *ref)
{
size_t i;
if (!ref) {
fprintf(stderr, "table_free(): NULL table!\n");
exit(EXIT_FAILURE);
}
i = ref->size;
while (i-->0)
array_free(ref->array + i); /* array_free(&(ref->array[i])); */
free(ref->array);
ref->size = 0;
ref->used = 0;
ref->array = NULL;
}
When freeing a table, we want to free all (possible) arrays individually, then the memory used by the pointers. Note that one could assume that only used arrays are in use; however, I wanted the above to be thorough, and free all size arrays allocated for.
Both the init and free functions above take a pointer to an array or table. They should never be NULL. (That is, the item or array member in the structure may well be NULL; it's just that you should never call e.g. array_init(NULL); or table_free(NULL).)
Let us implement a function to push and pop individual ints from a solitary array (that may or may not be part of a table):
void array_push(array *ref, const item value)
{
if (!ref) {
fprintf(stderr, "array_push(): NULL array!\n");
exit(EXIT_FAILURE);
}
/* Need to grow the array? */
if (ref->used >= ref->size) {
size_t size; /* Minimum is ref->used + 1 */
void *temp;
/* Dynamic growth policy. Pulled from a hat. */
if (ref->used < 64)
size = 64; /* Minimum 64 elements */
else
if (ref->used < 1048576)
size = (3*ref->used) / 2; /* Grow by 50% up to 1048576 */
else
size = (ref->used | 1048575) + 1048577; /* Grow to next multiple of 1048576 */
temp = realloc(ref->item, size * sizeof ref->item[0]);
if (!temp)
return -1; /* Out of memory */
ref->item = temp;
ref->size = size;
}
/* Append value to array. */
ref->item[ref->used++] = value;
}
and a corresponding pop operation:
item array_pop(array *ref)
{
if (!ref) {
fprintf(stderr, "array_pop(): NULL array!\n");
exit(EXIT_FAILURE);
}
if (ref->used < 1) {
fprintf(stderr, "array_pop(): Array is already empty; nothing to pop.\n");
exit(EXIT_FAILURE);
}
/* Since ref->used is the *number* of items in the array,
we want to decrement it first, to get the last item in array. */
return ref->item[--ref->used];
}
In your program, you can use an array for example thus:
array scores = ARRAY_INIT;
array_push(&scores, 5);
array_push(&scores, 2);
printf("%d\n", array_pop(&scores)); /* Will print 2 */
printf("%d\n", array_pop(&scores)); /* Will print 5 */
array_free(&scores);
The line array scores = ARRAY_INIT; both declares and initializes the array (to an empty array). You could also equivalently use array scores; array_init(&scores);.
The resize or growth policy in array_push() is roughly along the lines I'd personally recommend, although the actual numerical values are pulled from a hat, and you may wish to adjust them. The idea is that there is a minimum number of items allocated for (say, 64). For bigger arrays, we increase the size fractionally, so that when the array is large, the size increase is also large. Many people use 100% increase in size (doubling the size, i.e. size = 2 * ref->used;), but I like 50% increase better (multiplying the size by one and one half, size = (3 * ref->used) / 2;). For huge arrays, we don't want to waste potentially lots of memory, so we allocate in fixed-size (but huge) chunks instead. (There is no need or real benefit to align the huge size to some multiple like I did; I just like it that way. And the more complicated code ensures you need to understand it and edit it, rather than submitting it raw as yours; otherwise, you'll instructor will catch you cheating.)
Pushing a single value to the last array in the table is now simple to implement:
void table_push_value(table *ref, const item value)
{
size_t i;
if (!ref) {
fprintf(stderr, "table_push_value(): NULL table!\n");
exit(EXIT_FAILURE);
}
/* Ensure there is at least one array. */
if (!ref->size < 1) {
/* Empty table: ref->size = 0, and ref->used must be 0 too. */
const size_t size = 1; /* Allocate for exactly one array. */
void *temp;
temp = realloc(ref->array, size * sizeof ref->array[0]);
if (!temp) {
fprintf(stderr, "table_push_value(): Out of memory.\n");
exit(EXIT_FAILURE);
}
ref->size = size;
ref->array = temp;
for (i = 0; i < size; i++)
array_init(ref->array + i); /* array_init(&(ref->array[i])); */
}
if (ref->used > 0)
i = ref->used - 1; /* Last array in table */
else
i = 0; /* Table is empty, so use first array */
array_push(ref->array + i, value); /* array_push(&(ref->array[i])); */
}
This time, you need special logic for an empty table, for both allocating the description for an array, as well as where to push.
Pop is simpler:
item table_pop_value(table *ref)
{
size_t i;
if (!ref) {
fprintf(stderr, "table_pop_value(): NULL table!\n");
exit(EXIT_FAILURE);
}
i = ref->used;
/* Find the last array with items in it, and pop from it. */
while (i-- > 0)
if (ref->array[i].used > 0) {
return array_pop(ref->array + i); /* array_pop(&(ref->array[i])); */
fprintf(stderr, "table_pop_value(): Empty table, no items to pop!\n");
exit(EXIT_FAILURE);
}
To push an entire array to a table (pushing the array of items in it, not making a copy of the items) is pretty simple, but we do need to implement a reallocation/growth policy again:
void table_push_array(table *ref, array *one)
{
if (!ref) {
fprintf(stderr, "table_push_array(): NULL table!\n");
exit(EXIT_FAILURE);
}
if (!one) {
fprintf(stderr, "table_push_array(): NULL array!\n");
exit(EXIT_FAILURE);
}
if (ref->used >= ref->size) {
size_t size, i;
void *temp;
if (ref->used < 1)
size = 1; /* Minimum size is 1 */
else
size = (ref->used | 7) + 9; /* Next multiple of 8 */
temp = realloc(ref->array, size * sizeof ref->array[0]);
if (!temp) {
fprintf(stderr, "table_push_array(): Out of memory.\n");
exit(EXIT_FAILURE);
}
ref->array = temp;
for (i = ref->size; i < size; i++)
array_init(ref->array + i); /* array_init(&(ref->array[i])); */
ref->size = size;
}
ref->array[ref->used] = *one; /* "shallow copy" */
ref->used++;
}
The corresponding pop operation should be pretty obvious by now:
array *table_pop_array(table *ref)
{
array retval = ARRAY_INIT;
if (!ref) {
fprintf(stderr, "table_pop_array(): NULL table!\n");
exit(EXIT_FAILURE);
}
if (ref->used < 1) {
fprintf(stderr, "table_pop_array(): Table is empty, no arrays to pop!\n");
exit(EXIT_FAILURE);
}
/* Decrement the used count, so it refers to the last array in the table. */
ref->used--;
/* Shallow copy the array. */
retval = ref->array[ref->used];
/* Init, but do not free, the array in the table. */
array_init(ref->array + ref->used); /* array_init(&(ref->array[ref->used)); */
/* Return the array. */
return retval;
}
There is a "trick" in table_pop_array() above, that you should understand. While we cannot return pointers to local variables, we can return structures. In the above case, the structure describes the array, and the pointer in it does not refer to a local variable, but to a dynamically allocated array of items. Structure types can be assigned just as normal scalar types (like int or double); it is basically the same as if you assigned each member separately.
Overall, you should notice I have not used a single malloc() call. This is because realloc(NULL, size) is equivalent to malloc(size), and simply initializing unused pointers to NULL makes everything simpler.
When a table is grown (reallocated), we do need to initialize all the new arrays, because of the above realloc() use pattern.
The above approach does not preclude direct access to specific arrays in the table, or specific items in an array. If you intend to implement such functions, two helper functions similar to
void table_need_arrays(table *ref, const size_t size);
void array_need_items(array *ref, const size_t size);
that ensure that the table has room for at least size arrays, and an array has room for at least size items. They are also useful when pushing multiple items or arrays consecutively, as then one can do e.g. table_need_arrays(&mytable, mytable.used + 10); to ensure there is room for additional 10 arrays in the table.
All throughout the functions above, you can see notation name_of_array + index, and a comment with corresponding &(name_of_array[index]). This is because the two notations are equivalent: pointer to the index'th element in name_of_array.
I didn't bother to compile-check the above code, so there might be typos hidden in there. (This too is intentional, because I want you to understand the code, and not just copy it and use it as your own without understanding any of the details.) However, the logic is sound. So, if you find a typo or issue, let me know in a comment, and I shall fix.
Hello i'implementing a smart vector in c, and i'm having problems with the reallocation of the buffer.
this is the struct that contains the array and its infos:
struct _vector
{
item* vec;
size_t elements;
size_t size;
};
item is just a typedef that in this case happens to be int.
I made several function to manage the array, but the one that should resize it, gives me problems.
(Vector is also a typedef for struct _vector* by the way)
This is the function:
void insertVector(const Vector vec,const int pos,const item a)
{
if(vec->elements==vec->size)
{
item* temp=realloc(vec->vec,(vec->size*2)*sizeof(item));
if(temp==NULL)
{
puts("Error: space unavailable");
return;
}
//vec->vec=realloc(vec->vec,(vec->size*2)*sizeof(item));
vec->vec=temp;
vec->size*=2;
}
int size=vec->elements;
if(pos>=0&&pos<=size)
{
for(int i=size;i>pos;i--)
{
vec->vec[i]=vec->vec[i-1];
}
vec->vec[pos]=a;
vec->elements+=1;
printf("size is %lu\nelements are %lu\n",vec->size,vec->elements);
}
}
I just shift the contents to make space for the new element, and it works fine, the problem is when the array is reallocated.
when the number of valid elements is equal to the actual size of the array,
i do a realloc to double the actual size.
As soon as that if activates though the realloc makes the program crash with this error:incorrect checksum for freed object.
The problem is in the if, because it only crashes when the size and elements are equal, if i comment out that section, everything works
I don't know what could it be.
EDIT:
The functions that i used to create and the initialise the instance i'm working with are:
Vector newVector(void)
{
Vector new=malloc(sizeof(*new));
new->vec=NULL;
new->elements=0;
new->size=0;
return new;
}
and
void initVector(const Vector vec,const size_t size)
{
vec->vec=calloc(size,sizeof(item));
vec->elements=size;
vec->size=size*2;
}
Based of your comment
I created a new vector setting to zero every field, then i used this function:
void initVector(const Vector vec,const size_t size)
{
vec->vec=calloc(size,sizeof(item));
vec->elements=size;
vec->size=size*2;
}
I think you are treating the size and the number of elements incorrectly. The
initVector function just allocates memory for the vec->vec array, so
vec->elements should be 0, not size. And vec->size should be size, not
size*2. So the correct function should be
// remove the const, you are modifying the data vec is pointing to
int initVector(Vector vec, size_t size)
{
if(vec == NULL)
return 0;
vec->vec = calloc(size, sizeof *vec->vec);
if(vec->vec == NULL)
return 0;
vec->elements = 0;
vec->size = size;
return 1;
}
Now the insertVector would only allocate new space, when all allocated spaces
are used.
And I suggest you use memmove to copy the memory:
// again, remove the const here
int insertVector(Vector vec, const size_t pos, const item a)
{
if(vec == NULL)
return 0;
if(vec->elements==vec->size)
{
item* temp=realloc(vec->vec,(vec->size*2)*sizeof *temp);
if(temp==NULL)
{
fprintf(stderr, "Error: space unavailable\n");
return 0;
}
vec->vec=temp;
vec->size*=2;
}
// I use vec->elements as upper limit,
// otherwise you could have "holes" in the array and
// you wouldn't realize it.
if(pos < 0 || pos > vec->elements)
{
fprintf(stderr, "invalid position\n");
return 0;
}
memmove(vec->vec + pos + 1, vec->vec + pos, (vec->elements - pos) * sizeof *vec->vec);
vec->vec[pos] = a;
vec->elements += 1;
printf("size is %lu\nelements are %lu\n",vec->size,vec->elements);
return 1;
}
In your initVector function, you set the size incorrectly, to two times what you allocated with calloc. This memory then gets overwritten as you are adding new elements and this is the reason the free fails when you finally invoke realloc. Change initVector to:
void initVector(const Vector vec,const size_t size)
{
vec->vec=calloc(size,sizeof(item));
vec->elements=size;
vec->size=size;
}
I have a program that reads a "raw" list of in-game entities, and I intend to make an array holding an index number (int) of an indeterminate number of entities, for processing various things. I would like to avoid using too much memory or CPU for keeping such indexes...
A quick and dirty solution I use so far is to declare, in the main processing function (local focus) the array with a size of the maximum game entities, and another integer to keep track of how many have been added to the list.
This isn't satisfactory, as every list holds 3000+ arrays, which isn't that much, but feels like a waste, since I'll possible use the solution for 6-7 lists for varying functions.
I haven't found any C (not C++ or C#) specific solutions to achieve this. I can use pointers, but I am a bit afraid of using them (unless it's the only possible way).
The arrays do not leave the local function scope (they are to be passed to a function, then discarded), in case that changes things.
If pointers are the only solution, how can I keep track of them to avoid leaks?
I can use pointers, but I am a bit afraid of using them.
If you need a dynamic array, you can't escape pointers. Why are you afraid though? They won't bite (as long as you're careful, that is). There's no built-in dynamic array in C, you'll just have to write one yourself. In C++, you can use the built-in std::vector class. C# and just about every other high-level language also have some similar class that manages dynamic arrays for you.
If you do plan to write your own, here's something to get you started: most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array. As you can see in the example below, it's not very difficult at all: (I've omitted safety checks for brevity)
typedef struct {
int *array;
size_t used;
size_t size;
} Array;
void initArray(Array *a, size_t initialSize) {
a->array = malloc(initialSize * sizeof(int));
a->used = 0;
a->size = initialSize;
}
void insertArray(Array *a, int element) {
// a->used is the number of used entries, because a->array[a->used++] updates a->used only *after* the array has been accessed.
// Therefore a->used can go up to a->size
if (a->used == a->size) {
a->size *= 2;
a->array = realloc(a->array, a->size * sizeof(int));
}
a->array[a->used++] = element;
}
void freeArray(Array *a) {
free(a->array);
a->array = NULL;
a->used = a->size = 0;
}
Using it is just as simple:
Array a;
int i;
initArray(&a, 5); // initially 5 elements
for (i = 0; i < 100; i++)
insertArray(&a, i); // automatically resizes as necessary
printf("%d\n", a.array[9]); // print 10th element
printf("%d\n", a.used); // print number of elements
freeArray(&a);
One simple solution involves mmap. This is great if you can tolerate a POSIX solution. Just map a whole page and guard against overflows, since realloc would fail for such values anyway. Modern OSes won't commit to the whole lot until you use it, and you can truncate files if you want.
Alternatively, there's realloc. As with everything that seems scarier at first than it was later, the best way to get over the initial fear is to immerse yourself into the discomfort of the unknown! It is at times like that which we learn the most, after all.
Unfortunately, there are limitations. While you're still learning to use a function, you shouldn't assume the role of a teacher, for example. I often read answers from those who seemingly don't know how to use realloc (i.e. the currently accepted answer!) telling others how to use it incorrectly, occasionally under the guise that they've omitted error handling, even though this is a common pitfall which needs mention. Here's an answer explaining how to use realloc correctly. Take note that the answer is storing the return value into a different variable in order to perform error checking.
Every time you call a function, and every time you use an array, you are using a pointer. The conversions are occurring implicitly, which if anything should be even scarier, as it's the things we don't see which often cause the most problems. For example, memory leaks...
Array operators are pointer operators. array[x] is really a shortcut for *(array + x), which can be broken down into: * and (array + x). It's most likely that the * is what confuses you. We can further eliminate the addition from the problem by assuming x to be 0, thus, array[0] becomes *array because adding 0 won't change the value...
... and thus we can see that *array is equivalent to array[0]. You can use one where you want to use the other, and vice versa. Array operators are pointer operators.
malloc, realloc and friends don't invent the concept of a pointer which you've been using all along; they merely use this to implement some other feature, which is a different form of storage duration, most suitable when you desire drastic, dynamic changes in size.
It is a shame that the currently accepted answer also goes against the grain of some other very well-founded advice on StackOverflow, and at the same time, misses an opportunity to introduce a little-known feature which shines for exactly this usecase: flexible array members! That's actually a pretty broken answer... :(
When you define your struct, declare your array at the end of the structure, without any upper bound. For example:
struct int_list {
size_t size;
int value[];
};
This will allow you to unite your array of int into the same allocation as your count, and having them bound like this can be very handy!
sizeof (struct int_list) will act as though value has a size of 0, so it'll tell you the size of the structure with an empty list. You still need to add to the size passed to realloc to specify the size of your list.
Another handy tip is to remember that realloc(NULL, x) is equivalent to malloc(x), and we can use this to simplify our code. For example:
int push_back(struct int_list **fubar, int value) {
size_t x = *fubar ? fubar[0]->size : 0
, y = x + 1;
if ((x & y) == 0) {
void *temp = realloc(*fubar, sizeof **fubar
+ (x + y) * sizeof fubar[0]->value[0]);
if (!temp) { return 1; }
*fubar = temp; // or, if you like, `fubar[0] = temp;`
}
fubar[0]->value[x] = value;
fubar[0]->size = y;
return 0;
}
struct int_list *array = NULL;
The reason I chose to use struct int_list ** as the first argument may not seem immediately obvious, but if you think about the second argument, any changes made to value from within push_back would not be visible to the function we're calling from, right? The same goes for the first argument, and we need to be able to modify our array, not just here but possibly also in any other function/s we pass it to...
array starts off pointing at nothing; it is an empty list. Initialising it is the same as adding to it. For example:
struct int_list *array = NULL;
if (!push_back(&array, 42)) {
// success!
}
P.S. Remember to free(array); when you're done with it!
There are a couple of options I can think of.
Linked List. You can use a linked list to make a dynamically growing array like thing. But you won't be able to do array[100] without having to walk through 1-99 first. And it might not be that handy for you to use either.
Large array. Simply create an array with more than enough space for everything
Resizing array. Recreate the array once you know the size and/or create a new array every time you run out of space with some margin and copy all the data to the new array.
Linked List Array combination. Simply use an array with a fixed size and once you run out of space, create a new array and link to that (it would be wise to keep track of the array and the link to the next array in a struct).
It is hard to say what option would be best in your situation. Simply creating a large array is ofcourse one of the easiest solutions and shouldn't give you much problems unless it's really large.
Building on Matteo Furlans design, when he said "most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array". The difference in the "work in progress" below is that it doesn't double in size, it aims at using only what is required. I have also omitted safety checks for simplicity...Also building on brimboriums idea, I have tried to add a delete function to the code...
The storage.h file looks like this...
#ifndef STORAGE_H
#define STORAGE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
int *array;
size_t size;
} Array;
void Array_Init(Array *array);
void Array_Add(Array *array, int item);
void Array_Delete(Array *array, int index);
void Array_Free(Array *array);
#ifdef __cplusplus
}
#endif
#endif /* STORAGE_H */
The storage.c file looks like this...
#include <stdio.h>
#include <stdlib.h>
#include "storage.h"
/* Initialise an empty array */
void Array_Init(Array *array)
{
int *int_pointer;
int_pointer = (int *)malloc(sizeof(int));
if (int_pointer == NULL)
{
printf("Unable to allocate memory, exiting.\n");
free(int_pointer);
exit(0);
}
else
{
array->array = int_pointer;
array->size = 0;
}
}
/* Dynamically add to end of an array */
void Array_Add(Array *array, int item)
{
int *int_pointer;
array->size += 1;
int_pointer = (int *)realloc(array->array, array->size * sizeof(int));
if (int_pointer == NULL)
{
printf("Unable to reallocate memory, exiting.\n");
free(int_pointer);
exit(0);
}
else
{
array->array = int_pointer;
array->array[array->size-1] = item;
}
}
/* Delete from a dynamic array */
void Array_Delete(Array *array, int index)
{
int i;
Array temp;
int *int_pointer;
Array_Init(&temp);
for(i=index; i<array->size; i++)
{
array->array[i] = array->array[i + 1];
}
array->size -= 1;
for (i = 0; i < array->size; i++)
{
Array_Add(&temp, array->array[i]);
}
int_pointer = (int *)realloc(temp.array, temp.size * sizeof(int));
if (int_pointer == NULL)
{
printf("Unable to reallocate memory, exiting.\n");
free(int_pointer);
exit(0);
}
else
{
array->array = int_pointer;
}
}
/* Free an array */
void Array_Free(Array *array)
{
free(array->array);
array->array = NULL;
array->size = 0;
}
The main.c looks like this...
#include <stdio.h>
#include <stdlib.h>
#include "storage.h"
int main(int argc, char** argv)
{
Array pointers;
int i;
Array_Init(&pointers);
for (i = 0; i < 60; i++)
{
Array_Add(&pointers, i);
}
Array_Delete(&pointers, 3);
Array_Delete(&pointers, 6);
Array_Delete(&pointers, 30);
for (i = 0; i < pointers.size; i++)
{
printf("Value: %d Size:%d \n", pointers.array[i], pointers.size);
}
Array_Free(&pointers);
return (EXIT_SUCCESS);
}
Look forward to the constructive criticism to follow...
When you're saying
make an array holding an index number (int) of an indeterminate number of entities
you're basically saying you're using "pointers", but one which is a array-wide local pointer instead of memory-wide pointer. Since you're conceptually already using "pointers" (i.e. id numbers that refers to an element in an array), why don't you just use regular pointers (i.e. id numbers that refers to an element in the biggest array: the whole memory).
Instead of your objects storing a resource id numbers, you can make them store a pointer instead. Basically the same thing, but much more efficient since we avoid turning "array + index" into a "pointer".
Pointers are not scary if you think of them as array index for the whole memory (which is what they actually are)
To create an array of unlimited items of any sort of type:
typedef struct STRUCT_SS_VECTOR {
size_t size;
void** items;
} ss_vector;
ss_vector* ss_init_vector(size_t item_size) {
ss_vector* vector;
vector = malloc(sizeof(ss_vector));
vector->size = 0;
vector->items = calloc(0, item_size);
return vector;
}
void ss_vector_append(ss_vector* vec, void* item) {
vec->size++;
vec->items = realloc(vec->items, vec->size * sizeof(item));
vec->items[vec->size - 1] = item;
};
void ss_vector_free(ss_vector* vec) {
for (int i = 0; i < vec->size; i++)
free(vec->items[i]);
free(vec->items);
free(vec);
}
and how to use it:
// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT {
int id;
} apple;
apple* init_apple(int id) {
apple* a;
a = malloc(sizeof(apple));
a-> id = id;
return a;
};
int main(int argc, char* argv[]) {
ss_vector* vector = ss_init_vector(sizeof(apple));
// inserting some items
for (int i = 0; i < 10; i++)
ss_vector_append(vector, init_apple(i));
// dont forget to free it
ss_vector_free(vector);
return 0;
}
This vector/array can hold any type of item and it is completely dynamic in size.
Well, I guess if you need to remove an element you will make a copy of the array despising the element to be excluded.
// inserting some items
void* element_2_remove = getElement2BRemove();
for (int i = 0; i < vector->size; i++){
if(vector[i]!=element_2_remove) copy2TempVector(vector[i]);
}
free(vector->items);
free(vector);
fillFromTempVector(vector);
//
Assume that getElement2BRemove(), copy2TempVector( void* ...) and fillFromTempVector(...) are auxiliary methods to handle the temp vector.
These posts apparently are in the wrong order! This is #1 in a series of 3 posts. Sorry.
In attempting to use Lie Ryan's code, I had problems retrieving stored information. The vector's elements are not stored contiguously,as you can see by "cheating" a bit and storing the pointer to each element's address (which of course defeats the purpose of the dynamic array concept) and examining them.
With a bit of tinkering, via:
ss_vector* vector; // pull this out to be a global vector
// Then add the following to attempt to recover stored values.
int return_id_value(int i,apple* aa) // given ptr to component,return data item
{ printf("showing apple[%i].id = %i and other_id=%i\n",i,aa->id,aa->other_id);
return(aa->id);
}
int Test(void) // Used to be "main" in the example
{ apple* aa[10]; // stored array element addresses
vector = ss_init_vector(sizeof(apple));
// inserting some items
for (int i = 0; i < 10; i++)
{ aa[i]=init_apple(i);
printf("apple id=%i and other_id=%i\n",aa[i]->id,aa[i]->other_id);
ss_vector_append(vector, aa[i]);
}
// report the number of components
printf("nmbr of components in vector = %i\n",(int)vector->size);
printf(".*.*array access.*.component[5] = %i\n",return_id_value(5,aa[5]));
printf("components of size %i\n",(int)sizeof(apple));
printf("\n....pointer initial access...component[0] = %i\n",return_id_value(0,(apple *)&vector[0]));
//.............etc..., followed by
for (int i = 0; i < 10; i++)
{ printf("apple[%i].id = %i at address %i, delta=%i\n",i, return_id_value(i,aa[i]) ,(int)aa[i],(int)(aa[i]-aa[i+1]));
}
// don't forget to free it
ss_vector_free(vector);
return 0;
}
It's possible to access each array element without problems, as long as you know its address, so I guess I'll try adding a "next" element and use this as a linked list. Surely there are better options, though. Please advise.
These posts apparently are in the wrong order! This is #3 in a series of 3 posts. Sorry.
I've "taken a few MORE liberties" with Lie Ryan's code. The linked list admittedly was time-consuming to access individual
elements due to search overhead, i.e. walking down the list until you find the right element. I have now cured this by
maintaining an address vector containing subscripts 0 through whatever paired with memory addresses. This works
because the address vector is allocated all-at-once, thus contiguous in memory. Since the linked-list is no longer required,
I've ripped out its associated code and structure.
This approach is not quite as efficient as a plain-and-simple static array would be, but at least you don't have to "walk the list"
searching for the proper item. You can now access the elements by using a subscript. To enable this, I have had
to add code to handle cases where elements are removed and the "actual" subscripts wouldn't be reflected in the
pointer vector's subscripts. This may or may not be important to users. For me, it IS important, so
I've made re-numbering of subscripts optional. If renumbering is not used, program flow goes to a dummy
"missing" element which returns an error code, which users can choose to ignore or to act on as required.
From here, I'd advise users to code the "elements" portion to fit their needs and make sure that it runs correctly. If your
added elements are arrays, carefully code subroutines to access them, seeing as how there's extra array structure
that wasn't needed with static arrays. Enjoy!
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// Code from https://stackoverflow.com/questions/3536153/c-dynamically-growing-array
// For pointer-to-pointer info see:
// https://stackoverflow.com/questions/897366/how-do-pointer-to-pointers-work-in-c-and-when-might-you-use-them
typedef struct STRUCT_SS_VECTOR
{ size_t size; // # of vector elements
void** items; // makes up one vector element's component contents
int subscript; // this element's subscript nmbr, 0 thru whatever
// struct STRUCT_SS_VECTOR* this_element; // linked list via this ptr
// struct STRUCT_SS_VECTOR* next_element; // and next ptr
} ss_vector;
ss_vector* vector; // ptr to vector of components
ss_vector* missing_element(int subscript) // intercepts missing elements
{ printf("missing element at subscript %i\n",subscript);
return NULL;
}
typedef struct TRACKER_VECTOR
{ int subscript;
ss_vector* vector_ptr;
} tracker_vector; // up to 20 or so, max suggested
tracker_vector* tracker;
int max_tracker=0; // max allowable # of elements in "tracker_vector"
int tracker_count=0; // current # of elements in "tracker_vector"
int tracker_increment=5; // # of elements to add at each expansion
void bump_tracker_vector(int new_tracker_count)
{ //init or lengthen tracker vector
if(max_tracker==0) // not yet initialized
{ tracker=calloc(tracker_increment, sizeof(tracker_vector));
max_tracker=tracker_increment;
printf("initialized %i-element tracker vector of size %lu at %lu\n",max_tracker,sizeof(tracker_vector),(size_t)tracker);
tracker_count++;
return;
}
else if (max_tracker<=tracker_count) // append to existing tracker vector by writing a new one, copying old one
{ tracker_vector* temp_tracker=calloc(max_tracker+tracker_increment,sizeof(tracker_vector));
for(int i=0;(i<max_tracker);i++){ temp_tracker[i]=tracker[i];} // copy old tracker to new
max_tracker=max_tracker+tracker_increment;
free(tracker);
tracker=temp_tracker;
printf(" re-initialized %i-element tracker vector of size %lu at %lu\n",max_tracker,sizeof(tracker_vector),(size_t)tracker);
tracker_count++;
return;
} // else if
// fall through for most "bumps"
tracker_count++;
return;
} // bump_tracker_vector()
ss_vector* ss_init_vector(size_t item_size) // item_size is size of one array member
{ ss_vector* vector= malloc(sizeof(ss_vector));
vector->size = 0; // initialize count of vector component elements
vector->items = calloc(1, item_size); // allocate & zero out memory for one linked list element
vector->subscript=0;
bump_tracker_vector(0); // init/store the tracker vector
tracker[0].subscript=0;
tracker[0].vector_ptr=vector;
return vector; //->this_element;
} // ss_init_vector()
ss_vector* ss_vector_append( int i) // ptr to this element, element nmbr
{ ss_vector* local_vec_element=0;
local_vec_element= calloc(1,sizeof(ss_vector)); // memory for one component
local_vec_element->subscript=i; //vec_element->size;
local_vec_element->size=i; // increment # of vector components
bump_tracker_vector(i); // increment/store tracker vector
tracker[i].subscript=i;
tracker[i].vector_ptr=local_vec_element; //->this_element;
return local_vec_element;
} // ss_vector_append()
void bubble_sort(void)
{ // bubble sort
struct TRACKER_VECTOR local_tracker;
int i=0;
while(i<tracker_count-1)
{ if(tracker[i].subscript>tracker[i+1].subscript)
{ local_tracker.subscript=tracker[i].subscript; // swap tracker elements
local_tracker.vector_ptr=tracker[i].vector_ptr;
tracker[i].subscript=tracker[i+1].subscript;
tracker[i].vector_ptr=tracker[i+1].vector_ptr;
tracker[i+1].subscript=local_tracker.subscript;
tracker[i+1].vector_ptr=local_tracker.vector_ptr;
if(i>0) i--; // step back and go again
}
else
{ if(i<tracker_count-1) i++;
}
} // while()
} // void bubble_sort()
void move_toward_zero(int target_subscript) // toward zero
{ struct TRACKER_VECTOR local_tracker;
// Target to be moved must range from 1 to max_tracker
if((target_subscript<1)||(target_subscript>tracker_count)) return; // outside range
// swap target_subscript ptr and target_subscript-1 ptr
local_tracker.vector_ptr=tracker[target_subscript].vector_ptr;
tracker[target_subscript].vector_ptr=tracker[target_subscript-1].vector_ptr;
tracker[target_subscript-1].vector_ptr=local_tracker.vector_ptr;
}
void renumber_all_subscripts(gboolean arbitrary)
{ // assumes tracker_count has been fixed and tracker[tracker_count+1]has been zeroed out
if(arbitrary) // arbitrary renumber, ignoring "true" subscripts
{ for(int i=0;i<tracker_count;i++)
{ tracker[i].subscript=i;}
}
else // use "true" subscripts, holes and all
{ for(int i=0;i<tracker_count;i++)
{ if ((size_t)tracker[i].vector_ptr!=0) // renumbering "true" subscript tracker & vector_element
{ tracker[i].subscript=tracker[i].vector_ptr->subscript;}
else // renumbering "true" subscript tracker & NULL vector_element
{ tracker[i].subscript=-1;}
} // for()
bubble_sort();
} // if(arbitrary) ELSE
} // renumber_all_subscripts()
void collapse_tracker_higher_elements(int target_subscript)
{ // Fix tracker vector by collapsing higher subscripts toward 0.
// Assumes last tracker element entry is discarded.
int j;
for(j=target_subscript;(j<tracker_count-1);j++)
{ tracker[j].subscript=tracker[j+1].subscript;
tracker[j].vector_ptr=tracker[j+1].vector_ptr;
}
// Discard last tracker element and adjust count
tracker_count--;
tracker[tracker_count].subscript=0;
tracker[tracker_count].vector_ptr=(size_t)0;
} // void collapse_tracker_higher_elements()
void ss_vector_free_one_element(int target_subscript, gboolean Keep_subscripts)
{ // Free requested element contents.
// Adjust subscripts if desired; otherwise, mark NULL.
// ----special case: vector[0]
if(target_subscript==0) // knock out zeroth element no matter what
{ free(tracker[0].vector_ptr);}
// ----if not zeroth, start looking at other elements
else if(tracker_count<target_subscript-1)
{ printf("vector element not found\n");return;}
// Requested subscript okay. Freeit.
else
{ free(tracker[target_subscript].vector_ptr);} // free element ptr
// done with removal.
if(Keep_subscripts) // adjust subscripts if required.
{ tracker[target_subscript].vector_ptr=missing_element(target_subscript);} // point to "0" vector
else // NOT keeping subscripts intact, i.e. collapsing/renumbering all subscripts toward zero
{ collapse_tracker_higher_elements(target_subscript);
renumber_all_subscripts(TRUE); // gboolean arbitrary means as-is, FALSE means by "true" subscripts
} // if (target_subscript==0) else
// show the new list
// for(int i=0;i<tracker_count;i++){printf(" remaining element[%i] at %lu\n",tracker[i].subscript,(size_t)tracker[i].vector_ptr);}
} // void ss_vector_free_one_element()
void ss_vector_free_all_elements(void)
{ // Start at "tracker[0]". Walk the entire list, free each element's contents,
// then free that element, then move to the next one.
// Then free the "tracker" vector.
for(int i=tracker_count;i>=0;i--)
{ // Modify your code to free vector element "items" here
if(tracker[i].subscript>=0) free(tracker[i].vector_ptr);
}
free(tracker);
tracker_count=0;
} // void ss_vector_free_all_elements()
// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT
{ int id; // one of the data in the component
int other_id; // etc
struct APPLE_STRUCT* next_element;
} apple; // description of component
apple* init_apple(int id) // make a single component
{ apple* a; // ptr to component
a = malloc(sizeof(apple)); // memory for one component
a->id = id; // populate with data
a->other_id=id+10;
a->next_element=NULL;
// don't mess with aa->last_rec here
return a; // return pointer to component
}
int return_id_value(int i,apple* aa) // given ptr to component, return single data item
{ printf("was inserted as apple[%i].id = %i ",i,aa->id);
return(aa->id);
}
ss_vector* return_address_given_subscript(int i)
{ return tracker[i].vector_ptr;}
int Test(void) // was "main" in the example
{ int i;
ss_vector* local_vector;
local_vector=ss_init_vector(sizeof(apple)); // element "0"
for (i = 1; i < 10; i++) // inserting items "1" thru whatever
{local_vector=ss_vector_append(i);} // finished ss_vector_append()
// list all tracker vector entries
for(i=0;(i<tracker_count);i++) {printf("tracker element [%i] has address %lu\n",tracker[i].subscript, (size_t)tracker[i].vector_ptr);}
// ---test search function
printf("\n NEXT, test search for address given subscript\n");
local_vector=return_address_given_subscript(5);
printf("finished return_address_given_subscript(5) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(0);
printf("finished return_address_given_subscript(0) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(9);
printf("finished return_address_given_subscript(9) with vector at %lu\n",(size_t)local_vector);
// ---test single-element removal
printf("\nNEXT, test single element removal\n");
ss_vector_free_one_element(5,TRUE); // keep subscripts; install dummy error element
printf("finished ss_vector_free_one_element(5)\n");
ss_vector_free_one_element(3,FALSE);
printf("finished ss_vector_free_one_element(3)\n");
ss_vector_free_one_element(0,FALSE);
// ---test moving elements
printf("\n Test moving a few elements up\n");
move_toward_zero(5);
move_toward_zero(4);
move_toward_zero(3);
// show the new list
printf("New list:\n");
for(int i=0;i<tracker_count;i++){printf(" %i:element[%i] at %lu\n",i,tracker[i].subscript,(size_t)tracker[i].vector_ptr);}
// ---plant some bogus subscripts for the next subscript test
tracker[3].vector_ptr->subscript=7;
tracker[3].subscript=5;
tracker[7].vector_ptr->subscript=17;
tracker[3].subscript=55;
printf("\n RENUMBER to use \"actual\" subscripts\n");
renumber_all_subscripts(FALSE);
printf("Sorted list:\n");
for(int i=0;i<tracker_count;i++)
{ if ((size_t)tracker[i].vector_ptr!=0)
{ printf(" %i:element[%i] or [%i]at %lu\n",i,tracker[i].subscript,tracker[i].vector_ptr->subscript,(size_t)tracker[i].vector_ptr);
}
else
{ printf(" %i:element[%i] at 0\n",i,tracker[i].subscript);
}
}
printf("\nBubble sort to get TRUE order back\n");
bubble_sort();
printf("Sorted list:\n");
for(int i=0;i<tracker_count;i++)
{ if ((size_t)tracker[i].vector_ptr!=0)
{printf(" %i:element[%i] or [%i]at %lu\n",i,tracker[i].subscript,tracker[i].vector_ptr->subscript,(size_t)tracker[i].vector_ptr);}
else {printf(" %i:element[%i] at 0\n",i,tracker[i].subscript);}
}
// END TEST SECTION
// don't forget to free everything
ss_vector_free_all_elements();
return 0;
}
int main(int argc, char *argv[])
{ char cmd[5],main_buffer[50]; // Intentionally big for "other" I/O purposes
cmd[0]=32; // blank = ASCII 32
// while(cmd!="R"&&cmd!="W" &&cmd!="E" &&cmd!=" ")
while(cmd[0]!=82&&cmd[0]!=87&&cmd[0]!=69)//&&cmd[0]!=32)
{ memset(cmd, '\0', sizeof(cmd));
memset(main_buffer, '\0', sizeof(main_buffer));
// default back to the cmd loop
cmd[0]=32; // blank = ASCII 32
printf("REad, TEst, WRITe, EDIt, or EXIt? ");
fscanf(stdin, "%s", main_buffer);
strncpy(cmd,main_buffer,4);
for(int i=0;i<4;i++)cmd[i]=toupper(cmd[i]);
cmd[4]='\0';
printf("%s received\n ",cmd);
// process top level commands
if(cmd[0]==82) {printf("READ accepted\n");} //Read
else if(cmd[0]==87) {printf("WRITe accepted\n");} // Write
else if(cmd[0]==84)
{ printf("TESt accepted\n");// TESt
Test();
}
else if(cmd[0]==69) // "E"
{ if(cmd[1]==68) {printf("EDITing\n");} // eDit
else if(cmd[1]==88) {printf("EXITing\n");exit(0);} // eXit
else printf(" unknown E command %c%c\n",cmd[0],cmd[1]);
}
else printf(" unknown command\n");
cmd[0]=32; // blank = ASCII 32
} // while()
// default back to the cmd loop
} // main()
These posts may be in the wrong order! This is #2 in a series of 3 posts. Sorry.
I've "taken a few liberties" with Lie Ryan's code, implementing a linked list so individual elements of his vector can be accessed via a linked list. This allows access, but admittedly it is time-consuming to access individual elements due to search overhead, i.e. walking down the list until you find the right element. I'll cure this by maintaining an address vector containing subscripts 0 through whatever paired with memory addresses. This is still not as efficient as a plain-and-simple array would be, but at least you don't have to "walk the list" searching for the proper item.
// Based on code from https://stackoverflow.com/questions/3536153/c-dynamically-growing-array
typedef struct STRUCT_SS_VECTOR
{ size_t size; // # of vector elements
void** items; // makes up one vector element's component contents
int subscript; // this element's subscript nmbr, 0 thru whatever
struct STRUCT_SS_VECTOR* this_element; // linked list via this ptr
struct STRUCT_SS_VECTOR* next_element; // and next ptr
} ss_vector;
ss_vector* vector; // ptr to vector of components
ss_vector* ss_init_vector(size_t item_size) // item_size is size of one array member
{ vector= malloc(sizeof(ss_vector));
vector->this_element = vector;
vector->size = 0; // initialize count of vector component elements
vector->items = calloc(1, item_size); // allocate & zero out memory for one linked list element
vector->subscript=0;
vector->next_element=NULL;
// If there's an array of element addresses/subscripts, install it now.
return vector->this_element;
}
ss_vector* ss_vector_append(ss_vector* vec_element, int i)
// ^--ptr to this element ^--element nmbr
{ ss_vector* local_vec_element=0;
// If there is already a next element, recurse to end-of-linked-list
if(vec_element->next_element!=(size_t)0)
{ local_vec_element= ss_vector_append(vec_element->next_element,i); // recurse to end of list
return local_vec_element;
}
// vec_element is NULL, so make a new element and add at end of list
local_vec_element= calloc(1,sizeof(ss_vector)); // memory for one component
local_vec_element->this_element=local_vec_element; // save the address
local_vec_element->next_element=0;
vec_element->next_element=local_vec_element->this_element;
local_vec_element->subscript=i; //vec_element->size;
local_vec_element->size=i; // increment # of vector components
// If there's an array of element addresses/subscripts, update it now.
return local_vec_element;
}
void ss_vector_free_one_element(int i,gboolean Update_subscripts)
{ // Walk the entire linked list to the specified element, patch up
// the element ptrs before/next, then free its contents, then free it.
// Walk the rest of the list, updating subscripts, if requested.
// If there's an array of element addresses/subscripts, shift it along the way.
ss_vector* vec_element;
struct STRUCT_SS_VECTOR* this_one;
struct STRUCT_SS_VECTOR* next_one;
vec_element=vector;
while((vec_element->this_element->subscript!=i)&&(vec_element->next_element!=(size_t) 0)) // skip
{ this_one=vec_element->this_element; // trailing ptr
next_one=vec_element->next_element; // will become current ptr
vec_element=next_one;
}
// now at either target element or end-of-list
if(vec_element->this_element->subscript!=i)
{ printf("vector element not found\n");return;}
// free this one
this_one->next_element=next_one->next_element;// previous element points to element after current one
printf("freeing element[%i] at %lu",next_one->subscript,(size_t)next_one);
printf(" between %lu and %lu\n",(size_t)this_one,(size_t)next_one->next_element);
vec_element=next_one->next_element;
free(next_one); // free the current element
// renumber if requested
if(Update_subscripts)
{ i=0;
vec_element=vector;
while(vec_element!=(size_t) 0)
{ vec_element->subscript=i;
i++;
vec_element=vec_element->next_element;
}
}
// If there's an array of element addresses/subscripts, update it now.
/* // Check: temporarily show the new list
vec_element=vector;
while(vec_element!=(size_t) 0)
{ printf(" remaining element[%i] at %lu\n",vec_element->subscript,(size_t)vec_element->this_element);
vec_element=vec_element->next_element;
} */
return;
} // void ss_vector_free_one_element()
void ss_vector_insert_one_element(ss_vector* vec_element,int place)
{ // Walk the entire linked list to specified element "place", patch up
// the element ptrs before/next, then calloc an element and store its contents at "place".
// Increment all the following subscripts.
// If there's an array of element addresses/subscripts, make a bigger one,
// copy the old one, then shift appropriate members.
// ***Not yet implemented***
} // void ss_vector_insert_one_element()
void ss_vector_free_all_elements(void)
{ // Start at "vector".Walk the entire linked list, free each element's contents,
// free that element, then move to the next one.
// If there's an array of element addresses/subscripts, free it.
ss_vector* vec_element;
struct STRUCT_SS_VECTOR* next_one;
vec_element=vector;
while(vec_element->next_element!=(size_t) 0)
{ next_one=vec_element->next_element;
// free(vec_element->items) // don't forget to free these
free(vec_element->this_element);
vec_element=next_one;
next_one=vec_element->this_element;
}
// get rid of the last one.
// free(vec_element->items)
free(vec_element);
vector=NULL;
// If there's an array of element addresses/subscripts, free it now.
printf("\nall vector elements & contents freed\n");
} // void ss_vector_free_all_elements()
// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT
{ int id; // one of the data in the component
int other_id; // etc
struct APPLE_STRUCT* next_element;
} apple; // description of component
apple* init_apple(int id) // make a single component
{ apple* a; // ptr to component
a = malloc(sizeof(apple)); // memory for one component
a->id = id; // populate with data
a->other_id=id+10;
a->next_element=NULL;
// don't mess with aa->last_rec here
return a; // return pointer to component
};
int return_id_value(int i,apple* aa) // given ptr to component, return single data item
{ printf("was inserted as apple[%i].id = %i ",i,aa->id);
return(aa->id);
}
ss_vector* return_address_given_subscript(ss_vector* vec_element,int i)
// always make the first call to this subroutine with global vbl "vector"
{ ss_vector* local_vec_element=0;
// If there is a next element, recurse toward end-of-linked-list
if(vec_element->next_element!=(size_t)0)
{ if((vec_element->this_element->subscript==i))
{ return vec_element->this_element;}
local_vec_element= return_address_given_subscript(vec_element->next_element,i); // recurse to end of list
return local_vec_element;
}
else
{ if((vec_element->this_element->subscript==i)) // last element
{ return vec_element->this_element;}
// otherwise, none match
printf("reached end of list without match\n");
return (size_t) 0;
}
} // return_address_given_subscript()
int Test(void) // was "main" in the original example
{ ss_vector* local_vector;
local_vector=ss_init_vector(sizeof(apple)); // element "0"
for (int i = 1; i < 10; i++) // inserting items "1" thru whatever
{ local_vector=ss_vector_append(vector,i);}
// test search function
printf("\n NEXT, test search for address given subscript\n");
local_vector=return_address_given_subscript(vector,5);
printf("finished return_address_given_subscript(5) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(vector,0);
printf("finished return_address_given_subscript(0) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(vector,9);
printf("finished return_address_given_subscript(9) with vector at %lu\n",(size_t)local_vector);
// test single-element removal
printf("\nNEXT, test single element removal\n");
ss_vector_free_one_element(5,FALSE); // without renumbering subscripts
ss_vector_free_one_element(3,TRUE);// WITH renumbering subscripts
// ---end of program---
// don't forget to free everything
ss_vector_free_all_elements();
return 0;
}
I have a program that reads a "raw" list of in-game entities, and I intend to make an array holding an index number (int) of an indeterminate number of entities, for processing various things. I would like to avoid using too much memory or CPU for keeping such indexes...
A quick and dirty solution I use so far is to declare, in the main processing function (local focus) the array with a size of the maximum game entities, and another integer to keep track of how many have been added to the list.
This isn't satisfactory, as every list holds 3000+ arrays, which isn't that much, but feels like a waste, since I'll possible use the solution for 6-7 lists for varying functions.
I haven't found any C (not C++ or C#) specific solutions to achieve this. I can use pointers, but I am a bit afraid of using them (unless it's the only possible way).
The arrays do not leave the local function scope (they are to be passed to a function, then discarded), in case that changes things.
If pointers are the only solution, how can I keep track of them to avoid leaks?
I can use pointers, but I am a bit afraid of using them.
If you need a dynamic array, you can't escape pointers. Why are you afraid though? They won't bite (as long as you're careful, that is). There's no built-in dynamic array in C, you'll just have to write one yourself. In C++, you can use the built-in std::vector class. C# and just about every other high-level language also have some similar class that manages dynamic arrays for you.
If you do plan to write your own, here's something to get you started: most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array. As you can see in the example below, it's not very difficult at all: (I've omitted safety checks for brevity)
typedef struct {
int *array;
size_t used;
size_t size;
} Array;
void initArray(Array *a, size_t initialSize) {
a->array = malloc(initialSize * sizeof(int));
a->used = 0;
a->size = initialSize;
}
void insertArray(Array *a, int element) {
// a->used is the number of used entries, because a->array[a->used++] updates a->used only *after* the array has been accessed.
// Therefore a->used can go up to a->size
if (a->used == a->size) {
a->size *= 2;
a->array = realloc(a->array, a->size * sizeof(int));
}
a->array[a->used++] = element;
}
void freeArray(Array *a) {
free(a->array);
a->array = NULL;
a->used = a->size = 0;
}
Using it is just as simple:
Array a;
int i;
initArray(&a, 5); // initially 5 elements
for (i = 0; i < 100; i++)
insertArray(&a, i); // automatically resizes as necessary
printf("%d\n", a.array[9]); // print 10th element
printf("%d\n", a.used); // print number of elements
freeArray(&a);
One simple solution involves mmap. This is great if you can tolerate a POSIX solution. Just map a whole page and guard against overflows, since realloc would fail for such values anyway. Modern OSes won't commit to the whole lot until you use it, and you can truncate files if you want.
Alternatively, there's realloc. As with everything that seems scarier at first than it was later, the best way to get over the initial fear is to immerse yourself into the discomfort of the unknown! It is at times like that which we learn the most, after all.
Unfortunately, there are limitations. While you're still learning to use a function, you shouldn't assume the role of a teacher, for example. I often read answers from those who seemingly don't know how to use realloc (i.e. the currently accepted answer!) telling others how to use it incorrectly, occasionally under the guise that they've omitted error handling, even though this is a common pitfall which needs mention. Here's an answer explaining how to use realloc correctly. Take note that the answer is storing the return value into a different variable in order to perform error checking.
Every time you call a function, and every time you use an array, you are using a pointer. The conversions are occurring implicitly, which if anything should be even scarier, as it's the things we don't see which often cause the most problems. For example, memory leaks...
Array operators are pointer operators. array[x] is really a shortcut for *(array + x), which can be broken down into: * and (array + x). It's most likely that the * is what confuses you. We can further eliminate the addition from the problem by assuming x to be 0, thus, array[0] becomes *array because adding 0 won't change the value...
... and thus we can see that *array is equivalent to array[0]. You can use one where you want to use the other, and vice versa. Array operators are pointer operators.
malloc, realloc and friends don't invent the concept of a pointer which you've been using all along; they merely use this to implement some other feature, which is a different form of storage duration, most suitable when you desire drastic, dynamic changes in size.
It is a shame that the currently accepted answer also goes against the grain of some other very well-founded advice on StackOverflow, and at the same time, misses an opportunity to introduce a little-known feature which shines for exactly this usecase: flexible array members! That's actually a pretty broken answer... :(
When you define your struct, declare your array at the end of the structure, without any upper bound. For example:
struct int_list {
size_t size;
int value[];
};
This will allow you to unite your array of int into the same allocation as your count, and having them bound like this can be very handy!
sizeof (struct int_list) will act as though value has a size of 0, so it'll tell you the size of the structure with an empty list. You still need to add to the size passed to realloc to specify the size of your list.
Another handy tip is to remember that realloc(NULL, x) is equivalent to malloc(x), and we can use this to simplify our code. For example:
int push_back(struct int_list **fubar, int value) {
size_t x = *fubar ? fubar[0]->size : 0
, y = x + 1;
if ((x & y) == 0) {
void *temp = realloc(*fubar, sizeof **fubar
+ (x + y) * sizeof fubar[0]->value[0]);
if (!temp) { return 1; }
*fubar = temp; // or, if you like, `fubar[0] = temp;`
}
fubar[0]->value[x] = value;
fubar[0]->size = y;
return 0;
}
struct int_list *array = NULL;
The reason I chose to use struct int_list ** as the first argument may not seem immediately obvious, but if you think about the second argument, any changes made to value from within push_back would not be visible to the function we're calling from, right? The same goes for the first argument, and we need to be able to modify our array, not just here but possibly also in any other function/s we pass it to...
array starts off pointing at nothing; it is an empty list. Initialising it is the same as adding to it. For example:
struct int_list *array = NULL;
if (!push_back(&array, 42)) {
// success!
}
P.S. Remember to free(array); when you're done with it!
There are a couple of options I can think of.
Linked List. You can use a linked list to make a dynamically growing array like thing. But you won't be able to do array[100] without having to walk through 1-99 first. And it might not be that handy for you to use either.
Large array. Simply create an array with more than enough space for everything
Resizing array. Recreate the array once you know the size and/or create a new array every time you run out of space with some margin and copy all the data to the new array.
Linked List Array combination. Simply use an array with a fixed size and once you run out of space, create a new array and link to that (it would be wise to keep track of the array and the link to the next array in a struct).
It is hard to say what option would be best in your situation. Simply creating a large array is ofcourse one of the easiest solutions and shouldn't give you much problems unless it's really large.
Building on Matteo Furlans design, when he said "most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array". The difference in the "work in progress" below is that it doesn't double in size, it aims at using only what is required. I have also omitted safety checks for simplicity...Also building on brimboriums idea, I have tried to add a delete function to the code...
The storage.h file looks like this...
#ifndef STORAGE_H
#define STORAGE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
int *array;
size_t size;
} Array;
void Array_Init(Array *array);
void Array_Add(Array *array, int item);
void Array_Delete(Array *array, int index);
void Array_Free(Array *array);
#ifdef __cplusplus
}
#endif
#endif /* STORAGE_H */
The storage.c file looks like this...
#include <stdio.h>
#include <stdlib.h>
#include "storage.h"
/* Initialise an empty array */
void Array_Init(Array *array)
{
int *int_pointer;
int_pointer = (int *)malloc(sizeof(int));
if (int_pointer == NULL)
{
printf("Unable to allocate memory, exiting.\n");
free(int_pointer);
exit(0);
}
else
{
array->array = int_pointer;
array->size = 0;
}
}
/* Dynamically add to end of an array */
void Array_Add(Array *array, int item)
{
int *int_pointer;
array->size += 1;
int_pointer = (int *)realloc(array->array, array->size * sizeof(int));
if (int_pointer == NULL)
{
printf("Unable to reallocate memory, exiting.\n");
free(int_pointer);
exit(0);
}
else
{
array->array = int_pointer;
array->array[array->size-1] = item;
}
}
/* Delete from a dynamic array */
void Array_Delete(Array *array, int index)
{
int i;
Array temp;
int *int_pointer;
Array_Init(&temp);
for(i=index; i<array->size; i++)
{
array->array[i] = array->array[i + 1];
}
array->size -= 1;
for (i = 0; i < array->size; i++)
{
Array_Add(&temp, array->array[i]);
}
int_pointer = (int *)realloc(temp.array, temp.size * sizeof(int));
if (int_pointer == NULL)
{
printf("Unable to reallocate memory, exiting.\n");
free(int_pointer);
exit(0);
}
else
{
array->array = int_pointer;
}
}
/* Free an array */
void Array_Free(Array *array)
{
free(array->array);
array->array = NULL;
array->size = 0;
}
The main.c looks like this...
#include <stdio.h>
#include <stdlib.h>
#include "storage.h"
int main(int argc, char** argv)
{
Array pointers;
int i;
Array_Init(&pointers);
for (i = 0; i < 60; i++)
{
Array_Add(&pointers, i);
}
Array_Delete(&pointers, 3);
Array_Delete(&pointers, 6);
Array_Delete(&pointers, 30);
for (i = 0; i < pointers.size; i++)
{
printf("Value: %d Size:%d \n", pointers.array[i], pointers.size);
}
Array_Free(&pointers);
return (EXIT_SUCCESS);
}
Look forward to the constructive criticism to follow...
When you're saying
make an array holding an index number (int) of an indeterminate number of entities
you're basically saying you're using "pointers", but one which is a array-wide local pointer instead of memory-wide pointer. Since you're conceptually already using "pointers" (i.e. id numbers that refers to an element in an array), why don't you just use regular pointers (i.e. id numbers that refers to an element in the biggest array: the whole memory).
Instead of your objects storing a resource id numbers, you can make them store a pointer instead. Basically the same thing, but much more efficient since we avoid turning "array + index" into a "pointer".
Pointers are not scary if you think of them as array index for the whole memory (which is what they actually are)
To create an array of unlimited items of any sort of type:
typedef struct STRUCT_SS_VECTOR {
size_t size;
void** items;
} ss_vector;
ss_vector* ss_init_vector(size_t item_size) {
ss_vector* vector;
vector = malloc(sizeof(ss_vector));
vector->size = 0;
vector->items = calloc(0, item_size);
return vector;
}
void ss_vector_append(ss_vector* vec, void* item) {
vec->size++;
vec->items = realloc(vec->items, vec->size * sizeof(item));
vec->items[vec->size - 1] = item;
};
void ss_vector_free(ss_vector* vec) {
for (int i = 0; i < vec->size; i++)
free(vec->items[i]);
free(vec->items);
free(vec);
}
and how to use it:
// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT {
int id;
} apple;
apple* init_apple(int id) {
apple* a;
a = malloc(sizeof(apple));
a-> id = id;
return a;
};
int main(int argc, char* argv[]) {
ss_vector* vector = ss_init_vector(sizeof(apple));
// inserting some items
for (int i = 0; i < 10; i++)
ss_vector_append(vector, init_apple(i));
// dont forget to free it
ss_vector_free(vector);
return 0;
}
This vector/array can hold any type of item and it is completely dynamic in size.
Well, I guess if you need to remove an element you will make a copy of the array despising the element to be excluded.
// inserting some items
void* element_2_remove = getElement2BRemove();
for (int i = 0; i < vector->size; i++){
if(vector[i]!=element_2_remove) copy2TempVector(vector[i]);
}
free(vector->items);
free(vector);
fillFromTempVector(vector);
//
Assume that getElement2BRemove(), copy2TempVector( void* ...) and fillFromTempVector(...) are auxiliary methods to handle the temp vector.
These posts apparently are in the wrong order! This is #1 in a series of 3 posts. Sorry.
In attempting to use Lie Ryan's code, I had problems retrieving stored information. The vector's elements are not stored contiguously,as you can see by "cheating" a bit and storing the pointer to each element's address (which of course defeats the purpose of the dynamic array concept) and examining them.
With a bit of tinkering, via:
ss_vector* vector; // pull this out to be a global vector
// Then add the following to attempt to recover stored values.
int return_id_value(int i,apple* aa) // given ptr to component,return data item
{ printf("showing apple[%i].id = %i and other_id=%i\n",i,aa->id,aa->other_id);
return(aa->id);
}
int Test(void) // Used to be "main" in the example
{ apple* aa[10]; // stored array element addresses
vector = ss_init_vector(sizeof(apple));
// inserting some items
for (int i = 0; i < 10; i++)
{ aa[i]=init_apple(i);
printf("apple id=%i and other_id=%i\n",aa[i]->id,aa[i]->other_id);
ss_vector_append(vector, aa[i]);
}
// report the number of components
printf("nmbr of components in vector = %i\n",(int)vector->size);
printf(".*.*array access.*.component[5] = %i\n",return_id_value(5,aa[5]));
printf("components of size %i\n",(int)sizeof(apple));
printf("\n....pointer initial access...component[0] = %i\n",return_id_value(0,(apple *)&vector[0]));
//.............etc..., followed by
for (int i = 0; i < 10; i++)
{ printf("apple[%i].id = %i at address %i, delta=%i\n",i, return_id_value(i,aa[i]) ,(int)aa[i],(int)(aa[i]-aa[i+1]));
}
// don't forget to free it
ss_vector_free(vector);
return 0;
}
It's possible to access each array element without problems, as long as you know its address, so I guess I'll try adding a "next" element and use this as a linked list. Surely there are better options, though. Please advise.
These posts apparently are in the wrong order! This is #3 in a series of 3 posts. Sorry.
I've "taken a few MORE liberties" with Lie Ryan's code. The linked list admittedly was time-consuming to access individual
elements due to search overhead, i.e. walking down the list until you find the right element. I have now cured this by
maintaining an address vector containing subscripts 0 through whatever paired with memory addresses. This works
because the address vector is allocated all-at-once, thus contiguous in memory. Since the linked-list is no longer required,
I've ripped out its associated code and structure.
This approach is not quite as efficient as a plain-and-simple static array would be, but at least you don't have to "walk the list"
searching for the proper item. You can now access the elements by using a subscript. To enable this, I have had
to add code to handle cases where elements are removed and the "actual" subscripts wouldn't be reflected in the
pointer vector's subscripts. This may or may not be important to users. For me, it IS important, so
I've made re-numbering of subscripts optional. If renumbering is not used, program flow goes to a dummy
"missing" element which returns an error code, which users can choose to ignore or to act on as required.
From here, I'd advise users to code the "elements" portion to fit their needs and make sure that it runs correctly. If your
added elements are arrays, carefully code subroutines to access them, seeing as how there's extra array structure
that wasn't needed with static arrays. Enjoy!
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// Code from https://stackoverflow.com/questions/3536153/c-dynamically-growing-array
// For pointer-to-pointer info see:
// https://stackoverflow.com/questions/897366/how-do-pointer-to-pointers-work-in-c-and-when-might-you-use-them
typedef struct STRUCT_SS_VECTOR
{ size_t size; // # of vector elements
void** items; // makes up one vector element's component contents
int subscript; // this element's subscript nmbr, 0 thru whatever
// struct STRUCT_SS_VECTOR* this_element; // linked list via this ptr
// struct STRUCT_SS_VECTOR* next_element; // and next ptr
} ss_vector;
ss_vector* vector; // ptr to vector of components
ss_vector* missing_element(int subscript) // intercepts missing elements
{ printf("missing element at subscript %i\n",subscript);
return NULL;
}
typedef struct TRACKER_VECTOR
{ int subscript;
ss_vector* vector_ptr;
} tracker_vector; // up to 20 or so, max suggested
tracker_vector* tracker;
int max_tracker=0; // max allowable # of elements in "tracker_vector"
int tracker_count=0; // current # of elements in "tracker_vector"
int tracker_increment=5; // # of elements to add at each expansion
void bump_tracker_vector(int new_tracker_count)
{ //init or lengthen tracker vector
if(max_tracker==0) // not yet initialized
{ tracker=calloc(tracker_increment, sizeof(tracker_vector));
max_tracker=tracker_increment;
printf("initialized %i-element tracker vector of size %lu at %lu\n",max_tracker,sizeof(tracker_vector),(size_t)tracker);
tracker_count++;
return;
}
else if (max_tracker<=tracker_count) // append to existing tracker vector by writing a new one, copying old one
{ tracker_vector* temp_tracker=calloc(max_tracker+tracker_increment,sizeof(tracker_vector));
for(int i=0;(i<max_tracker);i++){ temp_tracker[i]=tracker[i];} // copy old tracker to new
max_tracker=max_tracker+tracker_increment;
free(tracker);
tracker=temp_tracker;
printf(" re-initialized %i-element tracker vector of size %lu at %lu\n",max_tracker,sizeof(tracker_vector),(size_t)tracker);
tracker_count++;
return;
} // else if
// fall through for most "bumps"
tracker_count++;
return;
} // bump_tracker_vector()
ss_vector* ss_init_vector(size_t item_size) // item_size is size of one array member
{ ss_vector* vector= malloc(sizeof(ss_vector));
vector->size = 0; // initialize count of vector component elements
vector->items = calloc(1, item_size); // allocate & zero out memory for one linked list element
vector->subscript=0;
bump_tracker_vector(0); // init/store the tracker vector
tracker[0].subscript=0;
tracker[0].vector_ptr=vector;
return vector; //->this_element;
} // ss_init_vector()
ss_vector* ss_vector_append( int i) // ptr to this element, element nmbr
{ ss_vector* local_vec_element=0;
local_vec_element= calloc(1,sizeof(ss_vector)); // memory for one component
local_vec_element->subscript=i; //vec_element->size;
local_vec_element->size=i; // increment # of vector components
bump_tracker_vector(i); // increment/store tracker vector
tracker[i].subscript=i;
tracker[i].vector_ptr=local_vec_element; //->this_element;
return local_vec_element;
} // ss_vector_append()
void bubble_sort(void)
{ // bubble sort
struct TRACKER_VECTOR local_tracker;
int i=0;
while(i<tracker_count-1)
{ if(tracker[i].subscript>tracker[i+1].subscript)
{ local_tracker.subscript=tracker[i].subscript; // swap tracker elements
local_tracker.vector_ptr=tracker[i].vector_ptr;
tracker[i].subscript=tracker[i+1].subscript;
tracker[i].vector_ptr=tracker[i+1].vector_ptr;
tracker[i+1].subscript=local_tracker.subscript;
tracker[i+1].vector_ptr=local_tracker.vector_ptr;
if(i>0) i--; // step back and go again
}
else
{ if(i<tracker_count-1) i++;
}
} // while()
} // void bubble_sort()
void move_toward_zero(int target_subscript) // toward zero
{ struct TRACKER_VECTOR local_tracker;
// Target to be moved must range from 1 to max_tracker
if((target_subscript<1)||(target_subscript>tracker_count)) return; // outside range
// swap target_subscript ptr and target_subscript-1 ptr
local_tracker.vector_ptr=tracker[target_subscript].vector_ptr;
tracker[target_subscript].vector_ptr=tracker[target_subscript-1].vector_ptr;
tracker[target_subscript-1].vector_ptr=local_tracker.vector_ptr;
}
void renumber_all_subscripts(gboolean arbitrary)
{ // assumes tracker_count has been fixed and tracker[tracker_count+1]has been zeroed out
if(arbitrary) // arbitrary renumber, ignoring "true" subscripts
{ for(int i=0;i<tracker_count;i++)
{ tracker[i].subscript=i;}
}
else // use "true" subscripts, holes and all
{ for(int i=0;i<tracker_count;i++)
{ if ((size_t)tracker[i].vector_ptr!=0) // renumbering "true" subscript tracker & vector_element
{ tracker[i].subscript=tracker[i].vector_ptr->subscript;}
else // renumbering "true" subscript tracker & NULL vector_element
{ tracker[i].subscript=-1;}
} // for()
bubble_sort();
} // if(arbitrary) ELSE
} // renumber_all_subscripts()
void collapse_tracker_higher_elements(int target_subscript)
{ // Fix tracker vector by collapsing higher subscripts toward 0.
// Assumes last tracker element entry is discarded.
int j;
for(j=target_subscript;(j<tracker_count-1);j++)
{ tracker[j].subscript=tracker[j+1].subscript;
tracker[j].vector_ptr=tracker[j+1].vector_ptr;
}
// Discard last tracker element and adjust count
tracker_count--;
tracker[tracker_count].subscript=0;
tracker[tracker_count].vector_ptr=(size_t)0;
} // void collapse_tracker_higher_elements()
void ss_vector_free_one_element(int target_subscript, gboolean Keep_subscripts)
{ // Free requested element contents.
// Adjust subscripts if desired; otherwise, mark NULL.
// ----special case: vector[0]
if(target_subscript==0) // knock out zeroth element no matter what
{ free(tracker[0].vector_ptr);}
// ----if not zeroth, start looking at other elements
else if(tracker_count<target_subscript-1)
{ printf("vector element not found\n");return;}
// Requested subscript okay. Freeit.
else
{ free(tracker[target_subscript].vector_ptr);} // free element ptr
// done with removal.
if(Keep_subscripts) // adjust subscripts if required.
{ tracker[target_subscript].vector_ptr=missing_element(target_subscript);} // point to "0" vector
else // NOT keeping subscripts intact, i.e. collapsing/renumbering all subscripts toward zero
{ collapse_tracker_higher_elements(target_subscript);
renumber_all_subscripts(TRUE); // gboolean arbitrary means as-is, FALSE means by "true" subscripts
} // if (target_subscript==0) else
// show the new list
// for(int i=0;i<tracker_count;i++){printf(" remaining element[%i] at %lu\n",tracker[i].subscript,(size_t)tracker[i].vector_ptr);}
} // void ss_vector_free_one_element()
void ss_vector_free_all_elements(void)
{ // Start at "tracker[0]". Walk the entire list, free each element's contents,
// then free that element, then move to the next one.
// Then free the "tracker" vector.
for(int i=tracker_count;i>=0;i--)
{ // Modify your code to free vector element "items" here
if(tracker[i].subscript>=0) free(tracker[i].vector_ptr);
}
free(tracker);
tracker_count=0;
} // void ss_vector_free_all_elements()
// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT
{ int id; // one of the data in the component
int other_id; // etc
struct APPLE_STRUCT* next_element;
} apple; // description of component
apple* init_apple(int id) // make a single component
{ apple* a; // ptr to component
a = malloc(sizeof(apple)); // memory for one component
a->id = id; // populate with data
a->other_id=id+10;
a->next_element=NULL;
// don't mess with aa->last_rec here
return a; // return pointer to component
}
int return_id_value(int i,apple* aa) // given ptr to component, return single data item
{ printf("was inserted as apple[%i].id = %i ",i,aa->id);
return(aa->id);
}
ss_vector* return_address_given_subscript(int i)
{ return tracker[i].vector_ptr;}
int Test(void) // was "main" in the example
{ int i;
ss_vector* local_vector;
local_vector=ss_init_vector(sizeof(apple)); // element "0"
for (i = 1; i < 10; i++) // inserting items "1" thru whatever
{local_vector=ss_vector_append(i);} // finished ss_vector_append()
// list all tracker vector entries
for(i=0;(i<tracker_count);i++) {printf("tracker element [%i] has address %lu\n",tracker[i].subscript, (size_t)tracker[i].vector_ptr);}
// ---test search function
printf("\n NEXT, test search for address given subscript\n");
local_vector=return_address_given_subscript(5);
printf("finished return_address_given_subscript(5) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(0);
printf("finished return_address_given_subscript(0) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(9);
printf("finished return_address_given_subscript(9) with vector at %lu\n",(size_t)local_vector);
// ---test single-element removal
printf("\nNEXT, test single element removal\n");
ss_vector_free_one_element(5,TRUE); // keep subscripts; install dummy error element
printf("finished ss_vector_free_one_element(5)\n");
ss_vector_free_one_element(3,FALSE);
printf("finished ss_vector_free_one_element(3)\n");
ss_vector_free_one_element(0,FALSE);
// ---test moving elements
printf("\n Test moving a few elements up\n");
move_toward_zero(5);
move_toward_zero(4);
move_toward_zero(3);
// show the new list
printf("New list:\n");
for(int i=0;i<tracker_count;i++){printf(" %i:element[%i] at %lu\n",i,tracker[i].subscript,(size_t)tracker[i].vector_ptr);}
// ---plant some bogus subscripts for the next subscript test
tracker[3].vector_ptr->subscript=7;
tracker[3].subscript=5;
tracker[7].vector_ptr->subscript=17;
tracker[3].subscript=55;
printf("\n RENUMBER to use \"actual\" subscripts\n");
renumber_all_subscripts(FALSE);
printf("Sorted list:\n");
for(int i=0;i<tracker_count;i++)
{ if ((size_t)tracker[i].vector_ptr!=0)
{ printf(" %i:element[%i] or [%i]at %lu\n",i,tracker[i].subscript,tracker[i].vector_ptr->subscript,(size_t)tracker[i].vector_ptr);
}
else
{ printf(" %i:element[%i] at 0\n",i,tracker[i].subscript);
}
}
printf("\nBubble sort to get TRUE order back\n");
bubble_sort();
printf("Sorted list:\n");
for(int i=0;i<tracker_count;i++)
{ if ((size_t)tracker[i].vector_ptr!=0)
{printf(" %i:element[%i] or [%i]at %lu\n",i,tracker[i].subscript,tracker[i].vector_ptr->subscript,(size_t)tracker[i].vector_ptr);}
else {printf(" %i:element[%i] at 0\n",i,tracker[i].subscript);}
}
// END TEST SECTION
// don't forget to free everything
ss_vector_free_all_elements();
return 0;
}
int main(int argc, char *argv[])
{ char cmd[5],main_buffer[50]; // Intentionally big for "other" I/O purposes
cmd[0]=32; // blank = ASCII 32
// while(cmd!="R"&&cmd!="W" &&cmd!="E" &&cmd!=" ")
while(cmd[0]!=82&&cmd[0]!=87&&cmd[0]!=69)//&&cmd[0]!=32)
{ memset(cmd, '\0', sizeof(cmd));
memset(main_buffer, '\0', sizeof(main_buffer));
// default back to the cmd loop
cmd[0]=32; // blank = ASCII 32
printf("REad, TEst, WRITe, EDIt, or EXIt? ");
fscanf(stdin, "%s", main_buffer);
strncpy(cmd,main_buffer,4);
for(int i=0;i<4;i++)cmd[i]=toupper(cmd[i]);
cmd[4]='\0';
printf("%s received\n ",cmd);
// process top level commands
if(cmd[0]==82) {printf("READ accepted\n");} //Read
else if(cmd[0]==87) {printf("WRITe accepted\n");} // Write
else if(cmd[0]==84)
{ printf("TESt accepted\n");// TESt
Test();
}
else if(cmd[0]==69) // "E"
{ if(cmd[1]==68) {printf("EDITing\n");} // eDit
else if(cmd[1]==88) {printf("EXITing\n");exit(0);} // eXit
else printf(" unknown E command %c%c\n",cmd[0],cmd[1]);
}
else printf(" unknown command\n");
cmd[0]=32; // blank = ASCII 32
} // while()
// default back to the cmd loop
} // main()
These posts may be in the wrong order! This is #2 in a series of 3 posts. Sorry.
I've "taken a few liberties" with Lie Ryan's code, implementing a linked list so individual elements of his vector can be accessed via a linked list. This allows access, but admittedly it is time-consuming to access individual elements due to search overhead, i.e. walking down the list until you find the right element. I'll cure this by maintaining an address vector containing subscripts 0 through whatever paired with memory addresses. This is still not as efficient as a plain-and-simple array would be, but at least you don't have to "walk the list" searching for the proper item.
// Based on code from https://stackoverflow.com/questions/3536153/c-dynamically-growing-array
typedef struct STRUCT_SS_VECTOR
{ size_t size; // # of vector elements
void** items; // makes up one vector element's component contents
int subscript; // this element's subscript nmbr, 0 thru whatever
struct STRUCT_SS_VECTOR* this_element; // linked list via this ptr
struct STRUCT_SS_VECTOR* next_element; // and next ptr
} ss_vector;
ss_vector* vector; // ptr to vector of components
ss_vector* ss_init_vector(size_t item_size) // item_size is size of one array member
{ vector= malloc(sizeof(ss_vector));
vector->this_element = vector;
vector->size = 0; // initialize count of vector component elements
vector->items = calloc(1, item_size); // allocate & zero out memory for one linked list element
vector->subscript=0;
vector->next_element=NULL;
// If there's an array of element addresses/subscripts, install it now.
return vector->this_element;
}
ss_vector* ss_vector_append(ss_vector* vec_element, int i)
// ^--ptr to this element ^--element nmbr
{ ss_vector* local_vec_element=0;
// If there is already a next element, recurse to end-of-linked-list
if(vec_element->next_element!=(size_t)0)
{ local_vec_element= ss_vector_append(vec_element->next_element,i); // recurse to end of list
return local_vec_element;
}
// vec_element is NULL, so make a new element and add at end of list
local_vec_element= calloc(1,sizeof(ss_vector)); // memory for one component
local_vec_element->this_element=local_vec_element; // save the address
local_vec_element->next_element=0;
vec_element->next_element=local_vec_element->this_element;
local_vec_element->subscript=i; //vec_element->size;
local_vec_element->size=i; // increment # of vector components
// If there's an array of element addresses/subscripts, update it now.
return local_vec_element;
}
void ss_vector_free_one_element(int i,gboolean Update_subscripts)
{ // Walk the entire linked list to the specified element, patch up
// the element ptrs before/next, then free its contents, then free it.
// Walk the rest of the list, updating subscripts, if requested.
// If there's an array of element addresses/subscripts, shift it along the way.
ss_vector* vec_element;
struct STRUCT_SS_VECTOR* this_one;
struct STRUCT_SS_VECTOR* next_one;
vec_element=vector;
while((vec_element->this_element->subscript!=i)&&(vec_element->next_element!=(size_t) 0)) // skip
{ this_one=vec_element->this_element; // trailing ptr
next_one=vec_element->next_element; // will become current ptr
vec_element=next_one;
}
// now at either target element or end-of-list
if(vec_element->this_element->subscript!=i)
{ printf("vector element not found\n");return;}
// free this one
this_one->next_element=next_one->next_element;// previous element points to element after current one
printf("freeing element[%i] at %lu",next_one->subscript,(size_t)next_one);
printf(" between %lu and %lu\n",(size_t)this_one,(size_t)next_one->next_element);
vec_element=next_one->next_element;
free(next_one); // free the current element
// renumber if requested
if(Update_subscripts)
{ i=0;
vec_element=vector;
while(vec_element!=(size_t) 0)
{ vec_element->subscript=i;
i++;
vec_element=vec_element->next_element;
}
}
// If there's an array of element addresses/subscripts, update it now.
/* // Check: temporarily show the new list
vec_element=vector;
while(vec_element!=(size_t) 0)
{ printf(" remaining element[%i] at %lu\n",vec_element->subscript,(size_t)vec_element->this_element);
vec_element=vec_element->next_element;
} */
return;
} // void ss_vector_free_one_element()
void ss_vector_insert_one_element(ss_vector* vec_element,int place)
{ // Walk the entire linked list to specified element "place", patch up
// the element ptrs before/next, then calloc an element and store its contents at "place".
// Increment all the following subscripts.
// If there's an array of element addresses/subscripts, make a bigger one,
// copy the old one, then shift appropriate members.
// ***Not yet implemented***
} // void ss_vector_insert_one_element()
void ss_vector_free_all_elements(void)
{ // Start at "vector".Walk the entire linked list, free each element's contents,
// free that element, then move to the next one.
// If there's an array of element addresses/subscripts, free it.
ss_vector* vec_element;
struct STRUCT_SS_VECTOR* next_one;
vec_element=vector;
while(vec_element->next_element!=(size_t) 0)
{ next_one=vec_element->next_element;
// free(vec_element->items) // don't forget to free these
free(vec_element->this_element);
vec_element=next_one;
next_one=vec_element->this_element;
}
// get rid of the last one.
// free(vec_element->items)
free(vec_element);
vector=NULL;
// If there's an array of element addresses/subscripts, free it now.
printf("\nall vector elements & contents freed\n");
} // void ss_vector_free_all_elements()
// defining some sort of struct, can be anything really
typedef struct APPLE_STRUCT
{ int id; // one of the data in the component
int other_id; // etc
struct APPLE_STRUCT* next_element;
} apple; // description of component
apple* init_apple(int id) // make a single component
{ apple* a; // ptr to component
a = malloc(sizeof(apple)); // memory for one component
a->id = id; // populate with data
a->other_id=id+10;
a->next_element=NULL;
// don't mess with aa->last_rec here
return a; // return pointer to component
};
int return_id_value(int i,apple* aa) // given ptr to component, return single data item
{ printf("was inserted as apple[%i].id = %i ",i,aa->id);
return(aa->id);
}
ss_vector* return_address_given_subscript(ss_vector* vec_element,int i)
// always make the first call to this subroutine with global vbl "vector"
{ ss_vector* local_vec_element=0;
// If there is a next element, recurse toward end-of-linked-list
if(vec_element->next_element!=(size_t)0)
{ if((vec_element->this_element->subscript==i))
{ return vec_element->this_element;}
local_vec_element= return_address_given_subscript(vec_element->next_element,i); // recurse to end of list
return local_vec_element;
}
else
{ if((vec_element->this_element->subscript==i)) // last element
{ return vec_element->this_element;}
// otherwise, none match
printf("reached end of list without match\n");
return (size_t) 0;
}
} // return_address_given_subscript()
int Test(void) // was "main" in the original example
{ ss_vector* local_vector;
local_vector=ss_init_vector(sizeof(apple)); // element "0"
for (int i = 1; i < 10; i++) // inserting items "1" thru whatever
{ local_vector=ss_vector_append(vector,i);}
// test search function
printf("\n NEXT, test search for address given subscript\n");
local_vector=return_address_given_subscript(vector,5);
printf("finished return_address_given_subscript(5) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(vector,0);
printf("finished return_address_given_subscript(0) with vector at %lu\n",(size_t)local_vector);
local_vector=return_address_given_subscript(vector,9);
printf("finished return_address_given_subscript(9) with vector at %lu\n",(size_t)local_vector);
// test single-element removal
printf("\nNEXT, test single element removal\n");
ss_vector_free_one_element(5,FALSE); // without renumbering subscripts
ss_vector_free_one_element(3,TRUE);// WITH renumbering subscripts
// ---end of program---
// don't forget to free everything
ss_vector_free_all_elements();
return 0;
}
See my previous post:
https://stackoverflow.com/questions/12548828/trying-to-save-command-line-arguments-in-a-dynamic-array-in-c
So, I want to return a char* array and the total number of elements in it. I couldn't figure a way out to get the total number of elements directly as I am unaware if I can just iterate through the array and stop when found NULL, is that possible? I mean does it give a NULL value if it reaches out of bounds of the arrray?
Otherwise I tried to create a struct as follows: struct name_vertex which has two variables a char** vertex_name and int curr_size and changed the code according and I get an error message
"expected =, ,, ;, asm or __attribute__ before unique_name" I don't understand what that means..and I checked my .h file and I am not missing any ; or , or =.
name_vertex* unique_name(char *argv[], int argc)
{
int i, j, curr_max, index, counter, flag;
char *temp;
name_vertex* structure_p;
/*
*Allocating of size 4 char*. Trying to make it a string pointer array
*Not sure if this is the correct way
*/
structure_p= malloc(4*sizeof(name_vertex));
/*
*Setting the first two elements of the allocation to
*the second and third element of the command line argv
*as you may know, the first one is the file name
*/
structure_p.vertex_name[0] = argv[1];
structure_p.vertex_name[1] = argv[2];
index=2; //Index variable for the dynamic array
curr_max = 3; //current total number of elements, 1 < total allocated as array element count starts at 0
structure_p.curr_size = 4; //Allocation amount int literal, count starts at 1
/*
*The outer loops starts at four as it is for the argv[] and the first one is irrelevent
*The outer loop essentially jumps 3 elements at a time as
*I am ultimately going to use this for a graph data structure that
*I am trying to create. So this array will store all the unique names of vertices
*so it makes setting the names when creating vertices easy
* Also will tell me how many unique vertices to create.
*The inner loop only runs once and it checks if there is a name that alreaady exist.
*It only takes into cosideration the first 2 out of the 3 that are considered in the outer loop
*The the third one is going to be an integer value for the edge weight of two vertices
*so don't need it right now
*/
for(i=4; i<argc; i+=3)
{
for(j=0; j<1; j++)
{
flag = 0;
counter = 0;
//Compare first argv[i] with all the elements
//of vertex_name array
while(counter<index)
{
if(strcmp(argv[i], stucture_p.vertex_name[counter]) == 0)
{
flag = 1;
break;
}
counter++;
}
//If no match found, allocates some memory
//adds the element to vertex_name
//Increments index, curr_size, curr_max
if(flag == 0)
{
temp = realloc(structure_p, (structure_p.curr_size + 2) * sizeof(name_vertex)); //CHECK THE SYNTAX, WANNA ADD 2 MORE ELEMENTS TO ARRAY
structure_p = temp;
structure_p.vertex_name[index] = argv[i];
index++;
structure.curr_size +=2;
curr_max +=2;
}
flag = 0; //reset flag
counter = 0; //reset counter
//Do the same comparison as above, but
//this time its argv[i+1] compared
while(counter < index)
{
if(strcmp(argv[i+1], structure_p.vertex_name[j]) ==0)
{
flag = 1;
break;
}
counter++;
}
//If no match found, same process as before
//Increment index, curr_size, curr_max variables
if(flag == 0)
{
temp = realloc(structure.vertex_name, (structure.curr_size + 2) * sizeof(name_vertex)); //CHECK THE SYNTAX, WANNA ADD 2 MORE ELEMENTS TO ARRAY
structure_p.vertex_name = temp;
structure_p.vertex_name[index] = argv[i+1];
index++;
structure_p.curr_size += 2;
curr_max +=2;
}
}
}
//Returning the new array
return structure_p;
}
name_vertex is probably undefined or defined differently from how you are trying to use it.
Option 1:
typedef struct
{
// yada, yada, yada
} name_vertex;
name_vertex unique_name(/*etc*/)
{
// ...
name_vertex structure;
// ...
return structure;
}
Option 2:
struct name_vertex
{
// yada, yada, yada
};
struct name_vertex unique_name(/*etc*/)
{
// ...
struct name_vertex structure;
// ...
return structure;
}
Option 3:
typedef struct name_vertex
{
// yada, yada, yada
} name_vertex;
name_vertex unique_name1(/*etc*/)
{
// ...
name_vertex structure;
// ...
return structure;
}
struct name_vertex unique_name2(/*etc*/)
{
// ...
struct name_vertex structure;
// ...
return structure;
}
As for iterating over an array with a pointer, when your pointer goes outside of the array boundaries, it does not become NULL. You either have to know the array size or the pointer to its first or last element (whichever is appropriate) or the array itself must contain a special value in its first/last element, so you know when you have finished processing the array. No magic. You have to manage your pointers and array indices yourself.
C does not support returning complex data types from functions. Instead, you need to pass in a pointer to the already-allocated structure and have the function fill it in, or have the function return a newly-allocated pointer.
Option 1:
void unique_name(char *argv[], int argc, name_vertex* structure_p) {
// fill structure_p using structure_p->field = value.
}
// In the caller:
name_vertex* structure_p = malloc(sizeof(name_vertex));
unique_name(argv, argc, structure_p);
// use structure_p
free(structure_p);
Option 2:
name_vertex* unique_name(char *argv[], int argc) {
// ...
name_vertex* structure_p = malloc(sizeof(name_vertex));
// fill structure_p using structure_p->field = value.
return structure_p;
}
// In the caller:
name_vertex* structure_p = unique_name(argv, argc);
// use structure_p
free(structure_p);