program crashes while using realloc - c

I am given a text file of unknown size and i have to read it till the end, calculate the number of words, letters and some other stuff. To do this i try to read the entire file and save all the words in an array. I am told to use dynamic memory allocation since i don't know the size of the text file beforehand.
Before i get into the algorithm for calculating the words and letters i am trying to make the dynamic memory allocation work. This is my code:
int main(int argc, char *argv[]) {
FILE *fp; // file pointer
//defining a dynamic string array
char **array = malloc(10 * sizeof(char *)); //10 rows for now, will be dynamically changed later
int i,size = 10, current = 0; // current points to the position of the next slot to be filled
for(i=0; i<10; i++){
array[i] = malloc(20); //the max word size will be 20 characters (char size = 1 byte)
}
fillArray(fp, array, current, size);
return 0;
}
I define an array of strings, a variable showing its size, and a variable pointing to the slot where the next element will be added.
The functions are as follows:
int fillArray(FILE *fp, char **p, int ptr, int size){
puts("What's the name of the file (and format) to be accessed?\n (It has to be in the same directory as the program)");
char str[20];
gets(str); //getting the answer
fp = fopen((const char *)str, "r"); //opening file
int x=0, i=0, j;
while(x!=EOF){ // looping till we reach the end of the file
printf("current size: %d , next slot: %d\n", size, ptr);
if(ptr>=size){
printf("increasing size\n");
addSpace(p, &size);
}
x = fscanf(fp, "%19s", p[i]);
puts(p[i]);
i++;
ptr++;
}
}
void addSpace(char **p, int *size){ //remember to pass &size
//each time this is called, 10 more rows are added to the array
p = realloc(p,*size + 10);
int i;
for(i=*size; i<(*size)+10; i++){
p[i] = malloc(20);
}
*size += 10;
}
void freeSpace(char **p, int ptr){
//each time this is called, the rows are reduced so that they exactly fit the content
p = realloc(p, ptr); //remember that ptr points to the position of the last occupied slot + 1
}
At the beginning, the rows of the array are 10. Each time the words of the text don't fit the array, the function addSpace is called adding 10 more rows. The program runs succesfully 3 times (reaching 30 rows) and then crashes.
After using printf's to find out where the program crashes (because i am not used to the debugger yet), it seems that it crashes while trying to add 10 more rows (to 40). I can't figure out the problem or how to fix it. Any help is appreciated.

C is pass by value. The pointer p is passed to addSpace(p, &size);, and a copy of that pointer is created in the function. Once the copy is changed: p = realloc(p,*size + 10); the original stays the same.
After the realloc call, the original pointer is not valid anymore. Using it causes undefined behavior, a crash in your case.
Return the new value and assign it to the original pointer:
p = addSpace( p , &size );

Classic!
You are also passing in a double pointer which is reallocd, the address has changed between the caller and callee.
Also there's a realloc issue.
p = realloc(p,*size + 10);
If realloc fails, the original pointer to block of memory is clobbered.
The proper way to do this:
char **tmp_ptr = realloc(p, *size + 10);
if (tmp_ptr == NULL){
perror("Out of memory");
}else{
p = tmp_ptr;
}
return p;
You can do it another way, either return back the address of the new block or use triple pointers.
void addSpace(char ***p, int *size){ //remember to pass &size
//each time this is called, 10 more rows are added to the array
char **tmp_ptr = realloc(*p, *size + 10);
if (tmp_ptr == NULL){
perror("Out of memory");
}else{
*p = tmp_ptr;
}
int i;
for(i=*size; i<(*size)+10; i++){
*p[i] = malloc(20);
}
*size += 10;
}
And from the caller
addSpace(&p, &size);

Related

How can I free an array of pointers where each pointer points to an address of a string which I allocated inside of a function?

