Dynamic buffer fgets in C - c

I've been searching on how to allocate a dynamic buffer using fgets, but I can't seem to get it on this example. The file has two numbers of unknown length separated by a white-space. For every line it reads each character until ' ' and \n and prints it.
char *ptr;
char line[MAX];
while(fgets(line, sizeof line , fp) != NULL){
ptr = line;
for(i=0; i<2; i++){
while(*ptr && (*ptr) != ' '){
if(*ptr == ' ')
break;
k = (*ptr) - '0';
if(k != -38) // wont print '\n'
printf("%d", k);
ptr++;
}
while(*ptr && (*ptr) != '\n') {
if(*ptr == ' '){
ptr++;
continue;
}
k = (*ptr) - '0';
printf("%d", k);
ptr++;
}
}
}
Can someone give me an idea on how to make line dynamic while still using ptr that way?

I think what you want is something like this:
size_t linelen = 80;
char *line = malloc(linelen);
while(magic_reallocating_fgets(&line, &linelen, fp) != NULL) {
/* ... do whatever you want with line ... */
}
But then, of course, the $64,000 question is, what does magic_reallocating_fgets look like? It's something like this:
char *magic_reallocating_fgets(char **bufp, size_t *sizep, FILE *fp) {
size_t len;
if(fgets(*bufp, *sizep, fp) == NULL) return NULL;
len = strlen(*bufp);
while(strchr(*bufp, '\n') == NULL) {
*sizep += 100;
*bufp = realloc(*bufp, *sizep);
if(fgets(*bufp + len, *sizep - len, fp) == NULL) return *bufp;
len += strlen(*bufp + len);
}
return *bufp;
}
That's not really complete code, it's almost pseudocode. I've left two things for you as exercises:
It has no error-checking on the malloc and realloc calls.
It's kinda grossly inefficient, in that it makes not one but two extra passes over each line it reads: to count the characters, and again to look for a '\n'. (It turns out fgets's interface isn't ideal for this kind of work.)

On systems with glibc >= 2.7, or POSIX.1-2008 support, what I think you want can be accomplished using:
char *line;
while (fscanf(f, "%m[^\n]\n", &line) == 1) {
/* do stuff with line */
free(line);
}
This works great on my Linux systems, but over in the Windows universe, Microsoft Visual C++ supports neither %m, nor any equivalent that I can find.

You cannot change the size of an array during run time in C. It is illegal. This is because arrays are allocated from the stack. To have the size be dynamic you would have to declare a pointer, and allocate the memory for it dynamically. This data is allocated from the heap.
You can change the size of allocated memory by using realloc.
int lineLen = 80;
char *line;
line = (char *)malloc(sizeof(char) * 80);
if (line == NULL) {
// Something went horribly wrong
exit(1);
}
while (fgets(line, lineLen, fp)) {
// Do something to find the size
line = (char *)realloc(line, sizeof(char) * newLen);
if (line == NULL) {
// Something went horribly wrong
exit(1);
}
}
However, allocating and reallocating memory is a rather expensive operation. As a result, you may be more effective by just choosing a big buffer size, if you can do that safely. If you have a short loop then it may not be significant enough to worry about, but it is probably not advisable to be constantly changing your buffer's size.

Related

Free() doesn't work as expected after calling malloc()

