I made a scanning function which essentially just scans in the lines of a file into a single char * called buffer. However, after a couple hundred lines are read, the program just stops working. I just get a program has stopped working pop up window. Assuming I did something wrong with memory allocation, but I am unsure what.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *scan_file(FILE *fp);
#define MAX_LINE 200
int main(void) {
FILE *fp = fopen("test.txt", "r");
char *contents = scan_file(fp);
printf("%s\n", contents);
return 0;
}
// Scan in file into a buffer. Returns a malloc-ed string
char *scan_file(FILE *fp) {
int buf_len = 1;
int contents_len = buf_len;
char *buffer = malloc(sizeof(char) * (MAX_LINE + 1));
char *contents = malloc(sizeof(char) * (buf_len + 1));
strcpy(contents, "\0");
while (fgets(buffer, MAX_LINE, fp) != NULL) {
buf_len = strlen(buffer);
contents_len += buf_len;
realloc(contents ,contents_len);
strcat(contents, buffer);
strcpy(buffer, "\0");
}
free(buffer);
return contents;
}
Code fails to use the return value form realloc()
Allocation sizes off by 1.
Repeated strcat() make for a slow (n*n) solution.
Consider using size_t for array sizes vs. int.
Rather than call the variable ...len, consider ...size to acknowledge the presence of the final null character in a string.
char *scan_file(FILE *fp) {
// int buf_len = 1;
size_t buf_size = 1;
// int contents_len = buf_len;
size_t contents_size = buf_size;
// char *buffer = malloc(sizeof(char) * (MAX_LINE + 1));
// fgets(..., MAX_LINE, ...) will only read up to MAX_LINE - 1 characters.
char *buffer = malloc(MAX_LINE);
char *contents = malloc(buf_size + 1u);
if (buffer == NULL || contents == NULL) {
fprintf(stderr, "Out of memory\n");
return EXIT_FAILURE;
}
// strcpy(contents, "\0");
contents[0] = '\0';
while (fgets(buffer, MAX_LINE, fp) != NULL) {
// buf_len = strlen(buffer);
buf_size = strlen(buffer) + 1u;
// contents_len += buf_len;
// realloc(contents ,contents_len);
void *p = realloc(contents ,contents_size + buf_size);
if (p == NULL) {
fprintf(stderr, "Out of memory\n");
return EXIT_FAILURE;
}
contents = p;
// strcat(contents, buffer);
strcpy(contents + contents_size, buffer);
// now add
contents_size += buf_size;
// Code here not truly needed, yet helps in debugging.
// strcpy(buffer, "\0");
buffer[0] = '\0';
}
free(buffer);
return contents;
}
use realloc()s return value
realloc(NULL, size) works just like malloc(size); no need to pre-allocate
avoid excessive copying and strlen()
just put the data where you want it the first (and only) time you touch it.
#include <stdio.h>
#include <stdlib.h>
char *scanfile (FILE *fp)
{
size_t size, used;
char *buff = NULL;
int ch;
for (size=used=0;; ) {
ch = getc(fp);
if (ch == EOF) break;
if (used+1 >= size) {
size_t newsize = used? 2*used: 1024 ;
char *tmp = realloc(buff, newsize);
if (!tmp) FAIL();
else {buff = tmp; size = newsize; }
}
buff[used++] = ch;
}
/* Nothing read: return NULL */
if (!used) return NULL;
buff[used++] = 0;
/* maybe realloc (shrink) buff here */
return buff;
}
Related
I am trying to enhance the string splitter by splits on : char. Original version can be found at string splitter - how is it working
I do not want to use MAX_TOKEN_SIZE, I want the buffer to be just enough to hold each token. I added malloc and realloc as follows but I am getting free(): double free detected in tcache 2 error which I do not understand. How am I double freeing ? Thanks for all your help.
PS: Based on Gerhardh's comments, I modified the code as follows, but now I am getting segfault.
PS: Based on user3121023's comments, I added parenthesis around *token in 2 places and it works now.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
# define GROWBY 32
const char* splitter(const char *str, char delimiter, char **token) {
size_t i = 0;
size_t buflen = 32;
while (*str) {
if ( i == buflen) {
buflen += GROWBY;
printf("gowing buffer\n");
char *new_token = realloc(*token, buflen * sizeof **token);
if (new_token == NULL){
fprintf(stderr, "Out of Memory");
abort();
}
*token = new_token; //set the new pointer to old pointer
}
char c = *(str++);
if (c == delimiter)
break;
(*token)[i++] = c; //user3121023
}
(*token)[i] = '\0'; /* set the null terminator, user3121023 */
return str;
}
int main(){
const char *env =
"/bin/svsgerertegdfyufdujhdcjxbcn:/sbin:::/usr/bin/46526vw67vxshgshnxxcxhcgbxhcbxn";
while (*env){
char *token = malloc(GROWBY * sizeof(char));
env = splitter(env, ':', &token);
if (token[0] == '\0') {
strcpy(token, "./");
}
printf("%s\n", token) ;
free(token);
}
return 0;
}
Try using strcspn to advance to the next delimiter.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* splitter(const char *str, char *delimiter, char **token) {
size_t buflen = 0;
size_t extra = 0;
buflen = strcspn ( str, delimiter); // characters to next delimiter
extra = 1;
if ( ! buflen) {
extra = 3; // need space for "./" copy in main
}
char *new_token = realloc(*token, ( buflen + extra) * sizeof **token);
if (new_token == NULL){
fprintf(stderr, "Out of Memory");
abort();
}
*token = new_token; //set the new pointer to old pointer
strncpy ( *token, str, buflen);
(*token)[buflen] = 0;
str += buflen;
if ( *str) {
str += 1;
}
return str;
}
int main(void){
const char *env =
"/bin/svsgerertegdfyufdujhdcjxbcn:/sbin:::/usr/bin/46526vw67vxshgshnxxcxhcgbxhcbxn";
while (*env){
char *token = NULL;
env = splitter(env, ":", &token); // note " instead of '
if (token[0] == '\0') {
strcpy(token, "./");
}
printf("%s\n", token) ;
free(token);
}
return 0;
}
hello guys a just need help on this, not showing the text I wrote:
This program open the file and just show on command what is inside,
if buffer is > 0 show all the text contained in file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
char *ft_strncat(char *dst, const char *src, size_t n)
{
if (n != 0) {
char *d = dst;
const char *s = src;
while (*d != 0)
d++;
do {
if ((*d = *s++) == 0)
break;
d++;
} while (--n != 0);
*d = 0;
}
return (dst);
}
char *get_next_line(int fd)
{
char buffer[2] = "";
char **line;
if( !*line )
*line = malloc(100 * sizeof(char));
*line[0] = '\0';
while( read(fd, buffer, 1) > 0 ) {
ft_strncat(*line, buffer, 1);
if( buffer[0] == '\n' )
break;
}
return (0);
}
int main(void)
{
int fd;
int ret;
fd = open("ola.txt", O_RDONLY);
if (fd < 3 && fd != 0)
return (-1);
printf("%d\n", fd);
printf("%s\n", get_next_line(fd));
return (0);
}
im trying to see the error but I cant, im a noob on C yet
thank you for help me.
line should be char *, not char **. That would only be needed if it were a function parameter that should be updated by the function.
You need to return line from the function, not 0.
You should use realloc() to grow line if the input line is longer than the size of line. Use a variable capacity to hold the current size.
There's no good reason to use ft_strncat(). Use another variable to hold the current position in line, and write the character there directly.
char *get_next_line(int fd)
{
char buffer;
size_t capacity = 100;
char *line = malloc(capacity * sizeof(char));
size_t pos = 0;
*line[0] = '\0';
while( read(fd, &buffer, 1) > 0 ) {
if (pos > capacity - 2) {
capacity += 100;
line = realloc(line, capacity);
}
line[pos++] = buffer;
if( buffer == '\n' ) {
line[pos] = '\0';
break;
}
}
return line;
}
In addition, the caller should assign the result to a variable, so it can free the memory. Otherwise you'll create lots of memory leaks when you read all the lines of the file.
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 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.