Appending to dynamically allocated array - c

I am trying to add data received onto a buffer which needs to be configurable at runtime (I read a size from file or command line).
So basically I determine my buffersize and allocate an area of memory using calloc (I also put a catchall to set a buffersize if it is not in the config file or command line - Let's assume we use that for now).
I am only putting applicable lines of code.
int buffersize=10000;
void *BuffPtr = (void *)calloc(1,buffersize * sizeof(char));
I then have a recv from UDP (I have tried receiving into char array and dynamically allocated array - both work fine)
// Setup socket......
void *PktBuff = (void *)calloc(1,1000 * sizeof(char));
// Loop and receive many packets......
rcvd_bytes=recv(recv_socket, PktBuff, 1000, 0);
I can, at this point, write the contents of PktBuff and it works fine. But I want to concatenate a number of received packets in my dynamically allocated array (BuffPtr defined above).
I have tried strcat, but I just get garbage out if I try to write the first packet received, without getting another packet.
strcat(BuffPtr, PktBuff);
What I am doing wrong??
Thanks in advance.

Your data doesn't seem to be 0-terminated strings, you may want to use memmove instead.

A few points and observations:
Don't cast the return value of malloc() in C.
The expression sizeof (char) is a wordy way of writing 1, multiplying by it is seldom informative.
Make sure all your data is 0-terminated (strings), otherwise you can't use string functions since that's what they require.
You should probably just use an extra size_t counter to keep track of the number of bytes in BuffPtr, and use that and memcpy() to append.

Okay a number of issues in your code strcat is NOT the right way to append binary data. Here is a semi-robust implementation. I have NOT checked it for syntax errors, just typed it out please study it as an example, and adjust it to your code.
/* total number of bytes you are willing to receive in single a single receive */
#define MAX_RECV_BUFFER 1000
/* total number of bytes you are willing to store in memory */
#define MAXBYTES MAX_RECV_BUFFER*1000
int append_bytes() {
char rcvbuf[MAX_RECV_BUFFER]; /* buffer where things are received */
void *buf = NULL; /* buffer where bytes are collected */
size_t rcvlen; /* length of data received */
size_t buflen = 0; /* total bytes */
while(1) {
void *p;
rcvlen = recv(recv_socket, rcvbuf, MAX_RECV_BUFFER, 0);
if ( rcvlen < 0 ) { manage_error(); }
if ( rcvlen == 0 )
break;
if ( buflen + rcvlen > MAXBYTES)
break;
p = realloc(buf, buflen+recvlen);
if ( !p ) { manage_memory_error(); }
buf = p; /* we have enough space */
memcpy(buf+buflen, rcvbuf, recvlen); /* append new content to end of buffer */
buflen+=recvlen; /* add length to buflen */
}
}

Related

Function that reads an array until 0 is entered

I want to make a program that dynamically allocates memory for each element of an array while it is entered from stdin and stored into an array. The reading should stop when 0 is entered. If I try to make it directly in main(), in looks like this:
int *a;
int i = 0;
a = malloc(sizeof(int));
do
{
scanf("%d", &a[i]);
a = realloc(a, (i + 2) * sizeof(int)); // enough space for storing another number
i++;
} while (a[i-1] != 0);
But I don't know how to make a function that does this. This is what I've tried, but it crashes everytime.
void read(int **a, int *cnt)
{
a = malloc(sizeof(int));
*a = malloc(sizeof(int));
*cnt = 0;
do
{
scanf("%d", a[*cnt]);
*a = realloc(*a, (*cnt + 2) * sizeof(int)); // enough space for storing another number
(*cnt)++;
} while (a[*cnt-1] != 0);
}
how about putting everything inside a function and returning a;
int *read()
{
int *a;
int i = 0;
a = malloc(sizeof(int));
if( !a ) return NULL;
do
{
scanf("%d", &a[i]);
a = realloc(a, (i + 2) * sizeof(int)); // enough space for storing another number
if( !a ) return NULL;
i++;
} while (a[i-1] != 0);
return a;
}
Assuming you are calling this in the usual way:
void read(int **a, int *cnt)
{
a = malloc(sizeof(int)); // This overwrites local a disconnecting it from the main a
*a = malloc(sizeof(int)); // so this will only change the memory pointed by local a and leak memory
...
}
int main()
{
int *a;
int cnt = 0;
read(&a, &cnt);
...
}
What is happening you’re giving the address to the pointer a to the function and then in the function you’re immediately overwriting it with the memory allocation. Matter this the a in the function and a in the main are completely separate entities. If you then allocate memory for *a you’re only storing that in the local a and the main a will remain pointing to whatever it happened to be. So it is uninitialized and causes undefined behavior.
So remove the line a = malloc(sizeof(int)) and your code will properly affect the main a also.
You also have to use *a for everything in read, including scanf and while. So it might be better to make the function handle allocation and return a pointer as was suggested in another answer.
Also note you should check realloc for return values so you won’t leak memory or crash there and you should use sizeof(int*) when allocating a pointer, no matter if size of int and int* were the same. It looks clearer.
You can pattern your function along the POSIX getline() function.
The pattern is very simple. Your function receives a reference to the pointer (i.e., a pointer to a pointer) used for the data, resized dynamically; and a pointer to the size allocated to that pointer. It will return the number of elements read to the array.
If you were reading doubles rather than ints, and wished to read all doubles from the input until end-of-input (either end of file, if redirected from a file, or until the user types a non-number and presses Enter, or until the user presses Ctrl+D at the beginning of the line), the code would look something like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
size_t read_doubles(double **dataptr, size_t *sizeptr, FILE *in)
{
double *data; /* A local copy of the pointer */
size_t used = 0; /* Number of doubles in data */
size_t size; /* Number of doubles allocated for data */
/* Sanity checks against NULL pointers. */
if (!dataptr || !sizeptr || !in) {
errno = EINVAL; /* "Invalid parameter" */
return 0;
}
/* If *dataptr is NULL, or *sizeptr is zero,
there is no memory allocated yet. */
if (*dataptr != NULL && *sizeptr > 0) {
data = *dataptr;
size = *sizeptr;
} else {
data = NULL;
size = 0;
*dataptr = NULL;
*sizeptr = 0;
}
while (1) {
/* Ensure there is room in the data. */
if (used >= size) {
/* Need to allocate more.
Note: we have a copy of (data) in (*dataptr),
and of (size) in (*sizeptr). */
/* Reallocation policy. This one is simple,
reallocating in fixed-size chunks, but
better ones are well known: you probably
wish to ensure the size is at least some
sensible minimum (maybe a thousand or so),
then double the size up to a few million,
then increase the size in fixed-size chunks
of a few million, in a real-world application. */
size = used + 500;
/* Note: malloc(size) and realloc(NULL, size)
are equivalent; no need to check for NULL. */
data = realloc(data, size * sizeof data[0]);
if (!data) {
/* Reallocation failed, but the old data
pointer in (*dataptr) is still valid,
it isn't lost. Return an error. */
errno = ENOMEM;
return 0;
}
/* Reallocation succeeded; update the originals,
that are visible to the caller. */
*dataptr = data;
*sizeptr = size;
}
/* Read one more element, if possible.
Note: "&(data[used])" and "data+used"
are completely equivalent expressions.
*/
if (fscanf(input, " %lf", data + used) != 1)
break; /* No more input, or not a number. */
/* Yes, read a new data element. */
used++;
}
/* If we encountered a true read error,
return an error. */
if (ferror(input)) {
errno = EIO;
return 0;
}
/* Not an error; there just weren't more
data, or the data was not a number.
*/
/* Normally, programs do not set errno
except in case of an error. However,
here, used==0 just means there was no
data, it does not indicate an error per se.
For simplicity, because we know no error
has occurred, we just set errno=0 here,
rather than check if used==0 and only then
set errno to zero.
This also means it is safe to examine errno
after a call to this function, no matter what
the return value is. errno will be zero if no
errors have occurred, and nonzero in error cases.
*/
errno = 0;
return used;
}
The <errno.h> was included for the library to expose errno, and <string.h> for strerror(). These are both standard C.
However, the error constants I used above, EINVAL, ENOMEM, and EIO, are only defined by POSIXy systems, and might not exist in all systems. That is okay; you can just pick any smallish nonzero values and use them instead, because the function always sets errno. In that case, however, you need to check each of them and print the appropriate error message for each. In my case, all the systems I use define those three error codes for me, and I can just use strerror(errno) to convert the code to a standard error message (Invalid argument, Not enough space, and Input/output error, respectively, in non-localized programs).
Using a function defined like above, is very simple:
int main(void)
{
double *data = NULL; /* NULL for "Not allocated yet" */
size_t size = 0; /* 0 for "Not allocated yet" */
size_t used;
size_t i; /* Just a loop variable. */
used = read_doubles(&data, &size, stdin);
if (!used) {
/* No data read. Was it an actual error, or just no data? */
if (errno)
fprintf(stderr, "Error reading standard input: %s.\n", strerror(errno));
else
fprintf(stderr, "No numbers in standard input!\n");
return EXIT_FAILURE;
}
printf("Read %zu numbers from standard input.\n", used);
printf("(The dynamically allocated array has room for %zu.)\n", size);
for (i = 0; i < used; i++)
printf(" %f\n", data[i]);
/* Array no longer needed, so we can free it.
Explicitly NULLing and zeroing them means
we can safely reuse them later, if we were
to extend this program. So, it's not necessary
to clear them this way, but it is a good practice
considering it makes long-term maintenance easier. */
free(data);
data = NULL;
size = 0;
used = 0;
/* This version of the program has nothing more to do. */
return EXIT_SUCCESS;
}
Essentially, you just set the pointer you supply the address of to NULL, and the size you supply the address of also to 0, before the call to indicate no array has been dynamically allocated yet. There is no need to malloc() an initial array; realloc(NULL, size) is completely safe, and does exactly what malloc(size) does. Indeed, I often write code that has no malloc() anywhere in it, and use only realloc().
Note that the above code snippets are untested, so there might be typos in them. (And I did choose to use doubles instead of ints and a different end-of-input condition, to ensure you don't just copy-paste the code and use as-is, without reading and understanding it first.) If you find or suspect you found any, let me know in a comment, and I'll check.
Also note that the above code snippets are long only because I tried to write descriptive comments -- literally most of the "code" in them is comments. Writing descriptive comments -- those that describe the intent of the code, and not just what the code actually does; the latter is easy to read from the code itself, but the former is what you or others later reading the code need to know, to check if the code is sound or buggy --, is very hard, and even after over two decades, I'm still trying to get better at it.
If you like writing code, I do warmly recommend you start practicing writing good, intent-describing comments right away, rather than battle with it for decades like I have. It is surprising how much good comments, and occasionally a good nights sleep to review the code with fresh pair of eyes, helps.

Copying very large strings in memory [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I'm trying to implement a solution to copy a large string in memory in C.
Can you give me any advice about implementation or any reference?
I'm thinking to copy byte by byte since I don't know the length (probably I can't calculate it with strlen() since the string is very large).
Another concern is that I will have to reallocate memory on every step and I don't know how is the best way to do that. Is there any way that I can reallocate using only the reference to the last position of the memory already alocated and filled? Thus if the memory allocation fails, it will not affect the rest of the memory already filled.
What is the best value to return from this function? Should I return the number of bytes that were succesfully copied?
If there is a memory allocation fail, does realloc() set any global variable that I can check in the main function after I call the copying function? As I don't want to just return NULL from it if at some point realloc() fails, but I want to return a value more useful.
strlen() won't fail, as it uses size_t to descirbe the string's size, and size_t is large enough to hold the size of any object on the machine the program runs on.
So simply do
#define _XOPEN_SOURCE 500 /* for strdup */
#include <string.h>
int duplicate_string(const char * src, char ** pdst)
{
int result = 0;
if (NULL == ((*pdst) = strdup(src)))
{
result = -1;
}
return result;
}
If this fails try using an more clever structure to hold the data, for example by chopping it into slices:
#define _XOPEN_SOURCE 700 /* for strndup */
#include <string.h>
int slice_string(const char * src, char *** ppdst, size_t s)
{
int result = 0;
size_t s_internal = s + 1; /* Add one for the 0-terminator. */
size_t len = strlen(src) + 1;
size_t n =len/s_internal + (len%s_internal ?1 :0);
*ppdst = calloc(n + 1, sizeof(**ppdst)); /* +1 to have a stopper element. */
if (NULL == (*ppdst))
{
result = -1;
goto lblExit;
}
for (size_t i = 0; i < n; ++i)
{
(*ppdst)[i] = strndup(src, s);
if (NULL == (*ppdst)[i])
{
result = -1;
while (--i > 0)
{
free((*ppdst)[i]);
}
free(*ppdst);
*ppdst = NULL;
goto lblExit;
}
src += s;
}
lblExit:
return result;
}
Use such functions by trying dump copy first and if this fails by slicing the string.
int main(void)
{
char * s = NULL;
read_big_string(&s);
int result = 0;
char * d = NULL;
char ** pd = NULL;
/* 1st try dump copy. */
result = duplicate_string(s, &d);
if (0 != result)
{
/*2ndly try to slice it. */
{
size_t len = strlen(s);
do
{
len = len/2 + (len%2 ?1 :0);
result = slice_string(s, &pd, len);
} while ((0 != result) || (1 == len));
}
}
if (0 != result)
{
fprintf(stderr, "Duplicating the string failed.\n");
}
/* Use copies. */
if (NULL != d)
{
/* USe result from simple duplication. */
}
if (NULL != pd)
{
/* Use result from sliced duplication. */
}
/* Free the copies. */
if (NULL != pd)
{
for (size_t i = 0; pd[i]; ++i)
{
free(pd[i]);
}
}
free(pd);
free(d);
return 0;
}
realloc() failing
If there is a memory allocation fail, does realloc() set any global variable that I can check in the main function after I call the copying function? As I don't want to just return NULL from it if at some point realloc() fails, but I want to return a value more useful.
There's no problem with realloc() returning null if you use realloc() correctly. If you use realloc() incorrectly, you get what you deserve.
Incorrect use of realloc()
char *space = malloc(large_number);
space = realloc(space, even_larger_number);
If the realloc() fails, this code has overwritten the only reference to the previously allocated space with NULL, so not only have you failed to allocate new space but you also cannot release the old space because you've lost the pointer to it.
(For the fastidious: the fact that the original malloc() might have failed is not critical; space will be NULL, but that's a valid first argument to realloc(). The only difference is that there would be no previous allocation that was lost.)
Correct use of realloc()
char *space = malloc(large_number);
char *new_space = realloc(space, even_larger_number);
if (new_space != 0)
space = new_space;
This saves and tests the result of realloc() before overwriting the value in space.
Continually growing memory
Another concern is that I will have to reallocate memory on every step and I don't know how is the best way to do that. Is there any way that I can reallocate using only the reference to the last position of the memory already allocated and filled? Thus if the memory allocation fails, it will not affect the rest of the memory already filled.
The standard technique for avoiding quadratic behaviour (which really does matter when you're dealing with megabytes of data) is to double the space allocated for your working string when you need to grow it. You do that by keeping three values:
Pointer to the data.
Size of the data area that is allocated.
Size of the data area that is in use.
When the incoming data won't fit in the space that is unused, you reallocate the space, doubling the amount that is allocated unless you need more than that for the new space. If you think you're going to be adding more data later, then you might add double the new amount. This amortizes the cost of the memory allocations, and saves copying the unchanging data as often.
struct String
{
char *data;
size_t length;
size_t allocated;
};
int add_data_to_string(struct String *str, char const *data, size_t datalen)
{
if (str->length + datalen >= str->allocated)
{
size_t newlen = 2 * (str->allocated + datalen + 1);
char *newdata = realloc(str->data, newlen);
if (newdata == 0)
return -1;
str->data = newdata;
str->allocated = newlen;
}
memcpy(str->data + str->length, data, datalen + 1);
str->length += datalen;
return 0;
}
When you've finished adding to the string, you can release the unused space if you wish:
void release_unused(struct String *str)
{
char *data = realloc(str->data, str->length + 1);
str->data = data;
str->allocated = str->length + 1;
}
It is very unlikely that shrinking a memory block will move it, but the standard says:
The realloc function deallocates the old object pointed to by ptr and returns a
pointer to a new object that has the size specified by size. The contents of the new
object shall be the same as that of the old object prior to deallocation, up to the lesser of
the new and old sizes.
The realloc function returns a pointer to the new object (which may have the same
value as a pointer to the old object), or a null pointer if the new object could not be
allocated.
Note that 'may have the same value as a pointer to the old object' also means 'may have a different value from a pointer to the old object'.
The code assumes that it is dealing with null terminated strings; the memcpy() code copies the length plus one byte to collect the terminal null, for example, and the release_unused() code keeps a byte for the terminal null. The length element is the value that would be returned by strlen(), but it is crucial that you don't keep doing strlen() on megabytes of data. If you are dealing with binary data, you handle things subtly differently.
use a smart pointer and avoid copying in the first place
OK, let's use Cunningham's Question to help figure out what to do. Cunningham's Question (or Query - your choice :-) is:
What's the simplest thing that could possibly work?
-- Ward Cunningham
IMO the simplest thing that could possibly work would be to allocate a large buffer, suck the string into the buffer, reallocate the buffer down to the actual size of the string, and return a pointer to that buffer. It's the caller's responsibility to free the buffer they get when they're done with it. Something on the order of:
#define BIG_BUFFER_SIZE 100000000
char *read_big_string(FILE *f) /* read a big string from a file */
{
char *buf = malloc(BIG_BUFFER_SIZE);
fgets(buf, BIG_BUFFER_SIZE, f);
realloc(buf, strlen(buf)+1);
return buf;
}
This is example code only. There are #includes which are not included, and there's a fair number of possible errors which are not handled in the above, the implementation of which are left as an exercise for the reader. Your mileage may vary. Dealer contribution may affect cost. Check with your dealer for price and options available in your area. Caveat codor.
Share and enjoy.

vector replication in c

how can i define an array in c which works like vector? This array should take any amount of values. It can take 0 values or 10 values or 100 values.
The code below works but gives me a runtime error that stack was corrupted.
int i = 0;
int* aPtr = &i;
int* head = aPtr;
for(i=0;i<6;i++){
(*aPtr)=i;
aPtr++;
}
Similarly how can i use char* str to take any amount of characters followed by null character in end to make a string?
Practice for interviews :)
There are many ways to do this in C, depending on your requirements, but you said "any number of values" (which usually means as many as will fit in memory). That's commonly done using realloc to grow the size of an array dynamically. You'll need to keep some bookkeeping information too on the size of the array as it grows.
void
store (vector_t * v, int idx, int value)
{
if (v->size < idx) {
v->size = idx * 2;
v->data = realloc(v->data, v->size);
}
v->data[idx] = value;
}
This being tagged "homework", I've left some details to fill in such as the definition of vector_t.
In Your for loop , after the first iteration, you are trying to access aPtr which points to a memory location which was not declared or reserved before. In the first iteration, the int i did the memory allocation for you.
What you could do though would be to initally allocate the memory required using malloc .
Once this memory is allocated , and if you walk through only the allocated stack space, you wont come across a run time error.
PS:Your code does not work if it just compiles. Any program may contain run time as well as compile time errors. Your code sample is a very common example of run-time error.
This isn't too difficult. The important thing to remember is that you will need to initially allocate memory for your array using malloc(...) or calloc(...). After that you can easily allocate (or deallocate) memory as items are added or removed. The method for dynamically adding or removing memory (which is used to store the items in the array) is realloc(...). The wiki page for C Dynamic Memory Allocation is actually pretty informative. I've provided an example below showing how to initially allocate a char* array, then increase the size and decrease the size.
#include "stdio.h"
#include "stdlib.h"
int main()
{
char *myDynamicString;
/* allocate initial memory */
myDynamicString = (char *)malloc(sizeof(char) * 2);
myDynamicString[1] = '\0';
/* set values */
myDynamicString[0] = 'A';
/* prints: A */
printf("String: %s\n", myDynamicString);
/* make string bigger */
myDynamicString = (char *)realloc(myDynamicString, sizeof(char) * 6);
myDynamicString[5] = '\0';
/* set values */
myDynamicString[1] = 'P';
myDynamicString[2] = 'P';
myDynamicString[3] = 'L';
myDynamicString[4] = 'E';
/* prints: APPLE */
printf("Bigger String: %s\n", myDynamicString);
/* make string smaller */
myDynamicString = (char *)realloc(myDynamicString, sizeof(char) * 3);
myDynamicString[2] = '\0';
/* set values */
myDynamicString[1] = 'Z';
/* prints: AZ */
printf("Smaller String: %s\n", myDynamicString);
/* don't forget to release the memory */
free(myDynamicString);
return 0;
}

Freeing a dynamically allocated 2D structure

I've dynamically allocated a structure, conceptually very similar to a matrix, to hold a set of strings. I've encountered a problem while trying to free the memory. My code looks like this:
# include <stdio.h>
# include <string.h>
# include <malloc.h>
# define SIZE 2
typedef struct fork{
char** dataPointersArray;
char* dataArray;
}fork;
int main(int argc, char* argv[]){
fork forkDS;
int i;
char* dataArrayPtr;
unsigned char data[255] = "some data"; /* this is actually a function's output */
int PtrIndex;
/* allocate memory for the arrays */
forkDS.dataPointersArray = (char**) calloc(SIZE ,sizeof(char*));
if(forkDS.dataPointersArray == NULL){
printf("couldn't allocate memory \n");
}
forkDS.dataArray = (char*) calloc(SIZE, 255);
if( forkDS.dataArray == NULL){
free(forkDS.dataPointersArray);
printf("couldn't allocate memory \n");
}
dataArrayPtr = forkDS.dataArray;
for(i = 0; i < SIZE; i++){
/* update the dataPointers Array */
forkDS.dataPointersArray[i] = dataArrayPtr;
/* copy data into data array */
memcpy(dataArrayPtr,data,20);
dataArrayPtr[255] = '\0';
/* update the pointer of the data array */
dataArrayPtr = dataArrayPtr + 256;
}
for (PtrIndex = 0; PtrIndex < 2; PtrIndex++) {
if (*(forkDS.dataPointersArray + PtrIndex) != NULL) {
*(forkDS.dataPointersArray + PtrIndex) = NULL;
}
}
/* DEBUG comment - this 2 lines works */
free(forkDS.dataArray);
forkDS.dataArray = NULL;
/* DEBUG comment - the next line fails */
free(forkDS.dataPointersArray);
forkDS.dataPointersArray = NULL;
return 0;
}
So the structure actually contains 2 arrays, one of pointers to strings, and the other one contains the strings aligned one after the other, separated by a terminating \0.
The code works fine, and the for loop in the end works as well. The first call to free also works. The problem is that the last call to free fails.
Although trying to search all possible data on the issue, all the examples I've found regarded the case where the second array, which holds the strings, is allocated step by step in a for loop, and freed afterwards in a for loop as well.
I wanted to avoid using dynamic allocation in a loop, and therefore my code looks different.
Does anyone know what the problem is?
======================================================================================
Thanks a lot to all of you who answered me. Eventually, the bug was solved. The problem was that the dataPointersArray was filled with more than SIZE elements in some other piece of code, which seemed innocent at first, and actually caused the free call to fail.
Thanks again for the comments!
Shachar
You are allocating SIZE*255 bytes, but using SIZE * 256 bytes:
forkDS.dataArray = (char*) calloc(SIZE, 255);
dataArrayPtr = forkDS.dataArray;
//SIZE TIMES loop:
dataArrayPtr = dataArrayPtr + 256;
So when you are NULLing the pointers, you probably overwrite control data placed past the end of the array by malloc that free is looking for.
You allocated the space for an array consisting of SIZE lines with 255 characters each. The highest index on each line therefore is 254 = 255 - 1. As you write the \0 character, you write it at the beginning of the next line. After the last iteration, you would be off by SIZE bytes.
Just another detail: If any of the memory allocations failed, the program would only print its error messages, but it won't stop causing a SEGFAULT later.

realloc homework help

For an assignment, part of what I have to do involves the use of malloc and realloc. I first create a 2D array of chars, the dimensions being the number of lines and the number of characters. I then use malloc to allocate enough memory to store input from some file. Using fgets I read one line in at a time, and store it in the array. This part works fine (or so I think). The problem comes in when I try to reallocate memory for more lines if need be. The program flow is supposed to be like this:
Create a character array of 50 lines, with 80 characters per line (working)
Use fgets to read one line at a time and save it to the array (working)
When 50 lines have been read, reallocate the array to allow for 100 lines (not working)
Keep reallocating as need be (not working)
This is what I have so far (the core of it at least, I omitted irrelevant code):
#define NUMBER_OF_LINES 50
#define CHARACTERS_PER_LINE 80
FILE *inputFile = fopen("some.text", "r");
char **lines;
lines = malloc(NUMBER_OF_LINES * sizeof(*lines));
int i;
for (i = 0; i < NUMBER_OF_LINES; i++)
*(lines+i) = malloc(CHARACTERS_PER_LINE * sizeof(char));
int linesRemaining = NUMBER_OF_LINES;
int reallocCount = 1;
i = 0;
while (!feof(inputFile)) {
if (!linesRemaining) {
reallocCount++;
lines = realloc(lines, (NUM_OF_LINES * reallocCount) * sizeof(*lines));
linesRemaining = NUM_OF_LINES;
}
fgets(*(lines+i), CHARS_PER_LINE, inputFile);
i++;
linesRemaining--;
}
My gut tells me the problem is with the realloc, so I'll explain what I think it's doing.
realloc(lines, (NUM_OF_LINES * reallocCount) * sizeof(*lines));
The first argument, lines, is the pointer I would like to reallocate a certain amount of memory. NUM_OF_LINES is the amount I would like to increase the size by. I multiply this by reallocLinesCount, which is a counter that keeps track of how many sets of 50 lines I ought to have. The sizeof(*lines) part is the size of a pointer to a char.
Thank you for reading and any help is greatly appreciated :)
EDIT: thank you all for the responses; I do not have time right now to read all of the answers right now, but all of your answers will be more thoroughly read and understood once this imminent deadline has passed :D
My motto is: "say what you mean". In your case, you MEAN to enlarge your array when it's not big enough to hold your data.
FILE *in; // you fill this in
int nlines=50; // initial value
char **buffer=malloc(nlines * sizeof *buffer);
int i=0;
for(int i=0; !feof(in); ++i)
{
if(i>=nlines)
buffer=realloc(buffer, (nlines+=50)*sizeof *buffer);
buffer[i]=malloc(80);
fgets(buffer[i], 80, in);
}
realloc() will often find out that there is not enough available room to expand the existing array in-place; in that case, it will create an entirely new array of the specified size, copy the contents of the old array to the new one, deallocate the old array, and return a pointer to the new one. So you should write
char **oldLines = lines;
lines = realloc(...);
(the purpose of oldLines is to keep the original pointer in case realloc() runs out of memory and returns NULL, as per #Brian L's tip).
This is how you should realloc:
char **new_lines = realloc(lines, (NUM_OF_LINES * ++reallocLinesCount) * sizeof(*lines));
if (new_lines)
{
lines = new_lines;
}
else
{
// Memory allocation fails. Do some error handling.
}
Read realloc reference for details.
EDIT
You need more allocation for each new lines.
You are allocating more pointers to lines but not the lines themselves. It is in your code at the beginning:
for (i = 0; i < NUMBER_OF_LINES; i++)
*(lines+i) = malloc(CHARACTERS_PER_LINE * sizeof(char));
So after you allocated your number of lines for each line you allocate the space for the line itself. You forgot to do this for the new lines when you reallocate.
Let's first see how realloc() works. It returns a pointer to new
memory on success, and NULL on failure. On failure, it doesn't
touch the old memory, and on success, it free()'s it, after copying
your data to the new place.
So, the way to use realloc() safely is:
/* allocate memory using malloc() */
ptr = malloc(N * sizeof *ptr);
/* make sure malloc succeeded */
...
/* call realloc() */
new_ptr = realloc(ptr, M * sizeof *new_ptr);
/* see if it succeeded */
if (new_ptr) {
/* okay, we can set ptr */
ptr = new_ptr;
} else {
/* realloc failed, old pointer still valid */
}
So, the first thing is that you are using realloc() incorrectly.
You should never say x = realloc(x, ...);, because if realloc()
fails, you assign x to NULL, and the old memory is lost. This is
a memory leak.
Now, on to your problem. Let's say you have successfully read
NUMBER_OF_LINES lines. Now you want to make room for an additional
NUMBER_OF_LINES lines. You would do:
char **new_lines = realloc(lines, NUMBER_OF_LINES*reallocCount*sizeof *new_lines);
if (new_lines) {
lines = new_lines;
} else {
fprintf(stderr, "realloc failed!\n");
return;
}
/* Now, lines[NUMBER_OF_LINES] to lines[2*NUMBER_OF_LINES-1] are
* available to point someplace useful. They don't point anywhere
* useful yet. We have to allocate memory for them, just like earlier */
start = NUMBER_OF_LINES*reallocCount;
for (i=0; i < NUMBER_OF_LINES; ++i) {
/* You weren't allocating memory here, and were writing to
* lines[0] through lines[NUMBER_OF_LINES-1], which is not what
* you want. */
lines[start+i] = malloc(CHARS_PER_LINE * sizeof *lines[start+i]);
/* check the result of malloc here */
}
fgets(lines[start+i], CHARS_PER_LINE, inputFile);
One final note: it's almost always wrong to use while (!feof(fp))
to read lines from a file.

Resources