Allocating and initializing a buffer with file data using a single loop - c

I try to read a file to a buffer using a low level file descriptor. The method suppose to store the file data byte by byte to a char * buffer, parse that data, and then free the allocated buffer.
static void
parse_file(char path[11]) {
int fd = open(path, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Failed to open a file '%s'", path);
exit(errno);
}
char c;
char *buffer = {0};
unsigned int i = 0;
while (read(fd, &c, 1)) {
buffer = malloc(sizeof(char)); // Why *buffer want work here?
*buffer = c;
++buffer;
++i;
}
buffer = malloc(sizeof(char));
*buffer = '\0';
buffer = &buffer[0];
printf("Buffer data:\n%s\n", buffer);
// Parse buffer data
// ...
buffer = &buffer[0];
for (unsigned int j = 0; j <= i; ++j) {
free(buffer);
++buffer;
}
}
I come up with the above solution, but flycheck gives me a warning of unix.Malloc type:
Attempt to free released memory
How can I allocate the buffer char by char in a single loop?

Construction like buffer = &buffer[0]; won't work. After the loop (and setting \0) buffer points to the last character (so to \0). Taking address of the 0th element will just give you address of the last element (as the buffer points). You cannot 'rewind' to the first character that way.
When you call then free() you start freeing from your last element and then iterate over some memory region that was not allocated before.

There are a three problems worth to mention with the above code.
*buffer = malloc(sizeof(char)) will not work as *buffer refers to a char type, not a char * (pointer).
buffer = &buffer[0] will not reset the buffer to it's first element. This can by done either by storing the address to the first element for later assignment, or rewinding backwards in a loop using pointer arithmetic.
Allocating memory byte by byte would be very inefficient. It is better to allocate the buffer after it's size is known.
I end up using a temporary automatic storage variable with a fixed size, and allocating the buffer's memory after the read loop:
static void
parse_file(char path[11]) {
int fd = open(path, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Failed to open a file '%s'\n", path);
exit(errno);
}
const int MAX = 50;
char c;
char *buffer = {0};
char tmp[MAX];
unsigned int i = 0;
while (read(fd, &c, 1)) {
tmp[i] = c;
++i;
}
buffer = malloc(sizeof(char) * i + 1);
tmp[i] = '\0';
strcpy(buffer, tmp);
printf("Buffer data:\n%s\n", buffer);
// Parse buffer data
// ...
free(buffer);
}

Related

How to read and store in memory each line of file using syscalls in C

