C: Realloc behaves in a way i cant figure out why - c

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
char buffer[103];
char **words = malloc(1 * sizeof(*words));
size_t counter = 0;
size_t array_size = 2;
for(int i = 0; i < 5; i++){
if(!fgets(buffer, 103, stdin)){
fputs("fgets failed", stderr);
}
words[counter] = buffer;
char **more_words = realloc(words, array_size * sizeof(*more_words));
words = more_words;
array_size++;
counter ++;
}
printf("********************************************************");
for(int i = 0; i < 5; i++){
printf("%s\n", words[i]);
}
}
Now this is the simplified code im working on.
I know i dont handle lots of errors that can occour.
The point is, that when you execute this, the word array seems to have 5 entries of the 'last' entry.
Say you give fgets :
1
2
3
4
5
, then
words[0] = 5;
words[1] = 5;
words[2] = 5;
words[3] = 5;
words[4] = 5;
Why is not:
words[0] = 1;
words[1] = 2;
words[2] = 3;
words[3] = 4;
words[4] = 5;
?

The problem is not the realloc, but what you assign to pointers that you allocated:
words[counter] = buffer;
The buffer is the same pointer all the time, so you end up with the last string read into the buffer.
You need to malloc and copy the buffer for each line:
words[counter] = malloc(strlen(buffer)+1);
strcpy(words[counter], buffer);
It goes without saying that you should be NULL-checking the value returned by realloc before assigning it back to words.

if(!fgets(buffer, 103, stdin)){
fputs("fgets failed", stderr);
}
words[counter] = buffer;
You have one buffer that are overwritten each time when calling fgets, so that all strings in words effectively point to the same char array. Try this:
if(!fgets(buffer, 103, stdin)){
fputs("fgets failed", stderr);
}
// here make a new buffer and copy the string just read into it.
char *new_buffer = malloc(strlen(buffer) + 1);
strcpy(new_buffer, buffer);
words[counter] = new_buffer;

Related

How do I fix realloc(): invalid next size in C

I have a Problem when I try to load a file into memory as an array.
I am trying to load a file into an array and print it out again but I would like to allow the memory to grow as the file length can be arbitrary.
When I run my Program locally on my Mac it seems to Work fine but when I try it on my Ubuntu VM, I get the following error
realloc(): invalid next size
Aborted (core dumped)
My Code is as follows
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **loadfile(char *filename, int *len);
int main(int argc, char *argv[])
{
if (argc == 1)
{
printf("Usage add file\n");
return 1;
}
int length = 0;
char **words = loadfile(argv[1],&length);
printf("%d\n", length);
for (int i = 0; i < length; i++) {
printf("%s\n",words[i]);
}
printf("Done\n");
return 0;
}
char **loadfile(char *filename, int *len)
{
const int STEPSIZE = 10;
FILE *f = fopen(filename,"r");
if (!f) {
fprintf(stderr, "Can't open file\n");
return NULL;
}
int arrlen = STEPSIZE;
char **lines = (char **)malloc(STEPSIZE);
char buf[100];
int i = 0;
int counter = 2;
while (fgets(buf,100,f))
{
if (i == arrlen)
{
counter++;
arrlen += STEPSIZE;
char **newlines = (char **)realloc(lines,counter * STEPSIZE);
if(!newlines)
{
printf("Out of memory\n");
//return 2;
}
lines = newlines;
}
buf[strlen(buf)-1] = '\0';
int slen = strlen(buf);
char *str = (char *)malloc(slen + 1 *sizeof(char ));
strcpy(str, buf);
lines[i] = str;
i++;
}
*len =i;
return lines;
}
and for the life of me I cannot find the problem.
I can only assume the problem is somewhere in this section but I may be wrong:
if (i == arrlen)
{
counter++;
arrlen += STEPSIZE;
char **newlines = (char **)realloc(lines,counter * STEPSIZE);
if(!newlines)
{
printf("Out of memory\n");
//return 2;
}
lines = newlines;
}
Your Help is greatly appreciated
const int STEPSIZE = 10;
char **lines = (char **)malloc(STEPSIZE);
char **newlines = (char **)realloc(lines,counter * STEPSIZE);
You don't want to allocate 10 bytes, but memory for 10 char * elements. Thus some subsequent access to lines[i] = str; is invalid.
What you meant to do is:
char **lines = malloc(sizeof(*lines) * STEPSIZE);
char **newlines = realloc(lines, sizeof(*newlines) * counter * STEPSIZE);
Alternatively you can use sizeof(char*).
Also:
char *str = (char *)malloc(slen + 1 *sizeof(char ));
although it is correct and will work, because sizeof(char) is 1, but it's more clearly the intention was:
char *str = malloc((slen + 1) * sizeof(char));
Also, it's good to think if you should cast the result of malloc.
You forgot that malloc and realloc take an amount of bytes, not an amount of 'cells' in your array.
The program works as expected if you replace your malloc(STEPSIZE) by malloc(STEPSIZE * sizeof(char*)) and realloc(lines, counter * STEPSIZE) by realloc(lines, (counter * STEPSIZE) * sizeof(char*))

