views:

146

answers:

3
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *readLine(FILE *inFile)  //Simply reads line in a text file till "\n"
{
    char *line = realloc(NULL, 1);
    char c;
    int i=0;
    while (!feof(inFile))
    {
        c = fgetc(inFile);
        if (ferror(inFile)) printf("Error reading");
        if (c == 10)
            {
                realloc(line,i+1);
                line[i]= 10;
                break;
            }
        realloc(line, i+1);
        line[i++] = c;
    }
    return line;
}

int main(int argc,char **argv)
{
    FILE *inFile;
    inFile = fopen("testFile","r");
    printf("%s",readLine(inFile));
    printf("%s",readLine(inFile));
    printf("%s",readLine(inFile));
    return 0;
}

If the contents of testFile is:-

abc
def
ghi

The three printf statements should show "abc" three times.. But the output is:-

abc
def
ghi

I know I am wrong in the concept somewhere. Pls help.

+6  A: 

Usage of realloc() is incorrect.

realloc(line,i+1); // wrong

// OK
void *new_line = realloc(line,i+1);
if (!new_line)
{
    free(line);
    return NULL;
}
line = new_line;

Because line is passed by value, it's not changed. The actual re-allocated memory is in the return value. Therefore line remains the same over and over again, and you are seeing the same line over and over again. Edit: just realized that's even though it's a bug, it's not what would cause repeating lines. Other points are still valid.

What's worse:

  1. You have a memory leak by losing the newly re-allocated pointer every time.
  2. You are potentially accessing freed memory, because old line value may become invalid after reallocation, if it was reallocated in a different part of the heap.
  3. You are re-allocating memory every character, which is potentially an expensive operation.
Alex B
bad bad bad|! it should be void * tmp = realloc(line, i+1); if (tmp) { line = tmp; } else { /* handle error */ }
asveikau
I would add to this +1 answer that you shouldn't be `realloc`ing for every character. It is a relatively expensive process. There are two common approaches to resizing buffers. 1) have an initial allocation (eg. 1024 bytes), and a delta size (512 bytes) which you add each time you need to extend the buffer. And 2) have a default size (eg. 64 bytes) and double the size each time you realloc. Oh and also you should put '\n' at the end of your strings if you want to see them; printf normally uses buffered output.
gavinb
You are both right, though I'd prefer to focus on question on hand and not overwhelm the asker with too much info. :)
Alex B
@asveikau error handling is missing in the code provided, but in the context of this function it's not really necessary to use a temporary: you can safely assign to `line` and return NULL anyway if it fails.
Alex B
@Checkers that's fine if you don't mind leaking memory on failure...
asveikau
On the second thought, I included one of the points.
Alex B
Actually yes, you are right, realloc won't touch the original pointer if it fails to allocate the new one.
Alex B
Ok.. that was being very bad program from my side.Ok now i know how to properly use realloc().But, why am i not getting "abc" again and again, even though I am passing "inFile" by value.
shadyabhi
+4  A: 

fgetc() advances the file pointer (which is "where the next character to be read is located"). That's how you're able to call it in a loop and read a whole line of characters.

After it advances past the newline character, it naturally moves on to the next character, which is the beginning of the next line.

You could modify the file pointer with the fseek() function. For example, calling fseek(inFile, 0, SEEK_SET) would reset it to the beginning of the file, causing the next fgetc() call to start over from the first character of the file.

aib
But I am passing file pointer by value. So i should get output "abc" again and again..
shadyabhi
You're mixing up terms. A pointer to a file is not the same thing as the "file pointer" also known as the "file position indicator."You are passing a pointer to the same file again and again, but the file position indicator is advancing with every call to fgetc(). As aib says, you'd have to reset it with fseek() if you want to start over.
Wade Williams
+4  A: 

But I am passing file pointer by value. So i should get output "abc" again and again

Ah, I understand your confusion.

A file pointer only points to the actual file structure. State such as the current offset are not part of the pointer but are part of the internal structure.

Another way to think about this is that the actual object representing the file is FILE. To get pass-by-reference semantics, you pass a pointer to the object. Since you are passing by reference, each line picks up where the last one left off.

R Samuel Klatchko
Yeah.. I got what I actually wanted.. Thank you very much..Thanx to others too for telling few more important points...
shadyabhi