Im trying to read each line from a file, and store those values in a pointer array. Im new to C so sorry for my scrappy code.
I tried reading char by char, put them in a string, and then "store" that string in the pointer array, since it looked like a simple solution, but im having trouble with memory allocations.
Here is my code:
char *files[100];
char buffer;
int r, i, j;
char temp[100];
int fd=open(argv[1], O_RDONLY);
i=j=0;
while(read (fd, &buffer, 1) > 0) {
if(buffer != '\n') {
temp[i] = buffer;
}
else{
files[j]=temp;
j++;
i=0;
continue;
}
i++;
}
I had to allocate memory for each line, otherwise, my files vector always pointed to the same buffer. I just added the following line before attributing the value of temp to files:
files[j] = (char*) malloc (size of (char) * strlen(temp);

How to append a char to a String in C using a function

I was writing a lexical analyzer in which I need to append a char to a string (a char *). For some reason, the code below is resulting in string having a value of "(null)" when I print it to stdout. The function is given below.
void append_char(char *buffer, char c) {
if(buffer == NULL) {
buffer = malloc(sizeof(char));
if(buffer == NULL) {
fprintf(stderr, "COuld not allcocate memory to buffer\n");
}
} else {
buffer = realloc(buffer, sizeof(buffer) + sizeof(char));
}
buffer[sizeof(buffer) - 1] = c;
}
When I run the lines
char *buf = NULL;
append_char(buf, 'a');
append_char(buf, '\0');
printf("buffer: %s\n", buf);
it prints (null) to stdout. How can I fix this?
Pass by value
append_char(char *buffer, char c) does not affect the caller's buf in main(): append_char(buf, 'a');. buf remains NULL. This leads to OP's output.
Insufficient size
Insufficient size for the newly allocated string. No room for the null character.
Wrong size
With char *buffer, sizeof(buffer) is the size of a pointer, not the amount allocated beforehand.
Lost memoery
When buffer = realloc(buffer, sizeof(buffer) + sizeof(char)); fails (realloc() returns NULL) , the original value of buffer is lost. Save the result and test.
Note: OK to call realloc(NULL, ...).
char *append_char(char *buffer, char c) {
size_t old_length = buffer ? strlen(buffer) : 0;
size_t new_length = old_length + 1; // +1 for c
// Size needed for a string is its length + 1
char *new_buffer = realloc(buffer, new_length + 1); // +1 for \0
if (new_buffer == NULL) {
fprintf(stderr, "Could not allocate memory to buffer\n");
free(buffer);
return NULL;
}
new_buffer[old_length] = c;
new_buffer[old_length + 1] = '\0';
return new_buffer;
}
// Usage
buf = append_char(buf, 'a');
There are a number of problems with your program:
buffer is a local variable of append_char(). As soon as this function returns, the buffer variable becomes unusable. You need to pass in the address of buffer to write the address returned by malloc() and realloc() to buffer.
buf is a pointer to a char. sizeof (buf) does not depend on the number of characters in buf.
You do not check the return value from realloc().
You are not allocating space for the NUL terminator.
You do not call free() after you are done.
Here is one way to achieve what you are trying to do (although I am not trying to fix all the problems I mentioned above):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void append_char(char **buffer, char c) {
if(*buffer == NULL) {
if((*buffer = malloc(sizeof(char) + 1)) == NULL) { /* + 1 for the NUL terminator */
fprintf(stderr, "COuld not allcocate memory to buffer\n");
return;
}
(*buffer)[0] = (*buffer)[1] = '\0';
} else {
*buffer = realloc(*buffer, strlen(*buffer) + sizeof(char) + 1 /* for the NUL terminator */);
}
(*buffer)[strlen(*buffer) + 1] = '\0';
(*buffer)[strlen(*buffer)] = c;
}
int main(void)
{
char *buf = NULL;
append_char(&buf, 'a');
append_char(&buf, '\0');
printf("buffer: %s\n", buf);
}

pointer of pointer of char in c, assignment crashes

I have a pointer of pointer to store lines I read from a file;
char **lines;
And I'm assigning them like this :
line_no=0;
*(&lines[line_no++])=buffer;
But it crashes why ?
According to my logic the & should give the pointer of zeroth index, then *var=value, that's how to store value in pointer. Isn't it ?
Here is my current complete code :
void read_file(char const *name,int len)
{
int line_no=0;
FILE* file;
int buffer_length = 1024;
char buffer[buffer_length];
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
printf("---%s", buffer);
++line_no;
if(line_no==0)
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
else
{
lines = (char**)realloc(lines,sizeof(*lines) * line_no);
}
lines[line_no-1] = (char*)malloc(sizeof(buffer));
lines[line_no-1]=buffer;
printf("-------%s--------\n", *lines[line_no-1]);
}
fclose(file);
}
You have just a pointer, nothing more. You need to allocate memory using malloc().
Actually, you need first to allocate memory for pointers, then allocate memory for strings.
N lines, each M characters long:
char** lines = malloc(sizeof(*lines) * N);
for (int i = 0; i < N; ++i) {
lines[i] = malloc(sizeof(*(lines[i])) * M);
}
You are also taking an address and then immediately dereference it - something like*(&foo) makes little to no sense.
For updated code
Oh, there is so much wrong with that code...
You need to include stdlib.h to use malloc()
lines is undeclared. The char** lines is missing before loop
if in loop checks whether line_no is 0. If it is, then it allocates lines. The problem is, variable line_no is 0 - sizeof(*lines) times 0 is still zero. It allocates no memory.
But! There is ++line_no at the beginning of the loop, therefore line_no is never 0, so malloc() isn't called at all.
lines[line_no-1] = buffer; - it doesn't copy from buffer to lines[line_no-1], it just assigns pointers. To copy strings in C you need to use strcpy()
fgets() adds new line character at the end of buffer - you probably want to remove it: buffer[strcspn(buffer, "\n")] = '\0';
Argument len is never used.
char buffer[buffer_length]; - don't use VLA
It would be better to increment line_no at the end of the loop instead of constantly calculating line_no-1
In C, casting result of malloc() isn't mandatory
There is no check, if opening file failed
You aren't freeing the memory
Considering all of this, I quickly "corrected" it to such state:
void read_file(char const* name)
{
FILE* file = fopen(name, "r");
if (file == NULL) {
return;
}
int buffer_length = 1024;
char buffer[1024];
char** lines = malloc(0);
int line_no = 0;
while (fgets(buffer, buffer_length, file)) {
buffer[strcspn(buffer, "\n")] = '\0';
printf("---%s\n", buffer);
lines = realloc(lines, sizeof (*lines) * (line_no+1));
lines[line_no] = malloc(sizeof (*lines[line_no]) * buffer_length);
strcpy(lines[line_no], buffer);
printf("-------%s--------\n", lines[line_no]);
++line_no;
}
fclose(file);
for (int i = 0; i < line_no; ++i) {
free(lines[i]);
}
free(lines);
}
Ok, you have a couple of errors here:
lines array is not declared
Your allocation is wrong
I don't understand this line, it is pointless to allocate something multiplying it by zero
if( line_no == 0 )
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
You shouldn't allocate array with just one element and constantly reallocate it. It is a bad practice, time-consuming, and can lead to some bigger problems later.
I recommend you to check this Do I cast the result of malloc? for malloc casting.
You could write something like this:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void read_file(char const *name)
{
int line_no = 0, arr_size = 10;
int buffer_length = 1024;
char buffer[buffer_length];
char **lines;
FILE* file;
lines = malloc(sizeof(char*) * 10);
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
buffer[strlen(buffer)-1] = '\0';
printf("---%s", buffer);
++line_no;
if(line_no == arr_size)
{
arr_size += 10;
lines = realloc(lines, sizeof(char*) * arr_size);
}
lines[line_no-1] = malloc(sizeof(buffer));
lines[line_no-1] = buffer;
printf("-------%s--------\n", lines[line_no-1]);
}
fclose(file);
}
PS, fgets() also takes the '\n' char at the end, in order to prevent this you can write the following line: buffer[strlen(buffer)-1] = '\0';

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));
}
}