Realloc on an array of structs, address boundary error when indexing

I have some code where I'm trying to read lines in from a file and store some information from each line in a struct. Since I don't know how long the file will be, I'm dynamically adjusting the array of structs using realloc.
My issue is that my code seems to work fine for the first 3 (technically 6) lines, and then I receive SIGSEGV (address boundary error). gdb says that this happens when trying to index the array (array[i]->string = (char*) _tmp).
typedef struct {
char* string;
int len;
} buffer;
int read_into_array(char *filename, buffer** array) {
int n;
size_t size;
char* buf = NULL;
FILE *file = fopen(filename, "r");
int i = 0;
while (1) {
buffer *tmp = (buffer*)realloc(*array, sizeof(buffer) * (i + 1));
if (!tmp)
printf("Failed realloc\n");
*array = tmp;
// First line is ignored, second line is taken as data.
getline(&buf, &size, file);
n = getline(&buf, &size, file);
if (n > 0) {
void* _tmp = malloc(sizeof(char) * n);
if (!_tmp)
printf("Failed malloc\n");
array[i]->string = (char*) _tmp;
array[i]->len = n-1;
strncpy(array[i]->string, buf, n-1);
}
i++;
if (feof(file)) {
printf("saw end of file, leaving.\n");
break;
}
}
return i;
}
int main(int argc, char* argv[]) {
char *filename = argv[1];
buffer *array = (buffer*) calloc(1, sizeof(buffer));
int num = read_into_array(filename, &array);
}
Apologies for the somewhat poor formatting, I've been trying to figure this out for a while.
Since it seems to work for the first few lines, my assumption is that I'm going wrong somewhere in the realloc calculation. My other guess is that I'm somehow using/reading the file incorrectly.
Thanks for any help. For posterity, the file looks something like this https://hastebin.com/vinidiyita.sm (the real file is thousands of lines long).
when you do *array=tmp you're allocating memory for array[0]
then you're using array[i] that should be a pointer to a buffer, but points to garbage or 0
You're confusing two ways to use data.
The first is by using arrays - there's the non-dynamic:
buffer array[x] = {0};
int num = read_into_array(filename, &array);
then you can use array[i]
and there's the dynamic type:
buffer **array = calloc(initial_len*sizeof(buffer *));
int num = read_into_array(filename, array, initial_len);
read_into_array(char *filename, buffer **&array, int initial_len)
{
int len = initial_len;
...
while()
{
...
if(i>len)
{
array = realloc(array, sizeof(buffer*) * (i + 1));
len = i;
}
array[i] = calloc(sizeof(buffer));
}
}

read int** from file C