I wrote the following function in C but have 2 problems:
I am asked to free the dynamically allocated memory to prevent using too much RAM but the line free(word) just causes bugs to my program.
if I delete it everything works fine, why is that happening? I used free after I finished using word as suggested in many different articles.
I am requested to use malloc with the minimum needed space but how could I do that?
currently my code allocates max_str_len blocks of RAM but if the word was much shorter like a letter I don't want to allocate 200 blocks for that.
Any suggestions please?
int read_words(char *words[], int size, int max_str_len) {
char *word;
char ch;
int i;
for (i = 0; i < size; ++i) {
word = (char *)malloc((max_str_len + 1) * sizeof(char));
if (word == NULL)
return -1;
for (int j = 0; j < max_str_len; ++j) {
scanf("%c", &ch);
if (ch == '\n') break;
if (ch == EOF || ch == 'R') return i;
word[j] = ch;
}
words[i] = word;
free(word);
}
return i;
}
You put a malloc'd char* into a caller-provided words[i] and then you free it. That doesn't make sense. If you free it, the caller can't do anything with it.
If you want the malloc'd strings to be minimal you could realloc them or you could read into a large buffer (allocated perhaps at the start of the function) and then copy the result into a malloc'd buffer that's sized just right.
Note that you're also failing to check scanf for errors and that you're leaking memory if you get a memory failure in the middle of the function—by returning -1, you effectively lose info on how many elements of words have been filled with owning pointers. You might want to return that info (return i;) or to free all pointers allocated by the function before the malloc failure.
There are multiple problems in your code:
you free the memory allocated for each word, yet you return pointers to the freed blocks in the array supplied by the caller, causing undefined behavior when the caller will dereference these pointers.
your test for end of file is incorrect: scanf() will return EOF then, bu the character will not be set to EOF, which might not be appropriate for a char anyway. You should use getchar() and make ch an int.
you should set a null terminator at the end of the string read.
you could use realloc to shrink the block of memory once you know the string length.
Here is a modified version:
int read_words(char *words[], int size, int max_str_len) {
char *word, *p;
int i, j, ch;
for (i = 0; i < size;) {
word = (char *)malloc(max_str_len + 1);
if (word == NULL) {
/* free the words allocated so far and return a failure code */
while (i-- > 0)
free(words[i];
return -1;
}
for (j = 0; j < max_str_len; ++j) {
ch = getchar();
if (ch == '\n' || ch == EOF || ch == 'R') break;
word[j] = ch;
}
if (j == 0 && (ch == EOF || ch == 'R'))
break;
word[j] = '\0';
p = (char *)realloc(word, j + 1);
if (p != NULL)
word = p;
words[i++] = word;
if (ch == EOF || ch == 'R')
break;
}
return i;
}

Reading in a line from file or stdin dynamically

I am posed with a situation where my function does exactly what I want except handle higher amounts of input.
I initially thought to process each character one by one but was running into problems doing this. So fscanf not only does what I want it to do but it is essential in reading in only one line. I noticed, I cannot reallocate space for bigger array this way though. I have tried using format specifiers i.e. %*s to include a specific amount of buffer space before hand but this still does not work.
I have noticed also, I would have no way of knowing the size of the string I am reading in.
Here is my attempt and thoughts:
#define LINE_MAX 1000
char* getline(FILE* inputStream)
{
int capacity = LINE_MAX;
char* line = malloc(capacity * sizeof(char));
int ch;
/* if (sizeof(capacity) == sizeof(line)) { // Not a valid comparison? Too late?
capacity *= 2;
line = realloc(line, capacity * sizeof(line));
} */
if (fscanf(stream, "%[^\n]s", line) == 1) {
ch = fgetc(inputStream);
if (ch != '\n' && ch != EOF) {
fscanf(inputStream, "%*[^\n]");
fscanf(inputStream, "%*c");
}
free(line);
return line;
}
free(line);
return NULL;
}
I am new to memory allocation in general but I feel as though I had a good idea of what to do here. Turns out I was wrong.
Here is an example to read a line and store it in a Character array.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
signed char *str;
int c;
int i;
int size = 10;
str = malloc(size*sizeof(char));
for(i=0;(c=getchar()) !='\n' && c != EOF;++i){
if( i == size){
size = 2*size;
str = realloc(str, size*sizeof(char));
if(str == NULL){
printf("Error Unable to Grow String! :(");
exit(-1);
}
}
str[i] = c;
}
if(i == size){
str = realloc(str, (size+1)*sizeof(char));
if(str == NULL){
printf("Error Unable to Grow String! :(");
exit(-1);
}
}
str[i] = '\0';
printf("My String : %s", str);
return 0;
}
The array is resized to twice it's original size if current array can't hold the characters read from input.

Trouble dynamically allocating memory for string array