dynamically allocating my 2d array in c

Any hints on how I would dynamically allocate myArray so I can enter any amount of strings and it would store correctly.
int main()
{
char myArray[1][1]; //how to dynamically allocate the memory?
counter = 0;
char *readLine;
char *word;
char *rest;
printf("\n enter: ");
ssize_t buffSize = 0;
getline(&readLine, &buffSize, stdin);//get user input
//tokenize the strings
while(word = strtok_r(readLine, " \n", &rest )) {
strcpy(myArray[counter], word);
counter++;
readLine= rest;
}
//print the elements user has entered
int i =0;
for(i = 0;i<counter;i++){
printf("%s ",myArray[i]);
}
printf("\n");
}
Use realloc like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
char **myArray = NULL;
char *readLine = NULL;
size_t buffSize = 0;
size_t counter = 0;
char *word, *rest, *p;
printf("\n enter: ");
getline(&readLine, &buffSize, stdin);
p = readLine;
while(word = strtok_r(p, " \n", &rest )) {
myArray = realloc(myArray, (counter + 1) * sizeof(*myArray));//check omitted
myArray[counter++] = strdup(word);
p = NULL;
}
free(readLine);
for(int i = 0; i < counter; i++){
printf("<%s> ", myArray[i]);
free(myArray[i]);
}
printf("\n");
free(myArray);
}
Here is one way you might approach this problem. If you are going to dynamically allocate storage for an unknown number of words of unknown length, you can start with a buffSize that seems reasonable, allocate that much space for the readLine buffer, and grow this memory as needed. Similarly, you can choose a reasonable size for the number of words expected, and grow word storage as needed.
In the program below, myArray is a pointer to pointer to char. arrSize is initialized so that pointers to 100 words may be stored in myArray. First, readLine is filled with an input line. If more space than provided by the initial allocation is required, the memory is realloced to be twice as large. After reading in the line, the memory is again realloced to trim it to the size of the line (including space for the '\0').
strtok_r() breaks the line into tokens. The pointer store is used to hold the address of the memory allocated to hold the word, and then word is copied into this memory using strcpy(). If more space is needed to store words, the memory pointed to by myArray is realloced and doubled in size. After all words have been stored, myArray is realloced a final time to trim it to its minimum size.
When doing this much allocation, it is nice to write functions which allocate memory and check for errors, so that you don't have to do this manually every allocation. xmalloc() takes a size_t argument and an error message string. If an allocation error occurs, the message is printed to stderr and the program exits. Otherwise, a pointer to the allocated memory is returned. Similarly, xrealloc() takes a pointer to the memory to be reallocated, a size_t argument, and an error message string. Note here that realloc() can return a NULL pointer if there is an allocation error, so you need to assign the return value to a temporary pointer to avoid a memory leak. Moving realloc() into a separate function helps protect you from this issue. If you assigned the return value of realloc() directly to readLine, for example, and if there were an allocation error, readLine would no longer point to the previously allocated memory, which would be lost. This function prints the error message and exits if there is an error.
Also, you need to free all of these memory allocations, so this is done before the program exits.
This method is more efficient than reallocing memory for every added character in the line, and for every added pointer to a word in myArray. With generous starting values for buffSize and arrSize, you may only need the initial allocations, which are then trimmed to final size. Of course, there are still the individual allocations for each of the individual words. You could also use strdup() for this part, but you would still need to remember to free those allocations as well.Still, not nearly as many allocations will be needed as when readLine and myArray are grown one char or one pointer at a time.
#define _POSIX_C_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void * xmalloc(size_t size, char *msg);
void * xrealloc(void *ptr, size_t size, char *msg);
int main(void)
{
char **myArray;
size_t buffSize = 1000;
size_t arrSize = 100;
size_t charIndex = 0;
size_t wordIndex = 0;
char *readLine;
char *inLine;
char *word;
char *rest;
char *store;
/* Initial allocations */
readLine = xmalloc(buffSize, "Allocation error: readLine");
myArray = xmalloc(sizeof(*myArray) * arrSize,
"Allocation error: myArray\n");
/* Get user input */
printf("\n enter a line of input:\n");
int c;
while ((c = getchar()) != '\n' && c != EOF) {
if (charIndex + 1 >= buffSize) { // keep room for '\0'
buffSize *= 2;
readLine = xrealloc(readLine, buffSize,
"Error in readLine realloc()\n");
}
readLine[charIndex++] = c;
}
readLine[charIndex] = '\0'; // add '\0' terminator
/* If you must, trim the allocation now */
readLine = xrealloc(readLine, strlen(readLine) + 1,
"Error in readLine trim\n");
/* Tokenize readLine */
inLine = readLine;
while((word = strtok_r(inLine, " \n", &rest)) != NULL) {
store = xmalloc(strlen(word) + 1, "Error in word allocation\n");
strcpy(store, word);
if (wordIndex >= arrSize) {
arrSize *= 2;
myArray = xrealloc(myArray, sizeof(*myArray) * arrSize,
"Error in myArray realloc()\n");
}
myArray[wordIndex] = store;
wordIndex++;
inLine = NULL;
}
/* You can trim this allocation, too */
myArray = xrealloc(myArray, sizeof(*myArray) * wordIndex,
"Error in myArray trim\n");
/* Print words */
for(size_t i = 0; i < wordIndex; i++){
printf("%s ",myArray[i]);
}
printf("\n");
/* Free allocated memory */
for (size_t i = 0; i < wordIndex; i++) {
free(myArray[i]);
}
free(myArray);
free(readLine);
return 0;
}
void * xmalloc(size_t size, char *msg)
{
void *temp = malloc(size);
if (temp == NULL) {
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
return temp;
}
void * xrealloc(void *ptr, size_t size, char *msg)
{
void *temp = realloc(ptr, size);
if (temp == NULL) {
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
return temp;
}
I suggest you first scan the data and then call malloc() with the appropriate size.
Otherwise, you can use realloc() to reallocate memory as you go through the data.

Resources