Related
I am experiencing a problem in shrinking the size of a stack in a personal implementation of the data structure.
I suppose is due to bad usage of realloc(). When the execution comes it (spop(), empty()) (If I remove the realloc and decrement the number of elements, the implementation works fine), the program just ends (crash).
What would be a better way to use the function in my implementation, or what might the problem be?
stack.h
/*Stack.h*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
typedef struct Stack{
char **storage; //Elements container;
size_t capacity; //Total amount of elements POSSIBLE in the stack;
size_t size; //Total amount of elements within the stack;
}Stack;
Stack *salloc(size_t);
void spush(Stack *, char *);
char *spop(Stack *);
void speek(Stack *);
void empty(Stack *);
void print_stack(Stack *); //Useful but non-conventional
stack.c
/*Stack.c*/
#include "stack.h"
Stack *salloc(size_t size){
Stack *s = (Stack *)malloc(sizeof(s));
s->storage = (char **)malloc(sizeof(char *) * size);
s->capacity = size;
s->size = 0;
}
static int expand_stack(Stack *s){
s->storage = realloc(s->storage, (s->capacity * 2));
}
static void shrink_stack(Stack *s){
s->storage = realloc(s->storage, (s->capacity / sizeof(char *)));
}
void spush(Stack *s, char *elem){
char *p = elem;
int k = (s->capacity-1) - s->size; //Next free position
if(s->size == s->capacity)
expand_stack(s);
s->storage[k] = (char *)malloc(sizeof(char) * (strlen(p) + 1));
memcpy(s->storage[k], p, (strlen(p) + 1));
// *(s->storage[k] + (strlen(p) + 1)) = '\0';
s->size++;
}
char *spop(Stack *s){
int k = s->capacity - s->size;
if(s->size == 0)
return NULL;
free(s->storage[k]);
s->size--;
shrink_stack(s);
}
void speek(Stack *s){
int k = s->capacity - s->size;
printf("'%s'\n", s->storage[k]);
}
void empty(Stack *s){
s->storage = realloc(s->storage, 0);
s->capacity = 0;
s->size = 0;
}
void print_stack(Stack *s){
printf("[STACK] = {\n");
int k = s->capacity - s->size;
for(int i = k; i <= s->capacity-1; i++)
printf(" '%s'\n", s->storage[i]);
printf("}\n");
}
main.c
#include "stack.h"
#define COM1 "echo"
#define COM2 "start"
#define COM3 "sort"
int main(){
Stack *s = salloc(5);
spush(s, COM1);
spush(s, COM2);
spush(s, COM3);
// speek(s);
print_stack(s); //Full Stack
spop(s);
print_stack(s);
spush(s, "cd");
print_stack(s);
empty(s);
print_stack(s);
}
Stack *salloc(size_t size){
Stack *s = (Stack *)malloc(sizeof(s));
s->storage = (char **)malloc(sizeof(char *) * size);
s->capacity = size;
s->size = 0;
}
Your first malloc call only allocates enough space for a Stack*, not enough space for the actual Stack structure. You want:
Stack *s = (Stack *)malloc(sizeof(*s));
or
Stack *s = (Stack *)malloc(sizeof(Stack));
There are quite a few issues in your code:
salloc is missing return s.
spop does not return anything (except for the NULL case).
salloc is not allocating enough memory for a Stack object (sizeof(s) is the size of the pointer, not the Stack object).
In all the calls in the form: s->storage = realloc(...) - the result from realloc (void*) should be cast to char**.
expand_stack is defined to return an int but nothing is actually returned. Should probably be changed to return void.
shrink_stack is not calculating the size properly. As a result in your case realloc can actually allocate a 0 size memory (Note:: this is a cause for an access violation exception in print_stack after calling spop). I suggest you use a debugger to catch this bug.
There's a lot of problems here. Both in design and in implementation, but all lessons worth learning.
Here are a summary of the changes:
salloc - We should store the initial capacity. empty was just freeing all storage which would make the stack unusable. By storing initial capacity, shrink_stack can avoid shrinking below that.
expand_stack - capacity must be modified after the expansion or we lose track of what the actual allocation is. Also, we wouldn't be able to add beyond the initial capacity without running into problems. Going by the int return, I suspect you intended to return the new capacity (which should be size_t.
shrink_stack should not just keep dividing the capacity, or eventually we hit zero. So using the initial_capacity we keep things no smaller than at the outset. It also needs to only shrink if the size has dropped to the point where there is value doing so.
spush - I don't know why you chose to allocate from the end of the storage but this fundamentally would break when the capacity increased and expansion occurred. Much simpler to add according to size, and pop off from size back towards zero. Note that const is added or some compilers will complain about passing a non-const pointer to a string literal, which is dangerous.
spop - As per spush, pop from size - 1 back towards zero. The other bug here was that the string is stored in a malloc'd buffer, so we should be freeing that, and that means we can't just return the pointer, but need the spop signature to provide a buffer and max size in which to put it. Given that we are dealing with null terminated strings, this can be an strncpy. Note that the return value is the passed address, or NULL if there is no element to pop. There are other ways to perhaps handle this that help remove the risk of elem == NULL etc.
speek - Again, just use size - 1
empty - Uses spop to remove all elements without discarding the initial capacity storage.
print_stack - From zero to size.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
typedef struct Stack{
char **storage; //Elements container;
size_t capacity; //Total amount of elements POSSIBLE in the stack;
size_t size; //Total amount of elements within the stack;
size_t initial_capacity;
}Stack;
Stack *salloc(size_t size){
Stack *s = (Stack *)malloc(sizeof(s));
s->storage = (char **)malloc(sizeof(char *) * size);
s->capacity = s->initial_capacity = size;
s->size = 0;
return s;
}
static int expand_stack(Stack *s){
s->storage = (char **)realloc(s->storage, (sizeof(char *) * s->capacity * 2));
s->capacity = s->capacity * 2;
return s->capacity;
}
static void shrink_stack(Stack *s){
if (s->capacity > s->initial_capacity && s->size < s->capacity / 2) {
s->storage = (char **)realloc(s->storage, (sizeof(char *) * (s->capacity / 2)));
s->capacity = s->capacity / 2;
}
}
void spush(Stack *s, char const * elem){
if(s->size == s->capacity)
expand_stack(s);
size_t size = strlen(elem) + 1;
s->storage[s->size] = (char *)malloc(sizeof(char) * size);
memcpy(s->storage[s->size], elem, size);
s->size++;
}
char *spop(Stack *s, char * elem, size_t size){
if(s->size == 0)
return NULL;
if (size > 0) {
strncpy(elem, s->storage[s->size - 1], size);
}
free(s->storage[s->size - 1]);
s->storage[s->size - 1] = NULL;
s->size--;
shrink_stack(s);
return elem;
}
void speek(Stack *s){
printf("'%s'\n", s->storage[s->size - 1]);
}
void empty(Stack *s){
char notused;
while (spop(s, ¬used, 0) != NULL);
}
void print_stack(Stack *s){
printf("[STACK] = {\n");
for(int i = 0; i < s->size; i++)
printf(" '%s'\n", s->storage[i]);
printf("}\n");
}
#define COM1 "echo"
#define COM2 "start"
#define COM3 "sort"
int main(){
Stack *s = salloc(5);
spush(s, COM1);
spush(s, COM2);
spush(s, COM3);
// speek(s);
print_stack(s); //Full Stack
char string[64];
spop(s, string, sizeof(string)/sizeof(string[0]));
print_stack(s);
spush(s, "cd");
print_stack(s);
empty(s);
print_stack(s);
}
I am trying to implement a generic stack in C using void pointers. This is not anything big, just for fun and learning. It is working with int and float as expected. But the problem I am facing is with char *, i.e. strings. It is not copying the address of the string instead trying to copy the actual string upto 4 bytes(as in my system pointer size is 4 bytes).
How to tell C to copy address of the string not the actual string, if possible, with out breaking the functionality of int and float already working?
My implementation so far is as follows,
typedef struct{
int top;
void *data;
int capacity;
size_t ele_size;
}stack_t;
int stack_init(stack_t *s, int capacity, size_t ele_size)
{
/* Initializes the stack with the given capacity
* #param s: Pointer to stack_t type variable
* #param capacity: capacity of the stack to be created
* Returns : Zero if succesful in allocating memory to the stack,
* -1 Otherwise
*/
s->top = -1;
s->capacity = capacity;
s->ele_size = ele_size;
s->data = calloc(s->capacity, s->ele_size);
if (s-> data != NULL || s->capacity == 0) {
return 0;
} else {
return -1;
}
}
int stack_push(stack_t *s, void *x)
{
/* Pushes an element on to the stack
* #param s: Pointer to stack_t type variable
* #param x: Value to Push on to the stack
* Returns : Zero if stack is not full when stack_push() is called,
* -1 Otherwise
*/
if (stack_len(s) capacity) {
s->top++;
memcpy(s->data + s->ele_size * s->top, x, s->ele_size);
return 0;
} else {
return -1;
}
}
int stack_pop(stack_t *s, void *value)
{
/* Value that is popped from the stack is placed in value parameter,
* #param s: Pointer to stack_t type variable
* #param x: Pointer to a variable to store the value popped from the
stack
* Returns: Zero if stack is not empty when stack_pop() is called,
* -1 Otherwise
*/
if (stack_len(s) > 0) {
memcpy(value, s->data + s->ele_size * s->top, s->ele_size);
s->top--;
return 0;
} else {
return -1;
}
}
For complete implementation of stack please refer here
Usage of the above stack is as follows:
Actually there is lot of unrelated stuff like using pseudo random number generator to
insert random numbers into stack.
#include"../src/stack.h"
START_TEST(int_push_pop)
{
stack_t s;
int n = random() % 60267;
int *a = calloc(n, sizeof (int));
ck_assert_int_eq(stack_init(&s, n, sizeof (int)), 0);
int i;
for (i = 0; i = 0; i--) {
int value;
int x = stack_pop(&s, &value);
ck_assert_int_eq(x, 0);
ck_assert_int_eq(value, a[i]);
x = stack_len(&s);
ck_assert_int_eq(x, i);
}
stack_clear(&s);
stack_destroy(&s);
}
END_TEST
START_TEST(float_push_pop)
{
/* similar to int_push_pop, so ignored here. */
}
END_TEST
START_TEST(string_push_pop)
{
stack_t s;
char *str = "stack overflow";
stack_push(&s, str);
char **popval = malloc(sizeof(char *));
stack_pop(&s, popval);
printf("%s\n", popval);
stack_destroy(&s);
}
END_TEST
Suite* stack_suite()
{
Suite *s = suite_create("Stack");
TCase *tc_int = tcase_create("int");
/* Stack int data type Test Case*/
TCase *tc_float = tcase_create("float");
/* Stack float data type Test Case*/
tcase_add_test(tc_int, int_push_pop);
tcase_add_test(tc_float, float_push_pop);
suite_add_tcase(s, tc_int);
suite_add_tcase(s, tc_float);
return s;
}
int main()
{
int number_failed;
Suite *s = stack_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
Since stack_push() and stack_pop() functions are taking a void* pointer, you will need to pass a pointer to the character array(string) that needs to be pushed and not the char array itself. e.g. if you declare your string as
char str[] = "hello world";
you will have to call the function as
stack_push(s,&str);
I'm a beginner in C and programming. I would like to ask some questions on dynamic array and pointer in C.
I am trying to create a dynamic array and increase its capacity, but I can't get my code working. I believe something is wrong in my setCapacityDynArr function.
Can someone give me some help?
Thanks!
struct DynArr {
TYPE *data; /* pointer to the data array */
int size; /* Number of elements in the array */
int capacity; /* capacity ofthe array */
};
void initDynArr(struct DynArr *v, int capacity) {
v->data = malloc(sizeof(TYPE) * capacity);
assert(v->data != 0);
v->size = 0;
v->capacity = capacity;
}
void freeDynArr(struct DynArr *v) {
if (v->data != 0) {
free(v->data); /* free the space on the heap */
v->data = 0; /* make it point to null */
}
v->size = 0;
v->capacity = 0;
}
int sizeDynArr(struct DynArr *v) {
return v->size;
}
void addDynArr(struct DynArr *v, TYPE val) {
/* Check to see if a resize is necessary */
if (v->size >= v->capacity) {
_setCapacityDynArr(v, 2 * v->capacity);
}
v->data[v->size] = val;
v->size++;
}
void _setCapacityDynArr(struct DynArr *v, int newCap) {
//create a new array
struct DynArr *new_v;
assert(newCap > 0);
new_v = malloc(newCap * sizeof(struct DynArr));
assert(new_v != 0);
initDynArr(new_v, newCap);
//copy old values into the new array
for (int i = 0; i < new_v->capacity; i++) {
new_v->data[i] = v->data[i];
}
//free the old memory
freeDynArr(v);
//pointer is changed to reference the new array
v = new_v;
}
int main(int argc, const char * argv[]) {
//Initialize an array
struct DynArr myArray;
initDynArr(&myArray, 5);
printf("size = 0, return: %d\n", myArray.size);
printf("capacity = 5, return: %d\n", myArray.capacity);
//Add value to the array
addDynArr(&myArray, 10);
addDynArr(&myArray, 11);
addDynArr(&myArray, 12);
addDynArr(&myArray, 13);
addDynArr(&myArray, 14);
addDynArr(&myArray, 15);
for (int i = 0; i < myArray.size; i++) {
printf("myArray value - return: %d\n", myArray.data[i]);
}
return 0;
}
//pointer is changed to reference the new array
v = new_v;
This is your problem, a classic mistake in C. In fact the function changes its own copy of the pointer, the caller never sees the change. The problem is amply described by this C FAQ.
I suggest a different approach. There's no reason to make a new v: you simply want more storage associated with it. So instead of actually changing v, you'll probably want to just call realloc on the storage: v->DATA.
You might get away with something like:
tmp = realloc(v->data, newCap * sizeof *v->data);
if (!tmp)
error;
v->data = tmp;
And this way you don't need to copy the elements either: realloc takes care of that.
//pointer is changed to reference the new array
v = new_v;
Your original pointer outside the function is not changed, since you passed the value of the pointer not the address of it here:
void _setCapacityDynArr(struct DynArr *v, int newCap)
{
Yes it's an error in _setCapacityDynArr. It's an error because you declare an DynArr structure on the stack, then you try to free it and assign a new pointer to it. That will not work, as items allocated on the stack can't be freed.
What you want to do is to reallocate only the actual data, not the whole structure. For this you should use the realloc function.
There are other problems with the function as well, like you assigning to the pointer. This pointer is a local variable so when the function returns all changes to it will be lost.
int *f, *l;
int main(){
int *a;
a = calloc(1, sizeof(int));
f = l = a;
put(&a, 1);
put(&a, 3);
put(&a, 2);
_getch();
return 0;
}
void put(int **a, int d){
printf("--%d--", sizeof(*a)); //always == 4
void *tmp = (int *)realloc(*a, sizeof(*a) + sizeof(int));
if (temp) //allocated succesfully
*a = temp;
else
printf("Allocating a failed");
l++;
}
I trying to create a queue model based on int pointers.
I've corrected the sample a bit. But it still failed.
Could you please help?
a is an int pointer (int*), therefore its size if 4 bytes (on your machine) you should keep track of the size of allocated memory.
For example:
int *f, *l;
int main(){
int *a;
size_tasize = 0;
a = calloc(1, sizeof(int));
f = l = a;
asize = sizeof(int);
put(a, 1, &asize);
put(a, 3, &asize);
put(a, 2, &asize);
_getch();
return 0;
}
void put(int *a, int d, size_t * asize){
printf("--%d--\n", asize); //always == 4
void *tmp = (int *)realloc(a, *asize + sizeof(int));
(*asize) += 4;
if (tmp)
a = tmp; //allocated succesfully
else
printf("Reallocating of 'a' size %d failed\n", asize);
l++;
}
In C, there is no way to know the size of an array which is referenced by a pointer:
int a[25]; // Known size
int *b = a; // Unknown size
so the sizeof() just prints the size of the pointer which is 4 bytes on a 32bit platform.
If you need the size, allocate a structure like so:
struct Mem {
int size;
int a[1];
}
Use sizeof(struct Mem) + sizeof(int) * amount to determine how much memory to allocate, assign it to a pointer. Now you can use the memory with ptr->a[x].
Note that it will allocate a bit more memory that necessary (usually 4 bytes) but this approach works with different alignments, pointer sizes, etc.
sizeof(a) is the size of the pointer, not what a points to.
You are modifying the local variable a within the function, not the variable a in your main function. You either need to return the new value of a from put() or pass in a pointer to your pointer (int **a) to modify it.
For example:
int *put(int *a, int d);
int main(){
int *a;
a = calloc(1, sizeof(int));
a = put(a, 1);
...
}
int *put(int *a, int d){
void *tmp = (int *)realloc(a, sizeof(a) + sizeof(int));
if (tmp)
a = tmp; //allocated succesfully
else
printf("Reallocating of 'a' size %d failed\n", sizeof(a));
return a;
}
sizeof(a) will always return 4 in your case. It returns the size of the pointer, not the size of the memory allocated that the pointer is pointing to.
Instead of doing
if (tmp)
a = tmp;
return tmp and assign it to a in main.
If you want to re-assign a new block to the pointer in a function other then one in which it has been defined , you have to pass a pointer to this pointer or return the newly allocated block and collect it into the same older block in caller function, as otherwise you'd be updating a copy.
The whole concept does not work the way you would it have to.
The sizeof a stuff does not work the way you intend to.
The reallocation itself is wrong, as you don't return the new address to the caller.
You have no information about the length of your data.
I would propose the following:
struct memblock {
unsigned int alloced;
unsigned int len;
int * data;
}
// in order to prealloc
char add_realloc(struct memblock * mb, unsigned int add) {
add += mb->alloced;
int * tmp = realloc(mb->data, sizeof(*mb) + add * sizeof(*(mb->data)));
if (!tmp) return 0;
mb->data = tmp;
mb->alloced = add;
return 1;
}
char put(struct memblock * mb, int d) {
if (mb->len == mb->alloced) {
// realloc
if (!add_realloc(mb, 1)) return 0;
}
mb->data[mb->len++] = d;
return 1;
}
int main(){
struct memblock a = {} // init with all zeros.
// Calling realloc() with a NULL pointer is like malloc().
// we put 3 values. Prealloc for not to have to realloc too often.
if (add_realloc(&a, 3) {
// now we are safe. Don't check the return values - it is guaranteed to be ok.
put(&a, 1);
put(&a, 3);
put(&a, 2);
}
return 0;
}
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;
}