I have a block of pointers to some structs which I want to handle (i.e. free) separately. As an example below there is an integer double-pointer which should keep other pointers to integer. I then would like to free the second of those integer pointers (in my program based on some filterings and calculations). If I do so however, I should keep track of int-pointers already set free so that when I iterate over the pointers in the double-pointer I do not take the risk of working with them further. Is there a better approach for solving this problem (in ANSI-C) without using other libs (e.g. glib or alike)?
Here is a small simulation of the problem:
#include <stdio.h>
#include <stdlib.h>
int main() {
int **ipp=NULL;
for (int i = 0; i < 3; i++) {
int *ip = malloc(sizeof (int));
printf("%p -> ip %d\n", ip, i);
*ip = i * 10;
if ((ipp = realloc(ipp, sizeof (int *) * (i + 1)))) {
ipp[i] = ip;
}
}
printf("%p -> ipp\n", ipp);
for (int i = 0; i < 3; i++) {
printf("%d. %p %p %d\n", i, ipp + i, *(ipp+i), **(ipp + i));
}
// free the middle integer pointer
free(*(ipp+1));
printf("====\n");
for (int i = 0; i < 3; i++) {
printf("%d. %p %p %d\n", i, ipp + i, *(ipp+i), **(ipp + i));
}
return 0;
}
which prints something like
0x555bcc07f2a0 -> ip 0
0x555bcc07f6f0 -> ip 1
0x555bcc07f710 -> ip 2
0x555bcc07f6d0 -> ipp
0. 0x555bcc07f6d0 0x555bcc07f2a0 0
1. 0x555bcc07f6d8 0x555bcc07f6f0 10
2. 0x555bcc07f6e0 0x555bcc07f710 20
====
0. 0x555bcc07f6d0 0x555bcc07f2a0 0
1. 0x555bcc07f6d8 0x555bcc07f6f0 0
2. 0x555bcc07f6e0 0x555bcc07f710 20
Here I have freed the middle int-pointer. In my actual program I create a new block for an integer double-pointer, iterate over the current one, create new integer pointers and copy the old values into it, realloc the double-pointer block and append the new pointer to it, and at the end free the old block and all it's containing pointers. This is a bit ugly, and resource-consuming if there is a huge amount of data, since I have to iterate over and create and copy all the data twice. Any help is appreciated.
Re:
"This is a bit ugly, and resource-consuming if there is a huge amount of data, since I have to iterate over and create and copy all the data
twice. Any help is appreciated."
First observation: It is not necessary to use realloc() when allocating new memory on a pointer that has already been freed. realloc() is useful when needing to preserve the contents in a particular area of memory, while expanding its size. If that is not a need (which is not in this case) malloc() or calloc() are sufficient. #Marco's suggestion is correct.
Second observation: the following code snippet:
if ((ipp = realloc(ipp, sizeof (int *) * (i + 1)))) {
ipp[i] = ip;
}
is a potential memory leak. If the call to realloc()_ fails, the pointer ipp will be set to null, making the memory location that was previously allocated becomes orphaned, with no way to free it.
Third observation: Your approach is described as needing:
Array of struct
dynamic memory allocation of a 2D array
need to delete elements of 2D array, and ensure they are not referenced once deleted
need to repurpose deleted elements of 2D array
Your initial reaction in comments to considering using an alternative approach notwithstanding, Linked lists are a perfect fit to address the needs stated in your post.
The fundamental element of a Linked List uses a struct
Nodes (elements) of a List are dynamically allocated when created.
Nodes of a List are not accessible to be used once deleted. (No need to track)
Once the need exists, a new node is easily created.
Example struct follows. I like to use a data struct to contain the payload, then use an additional struct as the conveyance, to carry the data when building a Linked List:
typedef struct {//to simulate your struct
int dNum;
char unique_name[30];
double fNum;
} data_s;
typedef struct Node {//conveyance of payload, forward and backward searchable
data_s data;
struct Node *next; // Pointer to next node in DLL
struct Node *prev; // Pointer to previous node in DLL
} list_t;
Creating a list is done by creating a series of nodes as needed during run-time. Typically as records of a database, or lines of a file are read, and the elements of the table record (of element of the line in a file) are read into and instance of the data part of the list_s struct. A function is typically defined to do this, for example
void insert_node(list_s **head, data_s *new)
{
list_s *temp = malloc(sizeof(*temp));
//insert lines to populate
temp.data.dNum = new.dNum;
strcpy(temp.data.unique_name, new.unique_name);
temp.fNum = new.fNum
//arrange list to accomdate new node in new list
temp->next = temp->prev = NULL;
if (!(*head))
(*head) = temp;
else//...or existing list
{
temp->next = *head;
(*head)->prev = temp;
(*head) = temp;
}
}
Deleting a node can be done in multiple ways. It the following example method a unique value of a node member is used, in this case unique_name
void delete_node_by_name(list_s** head_ref, const char *name)
{
BOOL not_found = TRUE;
// if list is empty
if ((*head_ref) == NULL)
return;
list_s *current = *head_ref;
list_s *next = NULL;
// traverse the list up to the end
while (current != NULL && not_found)
{
// if 'name' in node...
if (strcmp(current->data.unique_name, name) == 0)
{
//set loop to exit
not_found = FALSE;
//save current's next node in the pointer 'next' /
next = current->next;
// delete the node pointed to by 'current'
delete_node(head_ref, current);
// reset the pointers
current = next;
}
// increment to next node
else
{
current = current->next;
}
}
}
Where delete_node() is defined as:
void delete_node(list_t **head_ref, list_t *del)
{
// base case
if (*head_ref == NULL || del == NULL)
return;
// If node to be deleted is head node
if (*head_ref == del)
*head_ref = del->next;
// Change next only if node to be deleted is NOT the last node
if (del->next != NULL)
del->next->prev = del->prev;
// Change prev only if node to be deleted is NOT the first node
if (del->prev != NULL)
del->prev->next = del->next;
// Finally, free the memory occupied by del
free(del);
}
This link is an introduction to Linked Lists, and has additional links to other related topic to expand the types of lists that are available.
You could use standard function memmove and then call realloc. For example
Let's assume that currently there are n pointers. Then you can write
free( *(ipp + i ) );
memmove( ipp + i, ipp + i + 1, ( n - i - 1 ) * sizeof( *pp ) );
*( ipp + n - 1 ) = NULL; // if the call of realloc will not be successfull
// then the pointer will be equal to NULL
int **tmp = realloc( ipp, ( n - 1 ) * sizeof( *tmp ) );
if ( tmp != NULL )
{
ipp = tmp;
--n;
}
else
{
// some other actions
}
Related
So first of all i have 2 linked lists one inside the other (like a matrix) and i made a function to delete an entire node. It seems to be freeing but when i print the value t it outputs weird characters.
Here are the structs used inside the list
typedef struct
{
char codigo[LEN_CODIGO + 1];
char partidaID[LEN_ID + 1];
char chegadaID[LEN_ID + 1];
Data datapartida;
Tempo horapartida;
Tempo duracao;
Data datachegada;
Tempo horachegada;
int capacidade;
int ocupacao;
} Voo;
typedef struct r
{
char *codReserva;
int nPassangeiros;
struct r *next;
} *ListaReservas;
typedef struct node
{
Voo voo;
ListaReservas nodeReservas; /*this is the head to a list inside this list*/
struct node *next;
} *Node;
in the following function i pretend to delete one node and all the nodes of nodeReservas in it, like deleting an entire column of a matrix.
Node eliminaNode(Node head, char codigo[])
{
Node n, prev;
ListaReservas r, temp;
for (n = head, prev = NULL; n != NULL; prev = n, n = n->next)
{
if (strcmp(n->voo.codigo, codigo) == 0) /*If it's the correct node*/
{
if (n == head)
head = n->next;
else
prev->next = n->next;
/*deletes nodeReservas*/
r = n->nodeReservas;
temp = r;
while(temp != NULL)
{
temp = temp->next;
free(r->codReserva);
free(r);
r= temp;
}
/*deletes the whole node*/
free(n);
}
}
return head;
}
I then use this code to tell me which reservations still exist in a node
for (r=n->nodeReservas; r != NULL; r= r->next)
printf("%s %d\n", r->codReserva, r->nPassangeiros);
For example after adding 3 reservations to lets say Node X and deleting the Node with the reservations with eliminaNode(headofList, X). After recreating the node with that same name 'X' and printing its reservations, instead of getting a empty line i get this:
-725147632
�+���U -725147632
#+���U -725147632
So what is the free() freeing? Is this happening because Lista reservas is a pointer?
free() returns the allocated block to the heap where it may be re-used for subsequent allocation requests. It does not (how could it?) modify the pointer to that block and if you retain such a pointer and re-use it after de-allocation, nothing good will happen.
What you should do is set the pointer to NULL (or a valid pointer such as that of the new next node) immediately after freeing the block so that you retain no reference to the now invalid block:
free(r->codReserva);
r->codReserva = NULL ;
free(r);
r= temp;
}
/*deletes the whole node*/
free(n);
n = NULL ;
Doing that should be a habit in C code. You could make things simpler by creating a function say:
void dealloc( void** ref )
{
free( *ref ) ;
*ref = NULL ;
}
Then instead of calling free( n ) you would call dealloc( &n ) for example.
There are other serious issues with this code. For example the code involving temp is somewhat over-complicated (and any code with a variable temp should raise alarm bells - you have given it scope over the entire function, and used it for more than one purpose - that is not good practice). Consider:
r = n->nodeReservas;
while( r != NULL)
{
ListaReservas new_next= r->next;
free(r->codReserva);
r->codReserva = NULL ;
free(r);
r = new_next;
}
There new_next is very localised (literally "temporary") and named appropriately so it is clear what it is. The next problem is that having assigned the value r you do nothing with it! It is presumably n->nodeReservas that you intended to update not r? Perhaps:
ListaReservas r = n->nodeReservas;
while( r != NULL)
{
ListaReservas new_next= r->next;
free(r->codReserva);
r->codReserva = NULL ;
free(r);
n->nodeReservas = new_next;
}
Note in each case the declaration of temporary variables at point of first use, to give the narrowest scope. Note that r is also temporary. However here it is not truly necessary - it is just a shorthand for n->nodeReservas - personally I'd eradicate it - if only to avoid exactly teh bug described above. Having multiple references to a single allocation is a recipe for bugs. Instead:
while( n->nodeReservas != NULL)
{
ListaReservas new_next = n->nodeReservas->next;
free(n->nodeReservas->codReserva);
n->nodeReservas->codReserva = NULL ;
free(n->nodeReservas);
n->nodeReservas = new_next;
}
I cannot say for sure there are not other bugs - that is just the part that had an obvious "code smell".
I don't understand why my program seg faults at this line: if ((**table->table).link == NULL){ I seem to have malloc-ed memory for it, and I tried looking at it with gdb. *table->table was accessible and not NULL, but **table->table was not accessible.
Definition of hash_t:
struct table_s {
struct node_s **table;
size_t bins;
size_t size;
};
typedef struct table_s *hash_t;
void set(hash_t table, char *key, int value){
unsigned int hashnum = hash(key)%table->bins;
printf("%d \n", hashnum);
unsigned int i;
for (i = 0; i<hashnum; i++){
(table->table)++;
}
if (*(table->table) == NULL){
struct node_s n = {key, value, NULL};
struct node_s *np = &n;
*(table->table) = malloc(sizeof(struct node_s));
*(table->table) = np;
}else{
while ( *(table->table) != NULL){
if ((**table->table).link == NULL){
struct node_s n = {key, value, NULL};
struct node_s *np = &n;
(**table->table).link = malloc(sizeof(struct node_s));
(**table->table).link = np;
break;
}else if (strcmp((**table->table).key, key) == 0){
break;
}
*table->table = (**(table->table)).link;
}
if (table->size/table->bins > 1){
rehash(table);
}
}
}
I'm calling set from here:
for (int i = 0; i < trials; i++) {
int sample = rand() % max_num;
sprintf(key, "%d", sample);
set(table, key, sample);
}
Your hashtable works like this: You have bins bins and each bin is a linked list of key / value pairs. All items in a bin share the same hash code modulo the number of bins.
You have probably created the table of bins when you created or initialised the hash table, something like this:
table->table = malloc(table->bins * sizeof(*table->table);
for (size_t i = 0; i < table->bins; i++) table->table[i] = NULL;
Now why does the member table have two stars?
The "inner" star means that the table stores pointers to nodes, not the nodes themselves.
The "outer" start is a handle to allocated memory. If your hash table were of a fixed size, for example always with 256 bins, you could define it as:
struct node_s *table[256];
If you passed this array around, it would become (or "decay into") a pointer to its first element, a struct node_s **, just as the array you got from malloc.
You access the contents of the l´bins via the linked lists and the head of linked list i is table->table[i].
You code has other problems:
What did you want to achieve with (table->table)++? This will make the handle to the allocated memory point not to the first element but tho the next one. After doing that hashnum times, *table->table will now be at the right node, but you will have lost the original handle, which you must retain, because you must pass it to free later when you clean up your hash table. Don't lose the handle to allocated memory! Use another local pointer instead.
You create a local node n and then make a link in your linked list with a pointer to that node. But the node n will be gone after you leave the function and the link will be "stale": It will point to invalid memory. You must also create memory for the node with malloc.
A simple implementation of your has table might be:
void set(hash_t table, char *key, int value)
{
unsigned int hashnum = hash(key) % table->bins;
// create (uninitialised) new node
struct node_s *nnew = malloc(sizeof(*nnew));
// initialise new node, point it to old head
nnew->key = strdup(key);
nnew->value = value;
nnew->link = table->table[hashnum];
// make the new node the new head
table->table[hashnum] = nnew;
}
This makes the new node the head of the linked list. This is not ideal, because if you overwrite items, the new ones will be found (which is good), but the old ones will still be in the table (which isn't good). But that, as they say, is left as an exercise to the reader.
(The strdup function isn't standard, but widely available. It also creates new memory, which you must free later, but it ensures, that the string "lives" (is still valid) after you have ceated the hash table.)
Please not how few stars there are in the code. If there is one star too few, it is in hash_t, where you have typecasted away the pointer nature.
For school, I need to write a program that uses my own implementation of malloc and free. I need to be able to report on all the chunks of memory in my 'heap', whether it's allocated or not. I feel like I've written good code to do so, but evidently not. The first few times I ran it, the report kept reporting on the same address forever. While trying to debug that, there came a point that the program wouldn't even let me start allocate space to use as my 'heap', it would just get a segmentation fault and quit. Any pointers on where I'm going wrong, or even to clean up my code at all, would be super helpful.
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#define WORDSIZE 8
#define ALLOCMAGIC 0xbaddecaf
#define FREEMAGIC 0xdeadbeef
typedef struct __header_t {
size_t size;
int magic;
} header_t;
typedef struct __node_t {
size_t size;
struct __node_t *next;
} node_t;
node_t *head = NULL;
// Find the free node that occurs just before the given node.
node_t *findLastFree(node_t * node) {
// Initialize some pointers to traverse the free node linked list;
node_t *lastFree = head;
node_t *nextFree = lastFree->next;
// Traverse linked list until the last node's pointer is pointed to NULL,
// meaning the end of the list.
while (nextFree != NULL) {
// Check if target node is less than the next node, meaning the target node
// is between last and next. If so, then return last node.
if (node < nextFree) {
return lastFree;
}
lastFree = nextFree;
nextFree = lastFree->next;
}
// If we have reached the end of the list and the target node is still greater
// than the last node, return last node.
return lastFree;
}
// If the given pointer is allocated, deallocate the space and coalesce the free
// node list.
void myFree(void *ptr) {
// Initialize some free node pointers and assert that the given pointer is
// the beginning of allocated space.
node_t *lastFree;
node_t *nextFree;
node_t *newFree;
header_t *block = ((header_t *) ptr) - 1;
assert(block->magic == ALLOCMAGIC);
// Set this block's signal to free space
block->magic = FREEMAGIC;
// Typecast the block into a free node and set it's size.
size_t size = block->size + sizeof(header_t);
newFree = (node_t *) block;
newFree->size = size;
// Check if node is before the first free node. If so set the new node as
// the new head. If not, then handle node as it occurs after head.
if (newFree < head) {
nextFree = head;
// Check if new node ends at the start of head. If so, merge them
// into a single free node. Else point the new node at the previous head.
// Either way, set new free as the new head.
if ((newFree + newFree->size) == head) {
newFree->next = head->next;
newFree->size = newFree->size + head->size;
} else {
newFree->next = head;
}
head = newFree;
} else {
// Set the free nodes for before and after the new free node.
lastFree = findLastFree(newFree);
nextFree = lastFree->next;
// Check if new node is the last node. If so, point the previous final
// node at the new node and point the new node at NULL.
if (nextFree == NULL) {
lastFree->next = newFree;
newFree->next = NULL;
}
// Check if end of new node is touching next node. If so, merge them
// into a single free node. Else point new free and next free.
if ((newFree + newFree->size) == nextFree) {
newFree->next = nextFree->next;
newFree->size = newFree->size + nextFree->size;
} else {
newFree->next = nextFree;
}
// Check if start of new node is touching last free node. If so, merge
// them into a single free node. Else point last's next to new free.
if ((lastFree + lastFree->size) == newFree) {
lastFree->next = newFree->next;
lastFree->size = lastFree->size + newFree->size;
} else {
lastFree->next = newFree;
}
}
}
// Split the given free node to fit the given size. Create a new node at the
// remainder and rearrange the free list to accomodate.
void splitBlock(node_t *node, size_t size) {
// Create a new pointer at the end of the requested space.
void *newBlock = node + size;
// Set the bits of the new space as if it were allocated then freed.
header_t *hptr = (header_t *) newBlock;
hptr->size = (node->size - size - sizeof(header_t));
hptr->magic = FREEMAGIC;
// Typecast the new space into a node pointer. Reinsert it into the free
// node list.
node_t *newFree = (node_t *) newBlock;
newFree->size = node->size - size;
newFree->next = node->next;
node_t *lastFree = findLastFree(newFree);
lastFree->next = newFree;
}
// Find a free node that can fit the given size. Split the node so no space is
// wasted. If no node can fit requested size, increase the heap size to accomodate.
void *findFirstFit(size_t size) {
// Create a node pointer to traverse the free node list.
node_t *node = head;
// Traverse the list until the end is reached.
while(node != NULL) {
// Check if the node can accomodate the requested size.
if (node->size >= size) {
// Split the current node at the requested size and return a pointer
// to the start of the requested space.
splitBlock(node, size);
return (void *) node;
}
node = node->next;
}
// No free space could fit requested size, so request more space at the end
// of the heap.
void *newspace = sbrk(size);
assert(newspace >= 0);
return newspace;
}
// Allocate a block of space for the given size and return a pointer to the start
// of the freed space.
void *myMalloc(size_t need) {
// Round the given size up to the next word size. Add the size of a header to
// the amount actually needed to allocate.
need = (need + WORDSIZE - 1) & ~(WORDSIZE - 1);
size_t actual = need + sizeof(header_t);
// Find a free node that can accomodate the given size. Check it is valid.
void *firstfit = findFirstFit(actual);
assert(firstfit >= 0);
// Create a header for the newly allocated space.
header_t *hptr = (header_t *) firstfit;
hptr->magic = ALLOCMAGIC;
hptr->size = need;
return (void *) (hptr + 1);
}
// Print a report on the space starting at the given pointer. Return a pointer to
// the start of the next block of space.
void *reportAndGetNext(void *ptr) {
void *nextptr;
header_t *hptr = (header_t *) ptr;
// Check if the pointer is pointing to allocated space.
if (hptr->magic == ALLOCMAGIC) {
// Report the characteristics of the current block.
printf("%p is ALLOCATED starting at %p and is %zd bytes long.\n", hptr, (hptr + 1), hptr->size);
// Set the next pointer to be returned.
nextptr = hptr + hptr->size + sizeof(header_t);
} else {
// Cast the pointer as a free node. Set the next pointer to be returned.
node_t *free = (node_t *) ptr;
nextptr = free + free->size;
// Report the characteristics of the current block.
printf("%p is FREE for %zd bytes.\n", hptr, free->size);
}
return nextptr;
}
// Report on all blocks of space contained within the heap space, starting at the
// given pointer.
void report(void* startheap) {
void *ptr = startheap;
void *end = sbrk(0);
int count = 50;
printf("Current Status of Heap:\n");
while (ptr != NULL && count > 0) {
ptr = reportAndGetNext(ptr);
count = count - 1;
}
printf("Heap Length: %zd \n", (end - startheap));
}
int main(void) {
void *start = sbrk(4096);
assert(start >= 0);
head = (node_t *) start;
head->size = 4096;
head->next = NULL;
printf("Allocating block 1");
void *ptr1 = myMalloc(26);
void *ptr2 = myMalloc(126);
report(start);
myFree(ptr1);
myFree(ptr2);
return 0;
}
The first obvious error I see is with pointer arithmentic. SplitBlock is trying to split size bytes from the front of a block, but when you do:
void splitBlock(node_t *node, size_t size) {
// Create a new pointer at the end of the requested space.
void *newBlock = node + size;
Your newBlock pointer is actually size * sizof(node_t) bytes into the block -- which may well be past the end of the block. You need to cast node to a char * before doing pointer arithmetic with it if you want byte offsets. However, you may then run into alignment issues...
For part of my C data structures assignment, I am tasked with taking an array of pointers to nodes of 2 doubly linked lists (one representing the main service queue, and the other representing a "bucket" of buzzers ready to be reused or used for the first time in the queue), doubling the size, while keeping the original contents in tact. The idea is that each node has an ID associated which corresponds to the number index of the pointer array map. So for example, the pointer in index 3 will always point to the node whose ID is 3. The boolean inQ is for something unrelated to this issue.
I've written most of the code, but it seems to be functioning incorrectly (it changes all the original pointers to the last node in the list before the array resizing) So, since the starting size of the array is 10 elements, when I print out the contents after the function, it displays 9 9 9 9 9 9 9 9 9 9.
Here are the structs im using:
typedef struct node {
int id;
int inQ;
struct node *next;
struct node *prev;
}NODE;
typedef struct list
{
NODE *front;
NODE *back;
int size;
} LIST;
//referred to as SQ in the separate header file
struct service_queue
{
LIST *queue;
LIST *bucket;
NODE **arr;
int arrSize;
int maxID;
};
Here is the function in question:
SQ sq_double_array(SQ *q)
{
NODE **arr2 = malloc(q->arrSize * 2 * sizeof(NODE*));
int i;
//fill the first half of the new array with the node pointers of the first array
for (i = 0; i < q->arrSize; i++)
{
arr2[i] = malloc(sizeof(NODE));
if (i > 0)
{
arr2[i - 1]->next = arr2[i];
arr2[i]->prev = arr2[i - 1];
}
arr2[i]->id = q->arr[i]->id;
arr2[i]->inQ = q->arr[i]->inQ;
arr2[i]->next = q->arr[i]->next;
arr2[i]->prev = q->arr[i]->prev;
}
//fill the second half with node pointers to the new nodes and place them into the bucket
for (i = q->arrSize; i < q->arrSize * 2; i++)
{
//Point the array elements equal to empty nodes, corresponding to the inidicies
arr2[i] = malloc(sizeof(NODE));
arr2[i]->id = i;
arr2[i]->inQ = 0;
//If the bucket is empty (first pass)
if (q->bucket->front == NULL)
{
q->bucket->front = arr2[i];
arr2[i]->prev = NULL;
arr2[i]->next = NULL;
q->bucket->back = arr2[i];
}
//If the bucket has at least 1 buzzer in it
else
{
q->bucket->back = malloc(sizeof(NODE));
q->bucket->back->next = arr2[i];
q->bucket->back = arr2[i];
q->bucket->back->next = NULL;
}
}
q->arrSize *= 2;
q->arr = arr2;
return *q;
}
Keep in mind this must only be done in c, which is why im not using 'new'
You could use the realloc function:
void *realloc(void *ptr, size_t size);
Quoted from the man pages:
The realloc() function changes the size of the memory block pointed to
by ptr to size bytes. The
contents will be unchanged in the range from the start of the region up to the minimum of the old
and new sizes. If the new size is larger than the old size, the added memory will not be initial‐
ized. If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if
size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr). Unless ptr
is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc(). If the
area pointed to was moved, a free(ptr) is done.
How is an array of structures created in C without knowing the eventual amount of structures to be stored in the array?
I would like to loop in a for loop, create a tempStruct set its variables, add this to an array and then loop again, creating a new tempStruct and adding it to the array.
Im having some issues wrapping my head around how this is done in C while trying to relate from objective C.
Dynamically allocated arrays (using malloc) can be reallocated (using realloc).
Therefore the solution will look something like this:
malloc initial array (arbitrary size)
while still space in array, add structures
when array full, realloc to bigger size
goto 2
You could create a double linked list which points to parent and child
struct list{
list* next;
list* prev;
special_data* data;
}
easy to do and flexible
You can't create an array in C without knowing number of it's members up front.
Your options for adding are:
(Faster) Create new array with +1 element, copy entire array and add new element to the end
(Better) Create your own implementation of linked list (Linked list) which will dynamically allocate memory for each new member.
You can use malloc to create your structure.
Edit: The following demonstrates one way to do what you're asking by creating a linked list:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int data;
void* next;
} tempStruct;
#define NUM_STRUCTS 4
int main(void) {
tempStruct* cur_ptr;
tempStruct* root_ptr;
int i;
root_ptr = malloc(sizeof(tempStruct));
root_ptr -> data = 0;
root_ptr -> next = NULL;
cur_ptr = root_ptr;
for (i = 1; i < NUM_STRUCTS; i ++ ) {
tempStruct* new_ptr = malloc(sizeof(tempStruct));
new_ptr -> data = i;
new_ptr -> next = NULL;
cur_ptr -> next = new_ptr;
cur_ptr = cur_ptr -> next;
}
cur_ptr = root_ptr;
while (cur_ptr != NULL) {
printf("cur_ptr -> data = %d\n", cur_ptr -> data);
cur_ptr = cur_ptr -> next;
}
return 0;
}
If you really want to create something that acts more like an array, you'll need to allocate all your memory at the same time, using something like:
the_data = malloc(NUM_STRUCTS * sizeof(tempStruct);
Then you'll have to access the data with the dot operator (i.e. '.' (no quotes in your code).
struct foo {int bar;};
size_t i = 0, n = 8;
struct foo *arr = malloc(n * sizeof *arr);
int bar;
while ((bar = get_next_bar()) != -1) {
if (++i == n) { // no room for new element; expand array
arr = realloc(arr, n *= 2);
if (arr == NULL) abort; // see note below.
}
arr[i] = (struct foo){bar};
}
The number of assigned elements in the array is i+1. Don’t forget to free() the array when you’re done with it.
Note: In real programs you generally do not do p = realloc(p, s) directly. Instead you assign the result of realloc() to a new pointer, then do error detection & handling before clobbering your original pointer.