I have to read a file in C and create an int**.
This is the file:
2
-1,1,1,0,0,1
1,-1,0,1,0
I'm doing this:
FILE *fp = fopen("grafo.txt", "r");
char line[100];
int numLinea = 0;
char** tokens;
while (1) {
if (fgets(line,150, fp) == NULL) break;
if(numLinea == 0){
NUMERO_NODOS = atoi( line );
nodos = (int **)malloc (NUMERO_NODOS*sizeof(int *));
}else{
tokens = str_split(line, ',');
if (tokens) {
for (int i = 0; *(tokens + i); i++) {
char* contactoNodo;
strcpy(contactoNodo, *(tokens + i));
int numNodo = numLinea-1;
nodos[numNodo] = (int *) malloc (NUMERO_NODOS*sizeof(int));
nodos[numNodo][i] = atoi(contactoNodo);
printf("nodos[%i][%i] = %i\n",numNodo,i,nodos[numNodo][i]);
printf("nodos[0][0] = %i\n",nodos[0][0]);
//free(contactoNodo);
}
printf("nodos[0][0] = %i\n",nodos[0][0]);
//free(tokens);
}
}
numLinea++;
//printf("%3d: %s", i, line);
}
And this is the output:
nodos[0][0] = -1
nodos[0][0] = -1
nodos[0][1] = 1
nodos[0][0] = -1163005939
(...)
Why is nodos[0][0] = -1163005939 in the second iteration of the for loop?
SOLUTION
LOL, it was that:
if(i==0){
nodos[numNodo] = (int *) malloc (NUMERO_NODOS*sizeof(int));
}
I can't believe I didn't see it. Thanks MikeCAT!!!
Fatal errors:
You invoked undefined behavior by using value of uninitialized variable contactoNodo having automatic storage duration, which is indeteminate.
You threw away what is read in the first iteration by allocating new buffer and overwriting the pointer to old buffer by it, and invoked undefined behavior again by reading contents of buffer allocated via malloc and not initialized.
Warnings:
You should pass correct (equals or less than the actual buffer size) buffer size to fgets() to avoid buffer overrun.
They say you shouldn't cast the result of malloc() in C.
Try this:
FILE *fp = fopen("grafo.txt", "r");
char line[100];
int numLinea = 0;
char** tokens;
while (1) {
/* use correct buffer size to avoid buffer overrun */
if (fgets(line,sizeof(line), fp) == NULL) break;
if(numLinea == 0){
NUMERO_NODOS = atoi( line );
/* remove cast of what is returned from malloc() */
nodos = malloc (NUMERO_NODOS*sizeof(int *));
}else{
tokens = str_split(line, ',');
if (tokens) {
for (int i = 0; *(tokens + i); i++) {
char contactoNodo[100]; /* allocate buffer statically */
strcpy(contactoNodo, *(tokens + i));
int numNodo = numLinea-1;
if (i == 0) { /* allocate buffer in only the first iteration */
/* remove cast of what is returned from malloc() */
nodos[numNodo] = malloc (NUMERO_NODOS*sizeof(int));
}
nodos[numNodo][i] = atoi(contactoNodo);
printf("nodos[%i][%i] = %i\n",numNodo,i,nodos[numNodo][i]);
printf("nodos[0][0] = %i\n",nodos[0][0]);
/* do not free() what is not allocated via memory management functions such as malloc() */
}
printf("nodos[0][0] = %i\n",nodos[0][0]);
//free(tokens);
}
}
numLinea++;
//printf("%3d: %s", i, line);
}

realloc(): invalid next size: followed by a 32bit register

