In this function, I have leak of memory
static int read_buffer(int const fd, char **buffer)
{
char buff[BUFF_SIZE + 1];
int ret;
ret = read(fd, buff, BUFF_SIZE);
if (ret > 0)
{
buff[ret] = 0;
if (!(*buffer = ft_strjoin(*buffer, buff)))
return (-1);
}
return (ret);
}
I tried to do this and more solution again.. But I get also leak of memory
static int read_buffer(int const fd, char **buffer)
{
char buff[BUFF_SIZE + 1];
char *tmp;
int ret;
ret = read(fd, buff, BUFF_SIZE);
tmp = *buffer;
if (ret > 0)
{
buff[ret] = 0;
if (!(*buffer = ft_strjoin(*buffer, buff)))
return (-1);
free(tmp);
}
return (ret);
}
Here is ft_strjoin function: (can't modify)
char *ft_strjoin(char const *s1, char const *s2)
{
size_t size_s1;
size_t size_s2;
char *strjoin;
size_s1 = ft_strlen(s1);
size_s2 = ft_strlen(s2);
if (!(strjoin = malloc(size_s1 + size_s2 + 1)))
return (NULL);
ft_strcpy(strjoin, s1);
ft_strcat(strjoin, s2);
return (strjoin);
}
How I can resolve this ?
Thank you !
Anytime a function returns malloc'd memory, you've got a hot potato. You have to free that memory and cannot pass it on unless you've documented you're doing so. And you mustn't lose hold of (e.g. reuse) the pointer before freeing it. Below is how I interpret how read_buffer() needs to work. Since "buffer length is unknown" I'm assuming it comes from malloc and we can use realloc:
static int read_buffer(int const fd, char **buffer)
{
char local_buffer[BUFF_SIZE + 1];
int bytes_read = read(fd, local_buffer, BUFF_SIZE);
if (bytes_read > 0)
{
local_buffer[bytes_read] = '\0';
char *joined = ft_strjoin(*buffer, local_buffer);
if (joined != NULL)
{
*buffer = realloc(*buffer, strlen(joined) + 1);
if (*buffer != NULL)
{
ft_strcpy(*buffer, joined);
}
else
{
bytes_read = -1;
}
free(joined);
}
else {
bytes_read = -1;
}
}
return bytes_read;
}
Note that there are a lot of potential pitfalls that you need to test for and recover from.
If you're reading from the user directly, and don't want newlines as part of the concatenation of strings, you can replace this line:
local_buffer[bytes_read] = '\0';
with something like:
if (local_buffer[bytes_read - 1] == '\n')
{
local_buffer[bytes_read - 1] = '\0';
}
else
{
local_buffer[bytes_read] = '\0';
}
Assuming the above newline fix, here's a small test program I wrote:
int main() {
char *buffer = malloc(1);
*buffer = '\0';
for (int i = 0; i < 3; i++)
{
(void) read_buffer(0, &buffer);
puts(buffer);
}
free(buffer);
return 0;
}
RESULT
> ./a.out
abcdefg
abcdefg
hijklmn
abcdefghijklmn
opqrstu
abcdefghijklmnopqrstu
>
Related
So I have this function
char *read_file(int fd)
{
char *str;
char *buffer;
int bytes;
buffer = malloc(sizeof(char) * 101);
if (!buffer)
return (NULL);
bytes = read(fd, buffer, 100);
while (bytes != 0)
{
if (bytes == -1)
{
free(buffer);
return (NULL);
}
buffer[bytes] = '\0';
str = ft_strjoin(str, buffer);
bytes = read(fd, buffer, 100);
}
free(buffer);
return (str);
}
It calls this function ft_strjoin
char *ft_strjoin(char *s1, char *s2)
{
char *str;
if (!s1 && !s2)
return (ft_strdup(""));
if (s1 && !s2)
{
str = ft_strdup(s1);
free(s1);
return (str);
}
if (!s1 && s2)
return (ft_strdup(s2));
if (!s2[0])
return (s1);
str = malloc(sizeof(char) * (ft_strlen(s1) + ft_strlen(s2) + 1));
if (!str)
return (NULL);
ft_memmove(str, s1, ft_strlen(s1));
ft_memmove(str + ft_strlen(s1), s2, ft_strlen(s2));
str[ft_strlen(s1) + ft_strlen(s2)] = '\0';
free(s1);
return (str);
}
The reason why I free s1 is because the previous string will be malloced so I free the previous string and return the new string. How ever it's causing an error free() invalid pointer. If I remove the free(s1) at the end of ft_strjoin the error goes away but I need to free the previous string, so what do I do. ft_strdup just mallocs a new string, duplicates the argument, and copies it to the new malloced string and returns the new malloced string. ft_memmove does what memmove does.
Your function can be reduced to:
char *read_file(int fd)
{
char *str = NULL;
struct stat st;
if(fstat(fd, &st) != -1)
{
str = malloc(st.st_size);
if(str)
{
if(read(fd, str, st.st_size) != st.st_size)
{
/* error handling */
}
}
}
return str;
}
ft_strjoin is much too complicated. You should not free any memory in this kind general function. Use memcpy (or your version) to copy.
char *ft_strjoin(const char * restrict s1, const char * restrict s2)
{
char *str;
size_t s1len = s1 ? ft_strlen(s1) : 0, s2len = s2 ? ft_strlen(s2) : 0;
size_t newsize = s1len + s2len + 1;
str = malloc(newsize);
if(str)
{
if(s1) ft_memcpy(str, s1, s1len);
if(s2) ft_memcpy(str + s1len, s2, s2len);
str[newsize - 1] = 0;
}
return str;
}
I am learning c programming. Below program is showing me the output, But when it execute free method . It is give me error:- free(): invalid next size (normal) . Please let me know what i am missing.
#include<stdio.h>
#include<stdlib.h>
int main() {
FILE *fp;
char r[1024];
fp = popen("/bin/ls /etc/", "r");
if (fp == NULL) {
perror("Failed to run the command");
exit(-1);
}
int totallengthread = 0, alloc_size = 1024;
char *buffer = (char*) calloc(alloc_size , sizeof(char));
int lenofbuff = 0;
while((lenofbuff=fread(r,sizeof(char),1024,fp))>0){
totallengthread += lenofbuff;
if (totallengthread >= alloc_size) {
alloc_size += 1024;
buffer = realloc(buffer, alloc_size);
}
concat(buffer, r);
}
printf("this is the output =>%s", buffer);
pclose(fp);
free(buffer);
return 0;
}
void concat(char *dest, const char *source) {
char *d = dest;
char *s = source;
while (*d != '\0') {
d++;
}
while (*s != '\0') {
*d++ = *s++;
}
*d = '\0';
}
In modern C, routines must be declared before they are used. Either move the definition of concat before main or insert a declaration of concat before main.
Change int main() to int main(void).
fread does not add a null terminator to the data read. Change char r[1024]; to char r[1025]; and, after fread, insert r[lenofbuff] = '\0'; as the first statement inside the while body.
if (totallengthread >= alloc_size) does not account for the null terminator. Change it to if (totallengthread+1 >= alloc_size)`.
In concat, change char *s = source; to const char *s = source;.
Turn on compiler warnings and pay attention to them. They should have warned you about 1 and 5 above.
After char *buffer = (char*) calloc(alloc_size, sizeof(char));, test buffer == NULL. If it is, print an error and exit. Also, a better form for this statement is char *buffer = calloc(alloc_size, sizeof *buffer);. Casting the result of calloc is not needed in C, and basing the size on the thing being allocated rather than a repetition of the type may be safer if the type is changed in the future.
Change buffer = realloc(buffer, alloc_size); to char *temp = realloc(buffer, alloc_size * sizeof *buffer); if (temp == NULL) { print message and exit } else buffer = temp;.
Attaching the corrected code suggested by Eric Postpischil. It is working fine now.
#include<stdio.h>
#include<stdlib.h>
void concat(char *dest, const char *source);
int main(void) {
FILE *fp;
char r[1024];
fp = popen("/bin/ls /etc/", "r");
if (fp == NULL) {
perror("Failed to run the command");
exit(-1);
}
int totallengthread = 0, alloc_size = 1024;
char *buffer = (char*) calloc(alloc_size, sizeof(char));
if (buffer == NULL) {
perror("Failed allocate memory");
exit(-1);
}
int lenofbuff = 0;
while ((lenofbuff = fread(r, sizeof(char), 1023, fp)) > 0) {
r[lenofbuff] = '\0';
totallengthread += lenofbuff;
if ((totallengthread) >= alloc_size) {
alloc_size += 1024;
buffer = realloc(buffer, alloc_size*sizeof(char));
if (buffer == NULL) {
perror("Failed to extend memory");
exit(-1);
}
}
concat(buffer, r);
}
printf("this is the output =>%s", buffer);
pclose(fp);
free(buffer);
return 0;
}
void concat(char *dest, const char *source) {
char *d = dest;
const char *s = source;
while (*d != '\0') {
d++;
}
while (*s != '\0') {
*d++ = *s++;
}
*d = '\0';
}
I am relatively new to C and malloc. I wrote a lib with basic functions that I am filling with new ones as I go and that I am using for other small projects like this one.
I heard about Valgrind and decided to check my program with it but can't quite understand why I have so many leaks, I feel like all my mallocs are protected with if (line == NULL) when using functions using malloc themselves.
Could you guys put me back on tracks here ?
static char *concator(char *s1, char *s2, size_t len)
{
char *line;
size_t size;
if (!s1 || !s2)
return (NULL);
size = strlen(s1) + strlen(s2);
line = (char*)memalloc(sizeof(char) * size + 1);
if (line == NULL)
return (NULL);
strcpy(line, s1);
strncat(line, s2, len);
strdel(&s1);
return (line);
}
int line_reader(const int fd, char **line)
{
static char buf[BUFF_SIZE];
char *pos;
int ret;
if (fd < 0 || !line || read(fd, buf, 0) < 0 || BUFF_SIZE < 1)
return (-1);
*line = strnew(0);
if (line == NULL)
return (-1);
while (1)
{
pos = strchr(buf, '\n');
if (pos)
{
*line = concator(*line, buf, pos - buf);
if (line == NULL)
return (-1);
strncpy(buf, &buf[pos - buf + 1], BUFF_SIZE - (pos - buf));
return (1);
}
*line = concator(*line, buf, BUFF_SIZE);
if (line == NULL)
return (-1);
ret = read(fd, buf, BUFF_SIZE);
buf[ret] = '\0';
if (!ret)
return ((**line) ? 1 : 0);
}
}
Free line after you're done using it, like:
char* line;
int rc = line_reader(fd, &line);
if(rc > 0)
{
//use line
printf("%s\n", line);
//then free it
free(line);
}
This should fix the memory leak problems reported by valgrind. However watch out for other errors mentioned in the comments.
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 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.