I'm used to PHP, but I'm starting to learn C. I'm trying to create a program that reads a file line by line and stores each line to an array.
So far I have a program that reads the file line by line, and even prints each line as it goes, but now I just need to add each line to an array.
My buddy last night was telling me a bit about it. He said I'd have to use a multidimensional array in C, so basically array[x][y]. The [y] part itself is easy, because I know the maximum amount of bytes that each line will be. However, I don't know how many lines the file will be.
I figure I can make it loop through the file and just increment an integer each time and use that, but I feel that there might be a more simple way of doing it.
Any ideas or even a hint in the right direction? I appreciate any help.
To dynamically allocate a 2D array:
char **p;
int i, dim1, dim2;
/* Allocate the first dimension, which is actually a pointer to pointer to char */
p = malloc (sizeof (char *) * dim1);
/* Then allocate each of the pointers allocated in previous step arrays of pointer to chars
* within each of these arrays are chars
*/
for (i = 0; i < dim1; i++)
{
*(p + i) = malloc (sizeof (char) * dim2);
/* or p[i] = malloc (sizeof (char) * dim2); */
}
/* Do work */
/* Deallocate the allocated array. Start deallocation from the lowest level.
* that is in the reverse order of which we did the allocation
*/
for (i = 0; i < dim1; i++)
{
free (p[i]);
}
free (p);
Modify the above method. When you need another line to be added do *(p + i) = malloc (sizeof (char) * dim2); and update i. In this case you need to predict the max numbers of lines in the file which is indicated by the dim1 variable, for which we allocate the p array first time. This will only allocate the (sizeof (int *) * dim1) bytes, thus much better option than char p[dim1][dim2] (in c99).
There is another way i think. Allocate arrays in blocks and chain them when there is an overflow.
struct _lines {
char **line;
int n;
struct _lines *next;
} *file;
file = malloc (sizeof (struct _lines));
file->line = malloc (sizeof (char *) * LINE_MAX);
file->n = 0;
head = file;
After this the first block is ready to use. When you need to insert a line just do:
/* get line into buffer */
file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1));
n++;
When n is LINE_MAX allocate another block and link it to this one.
struct _lines *temp;
temp = malloc (sizeof (struct _lines));
temp->line = malloc (sizeof (char *) * LINE_MAX);
temp->n = 0;
file->next = temp;
file = file->next;
Something like this.
When one block's n becomes 0, deallocate it, and update the current block pointer file to the previous one. You can either traverse from beginning single linked list and traverse from the start or use double links.
There's no standard resizable array type in C. You have to implement it yourself, or use a third-party library. Here's a simple bare-bones example:
typedef struct int_array
{
int *array;
size_t length;
size_t capacity;
} int_array;
void int_array_init(int_array *array)
{
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_free(int_array *array)
{
free(array->array);
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_push_back(int_array *array, int value)
{
if(array->length == array->capacity)
{
// Not enough space, reallocate. Also, watch out for overflow.
int new_capacity = array->capacity * 2;
if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX / sizeof(int))
{
int *new_array = realloc(array->array, new_capacity * sizeof(int));
if(new_array != NULL)
{
array->array = new_array;
array->capacity = new_capacity;
}
else
; // Handle out-of-memory
}
else
; // Handle overflow error
}
// Now that we have space, add the value to the array
array->array[array->length] = value;
array->length++;
}
Use it like this:
int_array a;
int_array_init(&a);
int i;
for(i = 0; i < 10; i++)
int_array_push_back(&a, i);
for(i = 0; i < a.length; i++)
printf("a[%d] = %d\n", i, a.array[i]);
int_array_free(&a);
Of course, this is only for an array of ints. Since C doesn't have templates, you'd have to either put all of this code in a macro for each different type of array (or use a different preprocessor such as GNU m4). Or, you could use a generic array container that either used void* pointers (requiring all array elements to be malloc'ed) or opaque memory blobs, which would require a cast with every element access and a memcpy for every element get/set.
In any case, it's not pretty. Two-dimensional arrays are even uglier.
Instead of an array here, you could also use a linked list, The code is simpler, but the allocation is more frequent and may suffer from fragmentation.
As long as you don't plan to do much random access (Which is O(n) here), iteration is about as simple as a regular array.
typedef struct Line Line;
struct Line{
char text[LINE_MAX];
Line *next;
};
Line *mkline()
{
Line *l = malloc(sizeof(Line));
if(!l)
error();
return l;
}
main()
{
Line *lines = mkline();
Line *lp = lines;
while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){
lp->next = mkline();
lp = lp->next;
}
lp->next = NULL;
}
If you are using C you will need to implement the resizing of the array yourself. C++ and the SDL has this done for you. It is called a vector. http://www.cplusplus.com/reference/stl/vector/
While a multidimensional array can solve this problem, a rectangular 2D array would not really be the natural C solution.
Here is a program that initially reads the file into a linked list, and then allocates a vector of pointers of the right size. Each individual character does then appear as array[line][col] but in fact each row is only as long as it needs to be. It's C99 except for <err.h>.
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct strnode {
char *s;
struct strnode *next;
} strnode;
strnode *list_head;
strnode *list_last;
strnode *read1line(void) {
char space[1024];
if(fgets(space, sizeof space, stdin) == NULL)
return NULL;
strnode *node = malloc(sizeof(strnode));
if(node && (node->s = malloc(strlen(space) + 1))) {
strcpy(node->s, space);
node->next = NULL;
if (list_head == NULL)
list_head = node;
else
list_last->next = node;
list_last = node;
return node;
}
err(1, NULL);
}
int main(int ac, char **av) {
int n;
strnode *s;
for(n = 0; (s = read1line()) != NULL; ++n)
continue;
if(n > 0) {
int i;
strnode *b;
char **a = malloc(n * sizeof(char *));
printf("There were %d lines\n", n);
for(b = list_head, i = 0; b; b = b->next, ++i)
a[i] = b->s;
printf("Near the middle is: %s", a[n / 2]);
}
return 0;
}
You can use the malloc and realloc functions to dynamically allocate and resize an array of pointers to char, and each element of the array will point to a string read from the file (where that string's storage is also allocated dynamically). For simplicity's sake we'll assume that the maximum length of each line is less than M characters (counting the newline), so we don't have to do any dynamic resizing of individual lines.
You'll need to keep track of the array size manually each time you extend it. A common technique is to double the array size each time you extend, rather than extending by a fixed size; this minimizes the number of calls to realloc, which is potentially expensive. Of course that means you'll have to keep track of two quantities; the total size of the array and the number of elements currently read.
Example:
#define INITIAL_SIZE ... // some size large enough to cover most cases
char **loadFile(FILE *stream, size_t *linesRead)
{
size_t arraySize = 0;
char **lines = NULL;
char *nextLine = NULL;
*linesRead = 0;
lines = malloc(INITIAL_SIZE * sizeof *lines);
if (!lines)
{
fprintf(stderr, "Could not allocate array\n");
return NULL;
}
arraySize = INITIAL_SIZE;
/**
* Read the next input line from the stream. We're abstracting this
* out to keep the code simple.
*/
while ((nextLine = getNextLine(stream)))
{
if (arraySize <= *linesRead)
{
char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp);
if (tmp)
{
lines = tmp;
arraySize *= 2;
}
}
lines[(*linesRead)++] = nextLine;
)
return lines;
}
Related
I have a 2d pointer array:
char **fields = calloc(1, sizeof(char *));
I add to it different strings, like this:
if(i > 0) fields = realloc(fields, (i+1) * sizeof(char *));
fields[i] = calloc(size, sizeof(char));
I then use memcpy into the fields[i] the desired string.
At the end of the program, when I try to free fields, I do it like this:
int j=0
while(fields != NULL && fields[j]){
free(fields[j]);
j++;
}
free(fields);
The program inserts 4 strings into fields.
The first string frees as expected, however on the second iteration of the loop (j=1) the program stops and outputs the error: free(): invalid pointer
EDIT: I made a short program with the same problem:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
char **fields = calloc(1, sizeof(char *));
int fieldsIndex = 0,i=0;
while (i<4) {
if(fieldsIndex > 0){
fields = realloc(fields, (fieldsIndex + 1) * sizeof(char *));
fields[fieldsIndex] =NULL;
printf("amount of field places: %d\n", (fieldsIndex + 1));
}
fields[fieldsIndex] = calloc(8, sizeof(char));
fields[fieldsIndex] = "88888888";
fieldsIndex++;
i++;
}
int j=0;
for(j=0; j<i; j++){
printf("field: %s\n", fields[j]);
free(fields[j]);
}
free(fields);
return 0;
}
Can anyone help?
Addressing mainly the MRE.
The main problems are around this line:
fields[fieldsIndex] = "88888888";
It's not right for two reasons:
Firstly you need one more element in the array for the null byte.
Secondly, you make the fields[fieldsIndex] pointers point to string literals, it not only causes a memory leak, but also those string literals are usually stored in a readonly section of memory, either way the behavior freeing a pointer pointing to a string literal is undefined.
You need to copy the strings to the memory you just allocated. Using memcpy should work as long as you reserve enough memory as mentioned in the previous point, a cleaner way would be to use strdup.
Another issue is if(fieldsIndex > 0) because then fields[0] will not have allocated memory.
Some other notes, if you know the amount of strings (i < 4) you shouldn't need to realloc, just allocate space for all the pointers in the first calloc* (assuming that is not brought about by the construction of the MRE) , also i and fieldsIndex seem to be redundant.
Here is a demo keeping realloc (as it's tangential to the OP):
int main()
{
char **fields = NULL;
char **tempfields; // I advise the use of an auxiliary pointer for reallocation
int fieldsIndex = 0;
while (fieldsIndex < 4)
{
tempfields = realloc(fields, (fieldsIndex + 1) * sizeof *fields); //*
if (!tempfields)
{
// handle the allocation error appropriately
}
fields = tempfields;
printf("amount of field places: %d\n", (fieldsIndex + 1));
fields[fieldsIndex] = strdup("88888888");
// Or
// fields[fieldsIndex] = calloc(9, sizeof **fields); // check return
// strcpy(fields[fieldsIndex], "88888888");
fieldsIndex++;
}
// With int iterator
int j = 0;
for (j = 0; j < fieldsIndex; j++)
{
printf("field: %s\n", fields[j]);
free(fields[j]);
}
free(fields);
}
Or with a sentinel element in fields:
Live demo
// With sentinel
tempfields = realloc(fields, (fieldsIndex + 1) * sizeof *fields);
if (!tempfields)
{
// handle the allocation error appropriately
}
fields = tempfields;
fields[fieldsIndex] = NULL;
while (*fields)
{
printf("field: %s\n", *fields);
free(*fields);
fields++;
}
free(tempfields);
I'm having trouble accessing my double pointer struct within my structure.
typedef struct monster
{
char *name;
char *element;
int population;
} monster;
typedef struct region
{
char *name;
int nmonsters;
int total_population;
monster **monsters;
} region;
region **
readRegion (FILE * infile, int *regionCount)
{
region **temp;
char garbage[50];
char garbage2[50];
char rName[50];
int monsterNum;
fscanf (infile, "%d %s", regionCount, garbage);
temp = malloc (*regionCount * sizeof (region *));
for (int i = 0; i < *regionCount; i++)
{
fscanf (infile, "%s%d%s", rName, &monsterNum, garbage2);
temp[i] = createRegion (inFile, rName, monsterNum);
}
return temp;
}
region *
createRegion (FILE * inFile, char *rName, int nMonsters)
{
region *r = malloc (sizeof (region));
char rMonster[50];
int rLength;
r->name = malloc ((strlen (rName) + 1) * sizeof (char));
strcpy (r->name, rName);
r->nmonsters = nMonsters;
for (int i = 0; i < nMonsters; i++)
{
r->monsters.name = (nMonsters * sizeof (r->monsters.name));
fscanf (in, "%s", rMonster);
r->monsters.name = malloc ((strlen (rMonster) + 1) * sizeof (char));
strcpy (r->monsters.name, rMonster);
}
return r;
}
Hopefully my code is readable where you can get the jist of what im trying to do with the monster** monsters pointer in my region struct. Any explnation on how to access and use a double struct pointer within a structure would help.
I've tried to clean up and re-interpret your createRegion to read a lot more like traditional C:
region* createRegion(FILE * inFile, char *rName, int nMonsters) {
region *r = malloc(sizeof(region));
char buffer[1024];
r->name = strdup(rName);
r->nmonsters = nMonsters;
r->monsters = calloc(nMonsters, sizeof(monster*));
for (int i=0; i < nMonsters; i++) {
// Allocate a monster
monster *m = malloc(sizeof(monster));
fscanf(in,"%s", buffer);
m->name = strdup(buffer);
m->element = NULL; // TBD?
m->population = 1; // TBD?
// Put this monster in the monsters pointer array
r->monsters[i] = m;
}
return r;
}
Where the key here is you must allocate the monsters. Here it's done individually, but you could also allocate as a slab:
region* createRegion(FILE * inFile, char *rName, int nMonsters) {
region *r = malloc(sizeof(region));
char buffer[1024];
r->name = strdup(rName);
r->nmonsters = nMonsters;
// Make a single allocation, which is usually what's returned from
// C functions that allocate N of something
monsters* m = calloc(nMonsters, sizeof(monster));
// Normally you'd see a definition like m in the region struct, but
// that's not the case here because reasons.
r->monsters = calloc(nMonsters, sizeof(monster*));
for (int i=0; i < nMonsters; i++) {
fscanf(in,"%s", buffer);
m[i].name = strdup(buffer);
m[i].element = NULL; // TBD?
m[i].population = 1; // TBD?
// Put this monster in the monsters pointer array
r->monsters[i] = &m[i];
}
return r;
}
Note I've switched out the highly quirky strlen-based code with a simple strdup call. It's also very odd to see sizeof(char) used since on any computer you're likely to interface with, be it an embedded microcontroller or a fancy mainframe, that will be 1.
Inasmuch as you are asking about accessing a double pointer inside a structure, I think your issue is mostly about this function:
region *
createRegion (FILE * inFile, char *rName, int nMonsters)
{
region *r = malloc (sizeof (region));
char rMonster[50];
int rLength;
r->name = malloc ((strlen (rName) + 1) * sizeof (char));
strcpy (r->name, rName);
r->nmonsters = nMonsters;
[Point A]
So far, so good, but here you start to run off the rails.
for (int i = 0; i < nMonsters; i++)
{
r->monsters.name = (nMonsters * sizeof (r->monsters.name));
Hold on. r->monsters has type monster **, but you are trying to access it as if it were a monster. Moreover, r->monsters has never had a value assigned to it, so there's very little indeed that you can safely do with it.
I think the idea must be that r->monsters is to be made to point to a dynamically-allocated array of monster *, and that the loop allocates and initializes the monsters, and writes pointers to them into the array.
You need to allocate space for the array, then, but you only need or want to allocate the array once. Do that before the loop, at Point A, above, something like this:
r->monsters = malloc(nMonsters * sizeof(*r->monsters)); // a monster **
Then, inside the loop, you need to allocate space for one monster, and assign a pointer to that to your array:*
r->monsters[i] = malloc(sizeof(*r->monsters[i])); // a monster *
Then, to access the actual monster objects, you need to either dererference and use the direct member selection operator (.) ...
(*r->monsters[i]).name = /* ... */;
... or use the indirect member selection operator (->) ...
r->monsters[i]->name = /* ... */;
. The two are equivalent, but most C programmers seem to prefer the latter style.
At this point, however, I note that in the body of the loop, you seem to be trying to make two separate assignments to the monster's name member. That doesn't make sense, and the first attempt definitely doesn't make sense, because you seem to be trying to assign a number to a pointer.
fscanf (in, "%s", rMonster);
r->monsters.name = malloc ((strlen (rMonster) + 1) * sizeof (char));
strcpy (r->monsters.name, rMonster);
Using the above, then, and taking advantage of the fact that sizeof(char) is 1 by definition, it appears that what you want is
// ...
r->monsters[i]->name = malloc(strlen(rMonster) + 1);
strcpy (r->monsters[i]->name, rMonster);
And finally,
}
return r;
}
Note well that corresponding to the two levels of indirection in type monster **, each access to an individual monster property via r->members requires two levels of derferencing. In the expressions above, one is provided by the indexing operator, [], and the other is provided by the indirect member access operator, ->.
* Or you could allocate space for all of the monsters in one go, before the loop, and inside the loop just initialize them and the array of pointers to them. The use of a monster ** suggests the individual allocation approach, but which to choose depends somewhat on how these will be used. The two options are substantially interchangeable, but not wholly equivalent.
Is it possible to automatically initialize to NULL the pointers inside an array reallocated with realloc without iterate over it? I would like to do something like calloc but i need te resize an already existent block of memory.
For example:
#DEFINE N 50
typedef int *pointerToInt;
typedef pointerToInt *pointerToPointer;
int main(){
pointerToInt p;
pointerToPointer pp;
pp = malloc(sizeof(p)*N);
//Now i want to resize and initialize my resized vector
pp = realloc(pp, sizeof(p)*(N+10));
}
In first approximation I could change the mallocto calloc, but when I use realloc there's nothing that guarantees me initialized pointers.
Is it necessary to iterate over the whole array and set each single pointer to NULL? Or there's a better way using only callocand realloc?
The short answer is: No, there is no standard function to reallocate a block of memory and initialize its newly allocated portion to all bits zero.
The solution is either:
to not require initialization by keeping track of the valid portion of the array. You obviously must have a way to do this, otherwise how would you decide to reallocate the object.
to initialize the newly allocated portion explicitly.
There are several problems in your code:
#DEFINE N = 50 is incorrect, it should just be #define N 50
hiding pointers behind typedefs is bad: it makes the code obscure and error prone. To do it twice is evil.
you did not include <stdlib.h>
you do not test for malloc failure
you do not initialize the array allocated by malloc() either.
Here is a modified version:
#include <stdlib.h>
#define N 50
int main(void) {
int i;
int **pp, **pp1;
pp = malloc(sizeof(*pp) * N);
if (pp) {
for (i = 0; i < N; i++) {
pp[i] = NULL;
}
//Now I want to resize and initialize my resized vector
pp1 = realloc(pp, sizeof(*pp) * (N + 10));
if (pp1) {
pp = pp1;
for (i = N; i < N + 10; i++) {
pp[i] = NULL;
}
}
free(pp);
}
return 0;
}
Note that you could write a utility function for your purpose:
#include <stdlib.h>
#include <string.h>
void *realloc_zero(void *p, size_t size, size_t new_count, size_t count, int *err) {
void *newp;
if (p == NULL)
count = 0;
newp = realloc(p, size * new_count);
if (newp == NULL) {
*err = 1;
return p;
} else {
if (new_count > count) {
memset((unsigned char*)newp + size * count, 0, size * (new_count - count));
}
*err = 0;
return newp;
}
}
#define N 50
int main(void) {
int err;
int **pp;
pp = calloc(sizeof(*pp), N);
...
//Now I want to resize and initialize my resized vector
pp = realloc_zero(pp, sizeof(*pp), N + 10, N, &err);
if (err) {
// could not resize
free(pp);
return 1;
}
...
free(pp);
return 0;
}
Note however that both calloc and realloc_zero initialize the block to all bits zero, which is not guaranteed by the C Standard to be a proper representation of NULL, although most current architectures do represent the null pointer this way.
No, there is no automatic way. You must iterate and set each uninitialized pointer.
I have to implement game of life, it is almost complete, the last thing I want to do is to allocate my field dynamical. I'm working under Windows, got no Valgrind and I don't no what's the error in my code. Eclipse shows only that the process is not functional anymore.
Can anyone tell me, what's the problem in my code? Or maybe I don't need a 2 dim. array for game of life field?
struct game_field {
int length;
int **field;
};
static struct game_field *new_game_field(unsigned int l) {
struct game_field *pstField;
pstField = calloc(1, sizeof(struct game_field));
pstField->length = l;
pstField->field = malloc(l * sizeof(int*));
for( int i = 0; i < l; i++ ) {
pstField->field[i] = malloc(l * sizeof(int));
if(NULL == pstField->field[i]) {
printf("No memory for line %d\n",i);
}
}
return pstField;
}
You should think a little bit about the structures and what you are storing.
For the game of life you need to know the state of the cell on the board which is indicated by and integer so your struct should become:
struct game_field {
int length;
int *field;
};
And once you know the dimensions of the field you should allocate it once:
struct game_field *gf = calloc(1, sizeof(struct game_field));
gf->length = <blah>;
gf->field = malloc(gf->length*gf->length*sizeof(int));
This way you have an array of integers that you can use as your board.
The first malloc should be:
pstField->field = malloc(l * sizeof(int*));
Your array is int**, so the first level of allocation is an int*.
Edit: Well, I've tested your code and it does not crash for me. The problem might be somewhere else.
Here's a modification of your code that allocates the field in one block, but still lets you use array brackets for both dimensions:
struct game_field {
int length;
int **field;
};
static struct game_field *new_game_field(unsigned int len)
{
struct game_field *pstField;
pstField = malloc(sizeof(struct game_field));
pstField->length = len;
/* allocate enough space for all the row pointers + the row contents */
pstField->field = malloc((len * sizeof(int *)) + (len * len * sizeof(int)));
/* point the row pointers (at the start of the block) at the row contents
* (further into the block). */
for (int i = 0; i < len; i++)
pstField->field[i] = (int *)(&field[len]) + (i * len);
return pstField;
}
This way you can free the field in one shot:
void free_game_field(struct game_field *gf)
{
free(gf->field);
free(gf);
}
And you can keep the bracket notation to access the elements:
int row7col3 = gf->field[7][3];
Note that what you have (here as well as in your original code) is not exactly a two-dimensional array, but an array of pointers to arrays of integers
(there is a difference, but the arr[x][y] notation can work for either one).
How does one malloc an array of structs correctly if each struct contains an array of strings which vary in size?
So each struct might have a different size and would make it impossible to
realloc(numberOfStructs * sizeof(structName))
after
malloc(initialSize * sizeof(structName)
How does one allocate memory for this and keep track of what is going on?
If your structure has a char *, it takes up the size of one pointer. If it has a char[200], it takes up two hundred bytes.
I am making some guesses here, based on the information you have provided. The only reason I can see for wanting to realloc an array of structs is if you want to add more structs to that array. That's cool. There are plenty of reasons to want that kind of dynamic storage. The best way to handle it, especially if the structures are themselves dynamic, is to keep an array of pointers to these structures. Example:
1. Data structure:
typedef struct {
int numberOfStrings;
char ** strings;
}
stringHolder;
typedef struct {
int numberOfStructs;
stringHolder ** structs;
}
structList;
2. Managing dynamic arrays of strings:
void createNewStringHolder(stringHolder ** holder) {
(*holder) = malloc(sizeof(stringHolder));
(*holder)->numberOfStrings = 0;
(*holder)->strings = NULL;
}
void destroyStringHolder(stringHolder ** holder) {
// first, free each individual string
int stringIndex;
for (stringIndex = 0; stringIndex < (*holder)->numberOfStrings; stringIndex++)
{ free((*holder)->strings[stringIndex]); }
// next, free the strings[] array
free((*holder)->strings);
// finally, free the holder itself
free((*holder));
}
void addStringToHolder(stringHolder * holder, const char * string) {
int newStringCount = holder->numberOfStrings + 1;
char ** newStrings = realloc(holder->strings, newStringCount * sizeof(char *));
if (newStrings != NULL) {
holder->numberOfStrings = newStringCount;
holder->strings = newStrings;
newStrings[newStringCount - 1] = malloc((strlen(string) + 1) * sizeof(char));
strcpy(newStrings[newStringCount - 1], string);
}
}
3. Managing a dynamic array of structures:
void createNewStructList(structList ** list, int initialSize) {
// create a new list
(*list) = malloc(sizeof(structList));
// create a new list of struct pointers
(*list)->numberOfStructs = initialSize;
(*list)->structs = malloc(initialSize * sizeof(stringHolder *));
// initialize new structs
int structIndex;
for (structIndex = 0; structIndex < initialSize; structIndex++)
{ createNewStringHolder(&((*list)->structs[structIndex])); }
}
void destroyStructList(structList ** list) {
// destroy each struct in the list
int structIndex;
for (structIndex = 0; structIndex < (*list)->numberOfStructs; structIndex++)
{ destroyStringHolder(&((*list)->structs[structIndex])); }
// destroy the list itself
free((*list));
}
stringHolder * addNewStructToList(structList * list) {
int newStructCount = list->numberOfStructs + 1;
size_t newSize = newStructCount * sizeof(stringHolder *);
stringHolder ** newList = realloc(list->structs, newSize);
if (newList != NULL) {
list->numberOfStructs = newStructCount;
list->structs = newList;
createNewStringHolder(&(newList[newStructCount - 1]));
return newList[newStructCount - 1];
}
return NULL;
}
4. Main program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char * argv[]) {
structList * allHolders;
createNewStructList(&allHolders, 10);
addStringToHolder(allHolders->structs[4], "The wind took it");
addStringToHolder(allHolders->structs[4], "Am I not merciful?");
addStringToHolder(allHolders->structs[7], "Aziz, Light!");
printf("%s\n", allHolders->structs[4]->strings[0]); // The wind took it
printf("%s\n", allHolders->structs[4]->strings[1]); // Am I not merciful?
printf("%s\n", allHolders->structs[7]->strings[0]); // Aziz, Light!
stringHolder * newHolder = addNewStructToList(allHolders);
addStringToHolder(newHolder, "You shall not pass!");
printf("%s\n", newHolder->strings[0]); // You shall not pass!
printf("%s\n", allHolders->structs[10]->strings[0]); // You shall not pass!
destroyStructList(&allHolders);
return 0;
}
You don't, generally. There are two reasons you might want to do this:
So that a single free() will release the entire block of memory.
To avoid internal memory fragmentation.
But unless you have an exceptional situation, neither are very compelling, because there is crippling drawback to this approach:
If you do this, then block[i] is meaningless. You have not allocated an array. There is no way to tell where your next struct starts without either examining the struct or having outside information about the size/position of your structs in the block.
It is not so clear how your struct type is declared. C99 has a special construct for such things, called flexible array member of a struct:
As a special case, the last element of
a structure with more than one named
member may have an incomplete array
type; this is called a flexible array
member.
You could do something like
typedef struct myString myString;
struct myString { size_t len; char c[]; };
You may then allocate such a beast with
size_t x = 35;
myString* s = malloc(sizeof(myString) + x);
s->len = x;
and reallocate it with
size_t y = 350;
{
myString* tmp = realloc(s, sizeof(myString) + y);
if (!tmp) abort(); // or whatever
tmp->len = y;
}
s = tmp;
To use this more comfortably you'd probably better wrap this into macros or inline functions.