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*))
Related
I am working on a HW assignment dealing with a dynamically allocated array that can handle different sized input files. My issue is that a pair of (null) values keep appearing tied to the word_alloc variable (changing it moves where in the output the (null) appears). How do I fix this?
int main(int argc, char *argv[]) {
char str[80];
int word_alloc = 0;
int word_count = 0;
char **str_array;
FILE *file;
file = fopen("hw3-data.txt", "r");
// Allocate memory to the array of strings (char arrays)
word_alloc = 10;
str_array = (char **) malloc(sizeof(char*) * word_alloc);
while (fscanf(file, "%s", str) != EOF) {
//doubles the word_alloc/str_array if we have more words than allocated
if(word_count > word_alloc) {
word_alloc *= 2;
str_array =(char **) realloc(str_array, sizeof(char*) * word_alloc);
}
str_array[word_count] = (char *) malloc(sizeof(char) * (strlen(str) + 1));
strcpy(str_array[word_count], str);
++word_count;
}
return 0;
}
The errors happen because you have array overflow. Change
if(word_count > word_alloc)
to
if(word_count >= word_alloc)
to fix it.
I have to recode an implementation of the getline() function, but using the file descriptor of the file and not a FILE *. I am only allowed to use malloc() and free(), along with 5 functions being 25 lines long at most.
I think I've done correctly the project although I am a beginner in C and my code isn't probably good.
When I run it, it works fine, but valgrind shows that I definetely lost x bytes, x depending of the file length and the READ_SIZE (macro defined in the header).
According to valgrind's --leak-check=full, I have a memory leak in the str_realloc_cat function, when I malloc dest. I tried but couldn't find where should I free / do something else?
Here below is my code:
char *get_next_line(const int fd)
{
static char *remaining = "";
char *buffer;
ssize_t cread;
size_t i;
i = 0;
if (remaining == NULL)
return (NULL);
if ((buffer = malloc(SOF(char) * READ_SIZE + 1)) == NULL ||
(cread = read(fd, buffer, READ_SIZE)) < 0)
return (NULL);
buffer[cread] = 0;
remaining = str_realloc_cat(remaining, buffer);
while (remaining[i])
{
if (remaining[i] == 10)
{
remaining[i] = 0;
buffer = str_create_cpy(remaining);
remaining = remaining + i + 1;
return (buffer);
}
i++;
}
return (check_eof(fd, buffer, remaining, cread));
}
char *str_realloc_cat(char *rem, char *buf)
{
size_t i;
size_t dest_i;
char *dest;
i = (dest_i = 0);
if ((dest = malloc(SOF(char) * (str_len(rem) + str_len(buf) + 1))) == NULL)
return (NULL);
while (rem[i])
{
dest[dest_i] = rem[i];
dest_i++;
i++;
}
i = 0;
while (buf[i])
{
dest[dest_i] = buf[i];
dest_i++;
i++;
}
dest[dest_i] = 0;
free(buf);
return (dest);
}
char *check_eof(const int fd, char *buffer, char *remaining, ssize_t cread)
{
if (cread == 0)
return (NULL);
if (cread < READ_SIZE)
{
buffer = remaining;
remaining = NULL;
return (buffer);
}
return (get_next_line(fd));
}
char *str_create_cpy(const char *src)
{
char *dest;
size_t i;
i = 0;
if ((dest = malloc(sizeof(char) * str_len(src) + 1)) == NULL)
return (NULL);
while (src[i])
{
dest[i] = src[i];
i++;
}
dest[i] = 0;
return (dest);
}
int str_len(const char *str)
{
size_t i;
i = 0;
while (str[i])
i++;
return (i);
}
And a main functon if you would like to test:
#define SOF(x) sizeof(x) // Why in the comments
int main(int ac, char **av)
{
int fd;
char *s;
UNUSED(ac);
if (!av[1])
return 1;
fd = open(av[1], O_RDONLY);
while ((s = get_next_line(fd)))
{
printf("%s\n", s);
free(s);
}
close(fd);
}
Your algorithm is bad:
You keep the buffer in a allocate memory
You don't use a structure to regroup your variable
You use magic number remaining[i] == 10
You use recursive you can stack overflow return get_next_line(fd). Never mind, I didn't read well you have a tail recursive, just be sure to have the optimization on your compile for it.
You have Spaghetti code.
etc.
You should rewrite your whole function with a better logic first use this structure:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define GNL_SIZE 4096
struct gnl_context {
char buffer[GNL_SIZE];
size_t i;
size_t read;
};
char *get_next_line_r(int fd, struct gnl_context *gnl_context);
char *get_next_line(int fd);
static char *read_buffer(struct gnl_context *gnl_context, char *str,
size_t *size) {
size_t i = gnl_context->i;
while (i < gnl_context->read && gnl_context->buffer[i] != '\n') {
i++;
}
size_t j = i - gnl_context->i;
char *ret = realloc(str, *size + j + 1);
if (ret == NULL) {
return NULL;
}
memcpy(ret + *size, gnl_context->buffer + gnl_context->i, j);
*size += j;
ret[*size] = '\0';
gnl_context->i = i;
return ret;
}
char *get_next_line_r(int fd, struct gnl_context *gnl_context) {
char *str = NULL;
size_t size = 0;
loop:
if (gnl_context->i == gnl_context->read) {
ssize_t ret = read(fd, gnl_context->buffer, GNL_SIZE);
if (ret <= 0) {
return str;
}
gnl_context->read = (size_t)ret;
gnl_context->i = 0;
}
char *tmp = read_buffer(gnl_context, str, &size);
if (tmp == NULL) {
return str;
}
if (gnl_context->i != gnl_context->read) {
gnl_context->i++;
return tmp;
}
str = tmp;
goto loop;
}
char *get_next_line(int fd) {
static struct gnl_context gnl_context;
return get_next_line_r(fd, &gnl_context);
}
int main(void) {
char *str;
while ((str = get_next_line(0)) != NULL) {
printf("%s\n", str);
free(str);
}
}
I am concerned about this line:
remaining = remaining + i + 1;
remaining is a pointer to the allocated buffer. On this line, you destroy it, which means that you cannot free() it anymore.
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));
}
}
I have to recode an implementation of the getline() function, but using the file descriptor of the file and not a FILE *. I am only allowed to use malloc() and free(), along with 5 functions being 25 lines long at most.
I think I've done correctly the project although I am a beginner in C and my code isn't probably good.
When I run it, it works fine, but valgrind shows that I definetely lost x bytes, x depending of the file length and the READ_SIZE (macro defined in the header).
According to valgrind's --leak-check=full, I have a memory leak in the str_realloc_cat function, when I malloc dest. I tried but couldn't find where should I free / do something else?
Here below is my code:
char *get_next_line(const int fd)
{
static char *remaining = "";
char *buffer;
ssize_t cread;
size_t i;
i = 0;
if (remaining == NULL)
return (NULL);
if ((buffer = malloc(SOF(char) * READ_SIZE + 1)) == NULL ||
(cread = read(fd, buffer, READ_SIZE)) < 0)
return (NULL);
buffer[cread] = 0;
remaining = str_realloc_cat(remaining, buffer);
while (remaining[i])
{
if (remaining[i] == 10)
{
remaining[i] = 0;
buffer = str_create_cpy(remaining);
remaining = remaining + i + 1;
return (buffer);
}
i++;
}
return (check_eof(fd, buffer, remaining, cread));
}
char *str_realloc_cat(char *rem, char *buf)
{
size_t i;
size_t dest_i;
char *dest;
i = (dest_i = 0);
if ((dest = malloc(SOF(char) * (str_len(rem) + str_len(buf) + 1))) == NULL)
return (NULL);
while (rem[i])
{
dest[dest_i] = rem[i];
dest_i++;
i++;
}
i = 0;
while (buf[i])
{
dest[dest_i] = buf[i];
dest_i++;
i++;
}
dest[dest_i] = 0;
free(buf);
return (dest);
}
char *check_eof(const int fd, char *buffer, char *remaining, ssize_t cread)
{
if (cread == 0)
return (NULL);
if (cread < READ_SIZE)
{
buffer = remaining;
remaining = NULL;
return (buffer);
}
return (get_next_line(fd));
}
char *str_create_cpy(const char *src)
{
char *dest;
size_t i;
i = 0;
if ((dest = malloc(sizeof(char) * str_len(src) + 1)) == NULL)
return (NULL);
while (src[i])
{
dest[i] = src[i];
i++;
}
dest[i] = 0;
return (dest);
}
int str_len(const char *str)
{
size_t i;
i = 0;
while (str[i])
i++;
return (i);
}
And a main functon if you would like to test:
#define SOF(x) sizeof(x) // Why in the comments
int main(int ac, char **av)
{
int fd;
char *s;
UNUSED(ac);
if (!av[1])
return 1;
fd = open(av[1], O_RDONLY);
while ((s = get_next_line(fd)))
{
printf("%s\n", s);
free(s);
}
close(fd);
}
Your algorithm is bad:
You keep the buffer in a allocate memory
You don't use a structure to regroup your variable
You use magic number remaining[i] == 10
You use recursive you can stack overflow return get_next_line(fd). Never mind, I didn't read well you have a tail recursive, just be sure to have the optimization on your compile for it.
You have Spaghetti code.
etc.
You should rewrite your whole function with a better logic first use this structure:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define GNL_SIZE 4096
struct gnl_context {
char buffer[GNL_SIZE];
size_t i;
size_t read;
};
char *get_next_line_r(int fd, struct gnl_context *gnl_context);
char *get_next_line(int fd);
static char *read_buffer(struct gnl_context *gnl_context, char *str,
size_t *size) {
size_t i = gnl_context->i;
while (i < gnl_context->read && gnl_context->buffer[i] != '\n') {
i++;
}
size_t j = i - gnl_context->i;
char *ret = realloc(str, *size + j + 1);
if (ret == NULL) {
return NULL;
}
memcpy(ret + *size, gnl_context->buffer + gnl_context->i, j);
*size += j;
ret[*size] = '\0';
gnl_context->i = i;
return ret;
}
char *get_next_line_r(int fd, struct gnl_context *gnl_context) {
char *str = NULL;
size_t size = 0;
loop:
if (gnl_context->i == gnl_context->read) {
ssize_t ret = read(fd, gnl_context->buffer, GNL_SIZE);
if (ret <= 0) {
return str;
}
gnl_context->read = (size_t)ret;
gnl_context->i = 0;
}
char *tmp = read_buffer(gnl_context, str, &size);
if (tmp == NULL) {
return str;
}
if (gnl_context->i != gnl_context->read) {
gnl_context->i++;
return tmp;
}
str = tmp;
goto loop;
}
char *get_next_line(int fd) {
static struct gnl_context gnl_context;
return get_next_line_r(fd, &gnl_context);
}
int main(void) {
char *str;
while ((str = get_next_line(0)) != NULL) {
printf("%s\n", str);
free(str);
}
}
I am concerned about this line:
remaining = remaining + i + 1;
remaining is a pointer to the allocated buffer. On this line, you destroy it, which means that you cannot free() it anymore.
I have to dynamically allocate array of words. Words are stored in a file separated by variable count of white-space characters. I don't know how many words is in the file a they can have variable length.
I have this code:
void readWord(FILE* stream, char *word, char first_c) {
word[0] = first_c;
char val;
int wlen = 1;
// isWhitespac is my function - tests if char is blank or '\n'
while ((val = fgetc(stream)) != EOF && isWhitespace(val) == 0) {
wlen++;
word = realloc(word, (wlen+1) * sizeof (char));
word[wlen-1] = val;
}
word[wlen] = '\0';
}
int readList(const char *file) {
FILE* f;
char **arr;
char val;
int wcount = 0;
arr = malloc(sizeof (char*));
f = fopen(file, "r");
while (fscanf(f, " %c", &val) == 1) {
wcount++;
arr = realloc(arr, wcount * sizeof (char *));
arr[wcount - 1] = malloc(sizeof (char));
readWord(f, arr[wcount-1], val);
printf("%s\n", arr[wcount-1]);
}
for (int i = 0; i < wcount; ++i) {
free(arr[i]);
}
free(arr);
fclose(f);
return 0;
}
It appears to work fine, it reads a prints all the words. But when I run the program with Valgrind the are too many errors, which I can't find. Could anyone help me? (I know I have to test if malloc and others went fine, it is just a test func.)
The Valgrind log is quite long, should I post it too?
One of the issues is that you do realloc inside readWord. If realloc allocates a new buffer and doesn't just extend the current one then your code will crash (you will double free the pointer) and this is what Valgrind picks up. To fix this I would rewrite the code so it returns a pointer instead of void.
char * readWord(FILE* stream, char *word, char first_c) {
word[0] = first_c;
char val;
int wlen = 1;
// isWhitespac is my function - tests if char is blank or '\n'
while ((val = fgetc(stream)) != EOF && isWhitespace(val) == 0) {
wlen++;
word = realloc(word, (wlen+1) * sizeof (char));
word[wlen-1] = val;
}
word[wlen] = '\0';
return word;
}
And then change the loop in readList to this:
while (fscanf(f, " %c", &val) == 1) {
wcount++;
arr = realloc(arr, wcount * sizeof (char *));
arr[wcount-1]=malloc(sizeof(char));
arr[wcount - 1] = readWord(f, arr[wcount-1], val);
printf("%s\n", arr[wcount-1]);
}