0

I come from the Java world and have developed a penchant towards C lately. I was going through the chapter on pointers in K&R and there's an example wherein the authors input N lines in an array of pointers and then sorts it. Here's my readLines(..) method:

void readlines(char *lines[]) {

    /*read 5 lines*/
    for(int i = 0; i < 5; i++) {

        char line[MAXLENGTH];

        printf("%p\n",line);

        readNewLine(line,MAXLENGTH);

        /*make the ith pointer in the array point to this line*/
        *(lines + i) = line;
    }
}

What I have tried to do here is input 5 lines into lines. For each line (i), I get some space, print its address (for debugging) and then read a line into that space. Then I make the ith pointer point to the start of that memory location. When I run the program, I see the same virtual memory address. Shouldn't a new memory space be allocated with each loop on the heap with line pointing to it? Why do I see the same address then?

Also, K&R's solution is much more complicated and elegant. My version seems just too simple. What is it that I am missing here?

Edit:

Here's the caller:

int main(int argc, char *argv[]) {
    char *lines[5];
    readlines(lines);
    //print all the input lines
    for(int i = 0; i < 5; i++) {
        char *line = lines[i];
        while(*line) {
            printf("%c",*line);
            line++;
        }
    }
    return 0;
}

This prints the last line that I entered. Now as mentioned in the comments, the buffer in the callee is allocated on the stack, which should be destroyed once the function returns. Where is the last line getting stored then?

Prashant Pandey
  • 4,332
  • 3
  • 26
  • 44
  • You didn't show us the code that's doing the allocation. – Robert Harvey Mar 03 '20 at 17:51
  • You mean readNewLine(..)? @RobertHarvey – Prashant Pandey Mar 03 '20 at 17:51
  • 2
    `char line[MAXLENGTH];` is the same instance of the variable in each loop. It stays in scope until the loop ends. – Weather Vane Mar 03 '20 at 17:54
  • @WeatherVane that variable is local to the loop? So with each loop, a new one should be created? – Prashant Pandey Mar 03 '20 at 17:55
  • It's the same one AFAIK and the compiler is free to create it when the function begins, if it chooses, although legal access to it is only within the loop. – Weather Vane Mar 03 '20 at 17:56
  • 1
    Nope. Your array of pointers will end up being just five copies of one address, which will contain the last line read, and whose lifetime will end after the loop, so you haven't actually saved any text anywhere. – Lee Daniel Crocker Mar 03 '20 at 17:57
  • @PrashantPandey `int i` is local to the loop too, but you don't suppose a fresh one is created for each iteration... I don't think you even need that variable anyway, just `readNewLine(lines[i], MAXLENGTH);` assuming you allocated memory to each element. – Weather Vane Mar 03 '20 at 17:58
  • @LeeDanielCrocker that's exactly what's happening. The array contains just one address. Why doesn't it allocate new memory for each loop? – Prashant Pandey Mar 03 '20 at 18:01
  • You would need `malloc` for that. Remember that `line[]` goes out of life, so a pointer to it will be useless. – Weather Vane Mar 03 '20 at 18:02
  • @WeatherVane is this an optimisation that the GCC does? Is there some text on this I can refer? – Prashant Pandey Mar 03 '20 at 18:03
  • No I don't think it's an optimisation, just C. – Weather Vane Mar 03 '20 at 18:03
  • @WeatherVane even when it goes out-of-scope, *(lines + i) will be pointing to the space I allocated using line[MAXLENGTH]? – Prashant Pandey Mar 03 '20 at 18:05
  • Yes, but only until the end of the loop. Please see [Scope and lifetime of local variables in C](https://stackoverflow.com/questions/22527846/scope-and-lifetime-of-local-variables-in-c/). – Weather Vane Mar 03 '20 at 18:07
  • @WeatherVane I am looking at it from a Java perspective. Since *(lines + i) is still pointing to line, it won't be garbage collected. – Prashant Pandey Mar 03 '20 at 18:07
  • The variable `line[MAXLENGTH]` *ceases to exist* at the end of the loop, therefore using a pointer to it later, will be *undefined behaviour*. – Weather Vane Mar 03 '20 at 18:08
  • @WeatherVane I can print the last line I entered using lines[4] outside the loop. So it does save text at 0x7ffee2cdf8e0 (in my case)? – Prashant Pandey Mar 03 '20 at 18:27
  • There is no garbage collection in C. – the busybee Mar 03 '20 at 18:29
  • So it might be but it isn't legal, and can't be guaranteed. It just happens to still be there because the memory has not been overwritten. Same as if you leave your wallet somewhere, it might be still there when you go back for it. – Weather Vane Mar 03 '20 at 18:39
  • @WeatherVane thanks for all the help, really appreciate it. – Prashant Pandey Mar 03 '20 at 18:39
  • What's "garbage collection"? :-) – Lee Daniel Crocker Mar 03 '20 at 18:50
  • @Prashant Pandey How is the argument (the array of pointers) defined in the caller? – Vlad from Moscow Mar 03 '20 at 19:12
  • @VladfromMoscow I have added the caller in my question. You said that the lifetime of the buffer is till the function returns, but I can print the last entered line from the caller as well. Where it getting stored then? – Prashant Pandey Mar 04 '20 at 05:54
  • @VladfromMoscow one more question. Why does the buffer have the same address in every loop? Allocating a new buffer at a different address for the next loop, is there some overhead attached to it? – Prashant Pandey Mar 04 '20 at 08:10

1 Answers1

2

in this code you have a lots of issues. You use the same buffer, and this buffer is local so it will not exist if you leave the scope.

You need to allocate a line buffer for every single line you're going to read.

void readlines(char *lines[]) {

    /*read 5 lines*/
    for(int i = 0; i < 5; i++) {

        char *line = malloc(MAXLENGTH);

        printf("%p\n",(void *)line);

        readNewLine(line,MAXLENGTH);

        /*make the ith pointer in the array point to this line*/
        *(lines + i) = line;
    }
}
0___________
  • 60,014
  • 4
  • 34
  • 74
  • I can print the last line I entered using *lines[4] and iterating until it encounters '\0'. This is outside the loop. So if the buffer does not exist outside the loop, how come I can print the last line I entered? – Prashant Pandey Mar 03 '20 at 18:29
  • @PrashantPandey the array line, you are defining exists on the stack, so will be valid for the lifetime of your function. The problem is that for every iteration it always is the same memory location – bobby Mar 03 '20 at 18:43
  • @P__J__ I can print the last input line from the caller. The buffer is allocated on the stack, so when the callee returns, the frame should be destroyed. How can the caller still access the last line? I have edited my question to include the caller as well. – Prashant Pandey Mar 04 '20 at 05:51
  • Undefined Beahavior. search SO. thousands of identical questiond – 0___________ Mar 04 '20 at 10:15