I am trying to write a function that reads a text file and copies each line of the text file into a line of an array that is passed into the function.
void read_lines(FILE* fp, char*** lines, int* num_lines) {
int i = 0, line_count = 0;
char line[256], c;
fscanf(fp, "%c", &c);
while(!feof(fp)){
if(c == '\n') {
++line_count;
}
printf("%c", c);
fscanf(fp, "%c", &c);
}
rewind(fp);
*num_lines = line_count;
lines = (char***)malloc(line_count * sizeof(char**));
while (fgets(line, sizeof(line), fp) != NULL) {
lines[i] = (char**)malloc(strlen(line) * sizeof(char*));
strcpy(*lines[i], line);
}
++i;
}
}
The initial part scans for newlines so that I know how much to allocate to lines initially. I am not sure where I am going wrong.
Additionally, if anybody has any resources that could help me to better understand how to dynamically allocate space, that would be greatly appreciated.
You should understand how the pointers work. After that, dynamical memory allocation task would be pretty trivial. Right now your code is completely wrong:
//here you assign to the argument. While this is technically allowed
//it is most certainly not what you have intended
lines = (char***)malloc(line_count * sizeof(char**));
while (fgets(line, sizeof(line), fp) != NULL) {
//sizeof(char*) <> sizeof(char). Also you need a space for the trailing \0
lines[i] = (char**)malloc(strlen(line) * sizeof(char*));
//[] precedes * so it is copying the string somewhere you not intend to
strcpy(*lines[i], line);
}
++i;
}
Correct version should be:
*lines = malloc(line_count * sizeof(char*));
while (fgets(line, sizeof(line), fp) != NULL) {
(*lines)[i] = malloc((strlen(line) + 1) * sizeof(char));
strcpy((*lines)[i], line);
}
++i;
}
Note, that you need to use (*lines)[i] construct, because [] operator precedes * (dereference) operator.
Code is making various mistakes including the key ones detailed by #Ari0nhh
The other is the counting the '\n' can fail to get the correct number of lines vs. fgets() in 3 ways:
Line exceeds 256.
More than INT_MAX lines.
Last line does not end with a '\n'.
Suggest instead use the same loop to count "lines"
unsigned long long line_count = 0;
while (fgets(line, sizeof(line), fp) != NULL) {
line_count++;
}
rewind(fp);
....
assert(line_count <= SIZE_MAX/sizeof *(*lines));
*lines = malloc(sizeof *(*lines) * line_count);

How to realloc properly?

I wrote a little function to return a string made from the input given to the program, it worked fine until i traded constant size for dynamic memory allocation. After i tested with a few printf() it looks like the program crashes when realloc() is called. Am i doing something wrong with realloc(), or is it something else?
char* get_line()
{
size_t ptr_pos = 0, size = 50;
int c;
char* line = malloc(size * sizeof *line);
char* temp;
while((c = getchar()) != EOF)
{
if(++ptr_pos >= size)
{
size += 50;
temp = realloc(line, size * sizeof *line); // The program crashes on this intruction.
if(temp != NULL)
{
line = temp;
printf("Reallocation success.\n");
}
else
{
printf("Reallocation error.\n");
free(line);
exit(1);
}
}
*line++ = c;
if(c == '\n')
break;
}
if(ptr_pos == 0)
return NULL;
*line = '\0';
return line - ptr_pos;
}
Thanks for your help.
When you call realloc, you must give it the address of the beginning of the allocated memory, the same address as was originally returned by malloc. The same is true of free.
But you are modifying the value of line, so it is no longer pointing to the beginning of the block when realloc is called.
That is Undefined Behaviour, so a segfault is definitely possible.

C Programming getting input

How do I constantly get user input (strings) until enter is pressed in C just like string class in C++?
I don't know the input size so I can't declare a variable of fixed size or even I can't allocate memory dynamically using malloc() or calloc().
Is there any way to implement this as a separate function?
As H2CO3 said, you should allocate a buffer with malloc(), then resize it with realloc() whenever it fills up. Like this:
size_t bufsize = 256;
size_t buf_used = 0;
int c;
char *buf = malloc(bufsize);
if (buf == NULL) { /* error handling here */ }
while ((c = fgetc(stdin)) != EOF) {
if (c == '\n') break;
if (buf_used == bufsize-1) {
bufsize *= 2;
buf = realloc(buf, bufsize);
if (buf == NULL) { /* error handling here */ }
}
buf[buf_used++] = c;
}
buf[buf_used] = '\0';
Use exponential storage expansion:
char *read_a_line(void)
{
size_t alloc_size = LINE_MAX;
size_t len = 0;
char *buf = malloc(LINE_MAX); // should be good for most, euh, *lines*...
if (!buf)
abort();
int c;
while ((c = fgetc(stdin)) != '\n' && c != EOF) {
if (len >= alloc_size) {
alloc_size <<= 1;
char *tmp = realloc(buf, alloc_size);
if (!tmp)
abort(); // or whatever
buf = tmp;
}
buf[len++] = c;
}
if (len >= alloc_size) {
alloc_size++;
char *tmp = realloc(buf, alloc_size);
if (!tmp)
abort(); // or whatever
buf = tmp;
}
buf[len] = 0;
return buf;
}
In C, you have little choice: If you want to input a string of unbounded length, have to use allocations in a loop. Whether you use realloc() or a linked list of buffers, it comes down to reading (usually through fgets()), reading some more, and so on until the buffer you've just read contains a \n.
Then, depending on the method, you either already have a contiguous buffer (the realloc method) or just need to concatenate them all (the linked list method). Then you can return.
If you're lucky, your platform comes with the extension function getline() that does the realloc method for you. If not, you'll have to write it yourself.

Resources