so I've been writing an mtf encoder in C and I've been running into a realloc() error regardless of what I do. I've checked to see if there was an error in my logic (and there may be) by using print statements to see if I'm overstepping the bounds of my currently malloc'd array (adding a string past my original array size) and that doesn't seem to be the issue. I've used GDB and Valgrind and GDB gives me a cryptic message while Valgrind runs into a segmentation fault. This is my first time using dynamic memory and I'm pretty confused as to what the problem is, below are my code along with the GDB error:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int count = 0;
move_to_front(int index, char** words){
int i;
char *t = words[index];
for(i = index; i>1; i--){
words[i] = words[i-1];
}
words[1] = t;
}
char** reallocate_words(char** words, int* words_size_pointer){
printf("We're entering here\n");
printf("%d", *words_size_pointer);
int temp = *words_size_pointer;
char** tempor;
temp = temp*2;
printf("%d", temp);
tempor = (char**) realloc(words, temp);
int i = *words_size_pointer;
for(i; i<temp; i++){
tempor[i] = (char*) malloc(120);
}
words_size_pointer = &temp;
return tempor;
}
void encode_word(int* words_size_pointer, FILE *f, char* word, char** words){
if(count == 0){
words[1] = word;
fputs(words[1], f);
count++;
}
int i;
for(i=0; i<=count; i++){
if(strcmp(words[i], word) == 0){
break;
}
}
if(i>=(*words_size_pointer)){
printf("%d\n", i);
words = reallocate_words(words, words_size_pointer);
words[count+1] = word;
count++;
fputc(count+128, f);
fputs(words[count], f);
move_to_front(count, words);
}
if(i>count){
words[count+1] = word;
count++;
fputc(count+128, f);
fputs(words[count], f);
move_to_front(count, words);
}
else{
fputc(i+128, f);
move_to_front(i, words);
}
}
void sep_words(char** words, char *line, int* words_size_pointer, FILE *f){
char* x;
int i = 0;
x = strtok(line, " ");
while(x != NULL){
encode_word(words_size_pointer,f, x, words);
x = strtok(NULL, " ");
}
}
void readline(FILE *f_two, FILE *f, char** words, int* words_size_pointer){
char *line;
size_t len = 0;
ssize_t temp;
int count;
do{
temp = getline(&line,&len,f);
printf("%s", line);
if(temp!= -1){
sep_words(words, line, words_size_pointer, f_two);
}
}while(temp!=-1);
}
int main(int argc, char *argv[]){
int x;
int i;
int j;
x = strlen(argv[1]);
char fi[x];
char mtf[3] = "mtf";
FILE *f;
FILE *f_two;
for(j = 0; j<(x-3); j++){
fi[j] = argv[1][j];
}
strcat(fi, mtf);
f = fopen(argv[1], "r");
f_two = fopen(fi, "w");
fputc(0xFA, f_two);
fputc(0XCE, f_two);
fputc(0XFA, f_two);
fputc(0XDF, f_two);
if(f == NULL){
return 1;
}
char** words;
words = (char **) malloc(20);
for(i = 0; i<20; i++){
words[i] = (char*) malloc(120);
}
int words_size = 20;
int* words_size_pointer = &words_size;
readline(f_two, f, words, words_size_pointer);
return 0;
}
And as for the GDB error:
*** Error in `/file_loc/mtfcoding2': realloc(): invalid next size: 0x0000000000603490 ***
2040 \\This is due to print statements within my function.
Program received signal SIGABRT, Aborted.
0x00007ffff7a4acc9 in __GI_raise (sig=sig#entry=6)
at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
Thank you for your time! :)
malloc and realloc require the number of bytes as argument. However you are writing code like:
char** words;
words = (char **) malloc(20);
for(i = 0; i<20; i++){
words[i] = (char*) malloc(120);
You allocate 20 bytes but then you write 20 pointers (which probably takes 80 bytes). To fix this you need to compute how many bytes are required to store the 20 pointers. A safe way of doing this is to use malloc as recommended by SO:
words = malloc(20 * sizeof *words);
You have the same problem in your realloc call.
This line has no effect: words_size_pointer = &temp; . Perhaps you meant *words_size_pointer = temp; . Make sure you clearly understand the difference between those two lines.
NB. There may be other errors.
Well, for starters, your move_to_front is dropping pointers. This one is a pretty bad memory leak, and given the nature of C and memory leaks, could be the cause of your segfault (for now). You should be doing this
for(i = index; i > 1; i--){
char* tmp = words[i];
words[i] = words[i-1];
words[i-1] = tmp;
}
Otherwise, what you have done is overwritten the pointers from index to words[2] with the pointer at index. Also, you seem to like to start your words array at 1 instead of 0. Those off-by-one errors are gonna hurt ya too.
Also (as stated in my earlier comment), words_size_pointer = &temp; isn't quite right. Do this instead *words_size_pointer = temp;. The first way is only a local pointer re-assignment, but you want the change to be reflected in the caller's scope, so you must dereference the pointer and modify it.
It seems to be caused by your call to getline in your readline function.
char *line;
size_t len = 0;
...
temp = getline(&line,&len,f);
getline requires line to be NULL (in which case the value of len is ignored) or line must be a pointer returned by malloc, calloc, or realloc. If line is not NULL, and len isn't large enough, line is resized by calling realloc. This is the crucial point: line points to some random address that wasn't returned by malloc, so getline attempts to use realloc to increase the buffer size because 0 bytes is just too small.
You also have a buffer overflow here:
char mtf[3] = "mtf";
...
for(j = 0; j<(x-3); j++){
fi[j] = argv[1][j];
}
strcat(fi, mtf);
Because you made mtf only 3 bytes in size, it may or may not be followed immediately by a null terminator. When strcat is called, if mtf isn't followed immediately by a 0 byte in memory, you end up with something like sample.mtf\x1b\x01X as the output filename, assuming you don't write too far beyond the end of the fi array to crash the program with a SIGSEGV (segfault). Any of the following will correct it:
char mtf[4] = "mtf";
//OR
char mtf[] = "mtf";
//OR
const char *mtf = "mtf";
However, because fi is only made up of x number of bytes, you'll end up writing to fi[x] with your null terminator that strcat adds. This is a problem because char fi[x]; means you only have array indices 0 to x - 1 available. Fix this part by using x = strlen(argv[1]) + 1.

Why does realloc fail on repeated calls where as allocating a big chunk using malloc works?

I am trying to read in from stdin (passing in value from a file). I am reading each character from the string and storing it into a dynamically allocated string pointer. When needed I realloc the memory. I am trying to get as many characters as possible. Though I can limit it to 100,000 chars. But the realloc fails after some iteration. But if I specify a chunk size big, say 1048567 during the first initialization in malloc, I am able to read the string completely. Why is this?
Below is my program:
#include <stdio.h>
#include <stdlib.h>
int display_mem_alloc_error();
enum {
CHUNK_SIZE = 31 //31 fails. But 1048567 passes.
};
int display_mem_alloc_error() {
fprintf(stderr, "\nError allocating memory");
exit(1);
}
int main(int argc, char **argv) {
int numStr; //number of input strings
int curSize = CHUNK_SIZE; //currently allocated chunk size
int i = 0; //counter
int len = 0; //length of the current string
int c; //will contain a character
char *str = NULL; //will contain the input string
char *str_cp = NULL; //will point to str
char *str_tmp = NULL; //used for realloc
str = malloc(sizeof(*str) * CHUNK_SIZE);
if (str == NULL) {
display_mem_alloc_error();
}
str_cp = str; //store the reference to the allocated memory
scanf("%d\n", &numStr); //get the number of input strings
while (i != numStr) {
if (i >= 1) { //reset
str = str_cp;
len = 0;
curSize = CHUNK_SIZE;
}
c = getchar();
while (c != '\n' && c != '\r') {
*str = (char *) c;
//printf("\nlen: %d -> *str: %c", len, *str);
str = str + 1;
len = len + 1;
*str = '\0';
c = getchar();
if (curSize / len == 1) {
curSize = curSize + CHUNK_SIZE;
//printf("\nlen: %d", len);
printf("\n%d \n", curSize); //NB: If I comment this then the program simply exits. No message is displayed.
str_tmp = realloc(str_cp, sizeof(*str_cp) * curSize);
if (str_tmp == NULL) {
display_mem_alloc_error();
}
//printf("\nstr_tmp: %d", str_tmp);
//printf("\nstr: %d", str);
//printf("\nstr_cp: %d\n", str_cp);
str_cp = str_tmp;
str_tmp = NULL;
}
}
i = i + 1;
printf("\nlen: %d", len);
//printf("\nEntered string: %s\n", str_cp);
}
str = str_cp;
free(str_cp);
free(str);
str_cp = NULL;
str = NULL;
return 0;
}
Thanks.
When you realloc
str_tmp = realloc(str_cp, sizeof(*str_cp) * curSize);
if (str_tmp == NULL) {
display_mem_alloc_error();
}
//printf("\nstr_tmp: %d", str_tmp);
//printf("\nstr: %d", str);
//printf("\nstr_cp: %d\n", str_cp);
str_cp = str_tmp;
str_tmp = NULL;
you let str_cp point to the new block of memory, but str still points into the old, now freed block. Thus when you access what str points to in the next iteration, you invoke undefined behaviour.
You need to save the offset of str with respect to str_cp, and after the reallocation, letstr point into the new block at its old offset.
And *str = (char *) c; is wrong, although there is a nonzero chance of it being functionally equivalent to the correct *str = c;.
*str = (char *) c;
This line is wrong.
str is a pointer to char and *str is a char but you are assigning a pointer to char to a char. This cannot be done in C.
Moreover:
scanf("%d\n", &numStr);
The \n in scanf call probably does not what you expect:
http://c-faq.com/stdio/scanfhang.html
And also:
str = str_cp;
free(str_cp);
free(str);
You have a double free here. After the assignment str and str_cp will have the same value so doing:
free(str_cp);
free(str);
is as if you do:
free(str);
free(str);
which is undefined behavior (you cannot free twice).

Resources