How can I free an array of pointers where each pointer points to an address of a string which I allocated inside of a function?
I created a simple array of pointers, *pointers[], inside my main and I'm passing it to readline which returns the amount of lines I have read.
The purpose of this is to store all the lines of the input separated by '\n'. Each line is stored in a pointer in the array of pointers.
So after readline is called and it returns, I can printf("%s", pointers[0]) which will show the first line the user typed in.
The way I assign a line to each pointer in my array is inside readline by allocating a char pointer, *p, to have MAXLENGTH sizes and passing the current address of p to the respective pointer. After each assignment is done, I jump to the next free address of p.
Finally, my question is if I have to free (and how) my array of pointers after the readline routine is complete and I have printed all the lines stored.
I'll leave the two functions, main and readline, here for you.
#define NUMOFLINES 5
#define MAXLENGTH 1000
void main(void) {
char *pointers[NUMOFLINES]; // Array of pointers to string
int nlines; // Number of pointers read
nlines = readlines(pointers);
writelines(pointers, nlines);
/* FAILED ATTEMPT TO FREE THE ARRAY OF POINTERS */
for(int i = 0 ; i < nlines ; i++)
free(pointers[i]);
}
int readlines(char *pointers[]) {
char line[MAXLENGTH];
char *p;
int iptr, len;
/* ALLOCATE P */
p = malloc(sizeof(char) * MAXLENGTH);
for(iptr = 0 ; iptr < NUMOFLINES && (len = get_line(line)) > 0 ; iptr++)
{
strcpy(p, line); // copy the line to the array, ending in len-th position
pointers[iptr] = p; // Pass current adress 'p' to the respectively index of pointers
p += len+1; // Next free adress in array p
}
return iptr;
}
As you can see I tried to free by going through each pointer and calling free(pointer[i], but all I get is
malloc(): corrupted top size
Aborted (core dumped)
Do I need to call free?
Thanks in advance.
You are calling malloc() only 1 time, but you are calling free() multiple times. Every char* in your array points within that single malloc()'ed memory block. You can only call free() 1 time on the starting address of that block. Calling free() on addresses inside the block is undefined behavior.
Also, the memory block you are allocating with malloc() is not nearly large enough, assuming each string that get_line() outputs can be up to MAXLENGTH characters. You are only allocating enough memory for the maximum length of 1 single line, so as soon as your reading loop has copied MAXLENGTH characters into the allocated block, you are advancing your p pointer beyond the bounds of the block, causing subsequent strcpy()'s to write into and corrupt random memory.
If you want to stay with a single malloc() and sub-divide it, try something more like this instead:
#define NUMOFLINES 5
#define MAXLENGTH 1000
int main(void) {
char *pointers[NUMOFLINES], *buffer;
int nlines = readlines(pointers, &buffer);
if (nlines < 0) return -1;
writelines(pointers, nlines);
free(buffer);
return 0;
}
int readlines(char *pointers[], char **buffer) {
*buffer = malloc(sizeof(char) * (NUMOFLINES * MAXLENGTH));
if (*buffer == NULL) return -1;
int iptr, len;
char *p = *buffer;
for(iptr = 0; (iptr < NUMOFLINES) && ((len = get_line(p)) > 0); ++iptr)
{
pointers[iptr] = p;
p += len + 1;
}
return iptr;
}
Otherwise, I suggest you allocate a separate buffer for each pointer in the array, eg:
#define NUMOFLINES 5
#define MAXLENGTH 1000
int main(void) {
char *pointers[NUMOFLINES];
int nlines = readlines(pointers);
if (nlines < 0) return -1;
writelines(pointers, nlines);
for(int i = 0; i < nlines; ++i)
free(pointers[i]);
return 0;
}
int readlines(char *pointers[]) {
char line[MAXLENGTH], *p;
int iptr, len;
for(iptr = 0; (iptr < NUMOFLINES) && ((len = get_line(line)) > 0); ++iptr)
{
p = malloc(sizeof(char) * (len + 1));
if (p == NULL) {
for(int j = 0; j < iptr; ++j) free(pointers[j]);
return -1;
}
memcpy(p, line, sizeof(char) * len);
p[len] = '\0';
pointers[iptr] = p;
}
return iptr;
}
You only call malloc once, so you should only call free once.
After readlines returns, pointers[0] points to the start of the memory you allocated, while the remaining array members point someplace within that same block of memory. That means there's only one pointer to free.
So instead of this:
for(int i = 0 ; i < nlines ; i++)
free(pointers[i]);
Just do this:
free(pointers[0]);
To reiterate, you should only pass to free a pointer value that was returned from malloc (or realloc or calloc).
You don't need to free the other pointers you maintain within your buffer. If you do a single
p = malloc(sizeof(char) * MAXLENGTH);
then you need a single
free(p);
The malloc and free have to match exactly 1-to-1 and be called with the same addresses.
Since you want to modify p, you should do:
char* original = malloc(sizeof(char) * MAXLENGTH);
p = original;
and, once done:
free(original)

Why am I getting a "segmentation fault 11" when I have assigned enough memory?

I have created double pointer char to be used as a 2d array to store strings. The append function is meant to add the string provided to the end of the array, the num_strings pointer is provided to keep track of the elements in the array (since I can't use sizeof). It seems that at some point, the function isn't allocating enough memory but I can't seem to figure out where and can't find any other issues.
I have already tried giving both the outer array and the inner array large amounts of memory, much more than they need. The issue persists. I have also tried copying the string to the array after the function had run.
int main(int argc, char *argv[]) {
char **strings = NULL;
int num_strings = 0;
append(&strings, &num_strings, "Alex");
append(&strings, &num_strings, "Edward");
// Do things with array
for (int i = 0; i < num_strings; i++) {;
printf("%s\n", strings[i]);
}
// Free memory after use
for (int i = 0; i < num_strings; i++) {
free(strings[i]);
}
free(strings);
strings = NULL;
return 0;
}
void append(char ***array, int * num_strings, char *string) {
if (*array == NULL) {
*array = malloc(sizeof(*array)); // start with enough room for 1 item (pointer)
} else {
// reallocate memory for new item
*array = realloc(*array, (((*num_strings) + 1) * sizeof(*array)));
}
printf("Char Size: %lu\n", sizeof(char));
printf("Given Size: %lu\n", sizeof(***(array)));
*(array[*num_strings]) = malloc((strlen(string) + 1) * sizeof(***(array + 0)));
strcpy(*(array[*num_strings]), string);
(*num_strings)++; // increment the number of strings
}
The output of the program should be the two strings, at the moment it only prints the first and then crashs due to the segmentation fault.
The problem is there are a couple instances of *(array[*num_strings]) that should be (*array)[*num_strings].
The difference is that the first form tries to index through the pointer passed to the function, as if the passed strings were an array, corrupting the caller's stack. The corrected version first derefernces the pointer, then indexed through the target as desired.
There are also a few places where sizeof(*array) is used where it should be sizeof(**array). x = malloc(sizeof(x)) is never correct. But this isn't causing a visible problem.

C programming to increase the size of a string

So I wrote this code but it gives me the same answer everytime. I am increasing the memory allocated to the pointer in steps of 4 and then print the value.
#include <stdio.h>
int main(void) {
int n=0;
char *name = "hello";
scanf("%d",&n);
for(int i =0; i<n;i++){
name += sizeof(int);
printf("%d \n", (sizeof(&name)));
}
return 0;
}
can someone help me? I don't know whats wrong here. I don't need a different code, I just want to understand what's wrong with this.
Try the following, error checking was left out for clarity:
#include <stdio.h>
int main(void)
{
int n=0;
char *name = null;
scanf("%d",&n);
for(int i=0; i<n;i++)
{
char *buffer = null;
//allocate/reallocate the buffer. increases by 4 bytes every iteration
buffer = (char*) realloc(name, (i+1)*4);
name = buffer;
printf("%d \n", (sizeof(&name)));
}
//release the memory used by the buffer
free(name);
return 0;
}
Here are some explanations of what is happening.
#include <stdio.h>
int main(void) {
int n=0;
// this does not actually allocate any memory. It sets the POINTER name to point (like an arrow) to a read-only block that contains "hello"
char *name = "hello";
// string literals generally come in fixed read-only memory
scanf("%d",&n);
for(int i =0; i<n;i++){
// this causes the pointer memory address to be incremented by sizeof(int) (typically 4)
// after the first increment if it will point to a string "o" (incremented by 4 characters)
// after the second increment it will point to some undefined memory behind "hello" in your virtual address space and will have undefined behaviour when accessed
name += sizeof(int);
// sizeof(&name) will give you the size of a char **. Pointer to a character pointer.
// Wich is the same size as all pointers.
// = sizeof(void *) = 8 for 64-bit systems, 4 for 32-bit systems
printf("%d \n", (sizeof(&name)));
}
return 0;
}
This is the way to do it:
#include <stdio.h>
int main(void) {
int n=0;
// allocate 10 bytes of memory and assign that memory address to name
char *name = malloc(10);
// the size of that memory needs to be kept in a separate variable
size_t name_length = 10;
// copy the desired contents into that memory
memcpy(name, "hello", sizeof("hello"));
scanf("%d",&n);
for(int i =0; i<n;i++){
// reallocate the memory into something with sizeof(int) more bytes
void * tmp = realloc(name, name_length += sizeof(int));
// this can fail
if (tmp) {
name = tmp;
} else {
perror("realloc");
exit(-1);
}
printf("%d \n", name_length);
}
return 0;
}
You have not allocated any memory for the pointer at all in the code you provide. You will have to deal with dynamic memory if you want to change the size of the allocated chunk. You will have to initially use malloc and then use realloc to allocate more memory on each step.
Let's step through your code one by one:
char *name = "hello";
this create an array of chars 'h','e','l','l','o',0 and assignes the memory address of the first character to name
for(int i =0; i<n;i++){
name += sizeof(int);
printf("%d \n", (sizeof(&name)));
}
here you add to the name pointer the size of int, which increments this pointer by 4 each pass.
Since this is a char pointer, the pointer is incremented by 4 bytes - since sizeof(int) == 4
You cannot increase the size of your hello char array, since it is not a dynamic array.
If you wish to be able to resize the string, you should malloc and copy the chars to the bigger array.

Reading file to char** using fgets and memcpy

I'm trying to read a set of lines from a file to an array. I'm doing this to learn malloc and realloc.
#define MAX_LINE 301
char** read_file_lines(char* filename) {
char** ptr = NULL;
int max = 5;
int i = 0;
FILE *fp = fopen(filename, "r");
if(fp != NULL) {
char line[MAX_LINE];
while(fgets(line, MAX_LINE, fp) != NULL) {
/* allocate some extra memory for some more lines */
if(i == max) {
int new_max = max * 2;
int nr_bytes = new_max * sizeof(char) * MAX_LINE;
char **ptr2 = realloc(ptr, nr_bytes);
if(ptr2 != NULL) {
ptr = ptr2;
ptr2 = NULL;
max = new_max;
}
}
// ptr[i] = line;
// strcpy(ptr[i], line);
memcpy(ptr[i], line, strlen(line));
i++;
}
fclose(fp);
}
else {
printf("Error opening file %s\n", filename);
}
return ptr;
}
The code compiles. However, when it is executed, an error occurs (the program crashes).
I did some debugging and determined that the problem is in up in the memcpy () instruction. I had previously tried using strcpy, which also gives a similar problem.
I went to check memcpy ()'s protocolo and it is as followS:
void * memcpy ( void * destination, const void * source, size_t num );
Now, if ptr is char**, isn't ptr[i] equivalent to a char* ?
Thanks for your comments.
It looks like ptr isn't initialized to point to any memory at all. Also, you're not allocating any memory for the individual lines.
To initialize ptr, change the declaration to:
int max = 5;
char** ptr = malloc(max * sizeof(char*));
Try adding this before the call to memcpy:
ptr[i] = malloc(strlen(line) + 1);
and change the calculation for the realloc call:
int nr_bytes = new_max * sizeof(char*);
EDIT: To explain in more detail: ptr is a pointer to an array of pointers. You have to allocate memory for ptr (that is, enough memory just to store individual pointers). In addition to this, you also have to allocate each individual array of characters that the individual elements of ptr will point to.
The first change I suggested ensures that ptr always points to enough memory to hold 5 pointers (or more, once it's been realloc'd.)
The second change ensures that each member of ptr always points to valid memory before you try to access it as a pointer.
And the third change is required because ptr points to elements that are pointers to char, not char.
Nah. Arrays are not pointers. Pointers to pointers are not arrays of arrays. If you want a two-dimensional dynamic array, then you have to allocate memory for 1. the array of pointers that point to the individual lines, and 2. for the lines themselves too.
Problem is that at first execution memory is not allocated: i is 0, max is 5, the if condition is false and the realloc is never executed.

Assigning memory to double pointer?

I am having trouble understanding how to assign memory
to a double pointer.
I want to read an array of strings and store it.
char **ptr;
fp = fopen("file.txt","r");
ptr = (char**)malloc(sizeof(char*)*50);
for(int i=0; i<20; i++)
{
ptr[i] = (char*)malloc(sizeof(char)*50);
fgets(ptr[i],50,fp);
}
instead of this I just assign a large block of memory and
store the string
char **ptr;
ptr = (char**)malloc(sizeof(char)*50*50);
would that be wrong? And if so why is it?
Your second example is wrong because each memory location conceptually would not hold a char* but rather a char. If you slightly change your thinking, it can help with this:
char *x; // Memory locations pointed to by x contain 'char'
char **y; // Memory locations pointed to by y contain 'char*'
x = (char*)malloc(sizeof(char) * 100); // 100 'char'
y = (char**)malloc(sizeof(char*) * 100); // 100 'char*'
// below is incorrect:
y = (char**)malloc(sizeof(char) * 50 * 50);
// 2500 'char' not 50 'char*' pointing to 50 'char'
Because of that, your first loop would be how you do in C an array of character arrays/pointers. Using a fixed block of memory for an array of character arrays is ok, but you would use a single char* rather than a char**, since you would not have any pointers in the memory, just chars.
char *x = calloc(50 * 50, sizeof(char));
for (ii = 0; ii < 50; ++ii) {
// Note that each string is just an OFFSET into the memory block
// You must be sensitive to this when using these 'strings'
char *str = &x[ii * 50];
}
char **ptr;
fp = fopen("file.txt","r");
ptr = (char**)malloc(sizeof(char*)*50);
for(int i=0; i<50; i++)
{
ptr[i] = (char*)malloc(sizeof(char)*50);
fgets(ptr[i],50,fp);
}
fclose(fp);
may be your typo mistake but your loop should be of 50 instead of 20 if you are looking for 50 x 50 matrix. Also after allocation of memory mentioned above you can access the buffer as ptr[i][j] i.e in the 2D format.
A double pointer is just a pointer to another pointer. So you can allocate it like this:
char *realptr=(char*)malloc(1234);
char **ptr=&realptr;
You have to keep in mind where your pointer is stored at (in this example the double pointer points to a pointer variable on the stack so it's invalid after the function returns).
i will give one example, which might clear of the doubt,
char **str; // here its kind a equivalent to char *argv[]
str = (char **)malloc(sizeof(char *)*2) // here 2 indicates 2 (char*)
str[0]=(char *)malloc(sizeof(char)*10) // here 10 indicates 10 (char)
str[1]=(char *)malloc(sizeof(char)*10) // <same as above>
strcpy(str[0],"abcdefghij"); // 10 length character
strcpy(str[1],"xyzlmnopqr"); // 10 length character
cout<<str[0]<<endl; // to print the string in case of c++
cout<<str[1]<<endl; // to print the string in case of c++
or
printf("%s",str[0]);
printf("%s",str[1]);
//finally most important thing, dont't forget to free the allocated mem
free(str[0]);
free(str[1]);
free(str);
other simpler way to memorize
Case -1 :
step-1 : char *p;
step -2 :
please read it like below
char (*p); ==> p is a pointer to a char
now you just need to do malloc for the type (step-2) without braces
i.e., p = malloc(sizeof(char) * some_len);
Case -2 :
step-1 : char **p;
step -2 :
please read it like below
char* (* p); ==> p is a pointer to a char *
now you just need to do malloc for the type (step-2) without braces
i.e., p = malloc(sizeof(char *) * some_len);
Case -3 :
No one uses this but just for sake of explanation
char ***p;
read it as,
char** (*p); ==> p is a pointer to a char** (and for this check case-2 above)
p = malloc(sizeof(char**) * some_len);
Adding to Pent's answer, as he correctly pointed out, you will not be able to use this double pointer once the function returns, because it will point to a memory location on the function's activation record on stack which is now obsolete (once the function has returned). If you want to use this double pointer after the function has returned, you may do this:
char * realptr = (char *) malloc(1234);
char ** ptr = (char **) malloc(sizeof(char *));
*ptr = realptr;
return ptr;
The return type of the function must obviously be char ** for this.
well, this is how I do it:
#include <stdlib.h>
int main(void)
{
int i = -1; // just a counter
int j = 5; // how many strings
char *s[j];
while(++i < j)
s[i] = malloc(sizeof(char*)); // allocating avery string separately
return (0);
}
this also works:
char **allocate(int lines)
{
int i = -1;
char **s = malloc(sizeof(char *) * lines); // allocating lines
while (++i < lines)
{
s[i] = malloc(sizeof(char*)); // alicating line
scanf("%s", s[i]);
}
return (s);
}
int main(int ac, char *av[])
{
int lines = 5; // how many lines
char **s = allocate(lines);
return (0);
}
Double pointer is, simply put, a pointer to a pointer,
In many cases it is used as an array of other types.
For example, if you want to create an array of strings you can simply do:
char** stringArray = calloc(10, 40);
this will create an array of size 10, each element will be a string of length 40.
thus you can access this by stringArray[5] and get a string in the 6th position.
this is one usage, the others are as mentioned above, a pointer to a pointer, and can be allocated simply by:
char* str = (char*)malloc(40);
char** pointerToPointer = &str //Get the address of the str pointer, valid only in the current closure.
read more here:
good array tutorial

Resources