-1

I was playing with automatic vs dynamic memory allocation for the linked list node. The following code represents the minimal working example.

The following code works and prints the result I expect:

list 1:
1 
list 2:
2 
list 3:
1 2 

Code:

#include <iostream>

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(nullptr) {}
};

ListNode* merge_two_lists_iter(ListNode* l1, ListNode* l2) {

    ListNode* res = nullptr;
    res = new ListNode(l1->val);
    res->next = new ListNode(l2->val);

    return res;
}

void print_list(ListNode* l) {
    while (l) {
        std::cout << l->val << " ";
        l = l->next;
    }
    std::cout << std::endl;
}

int main() {

    // List l1 consists of one node with value 1
    ListNode* l1 = new ListNode(1);

    // List l2 consists of one node with value 2
    ListNode* l2 = new ListNode(2);

    // List l3 is l1 and l2 merged
    ListNode* l3 = merge_two_lists_iter(l1, l2);

    std::cout << "list 1:" << std::endl;
    print_list(l1);
    std::cout << "list 2:" << std::endl;
    print_list(l2);
    std::cout << "list 3:" << std::endl;
    print_list(l3);

    return 0;
}

However, when I replace the line res = new ListNode(l1->val); with the following:

ListNode test (l1->val);
res = &test;

The l3 seems to consist of some random values:

list 1:
1 
list 2:
2 
list 3:
-1845946528 -272632592 -2092384207 (lldb)

I tried to debug the merge_two_lists_iter function and the l3 looks correct. However, the print_list function prints some random values. I would really appreciate if someone could ELI5 what is going on here.

user_185051
  • 426
  • 5
  • 19
  • 2
    Does this answer your question? [Can a local variable's memory be accessed outside its scope?](https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope) – Yunnosch Feb 25 '20 at 21:43
  • 1
    The topic is discussed there in detail. It also answers the question what happens to local variables. Not a direct answer, I admit. I expect others to propose better duplicates. – Yunnosch Feb 25 '20 at 21:44
  • I thought I return the memory of the local variable in both cases though, or am I wrong? Do you mean that when I use ```res = new ListNode(l1->val);```, I just get the correct answer by accident? – user_185051 Feb 25 '20 at 21:57
  • Got it now, ```test``` is a local variable you meant. – user_185051 Feb 25 '20 at 22:04

1 Answers1

2
ListNode* merge_two_lists_iter(ListNode* l1, ListNode* l2) {

    ListNode* res = nullptr;
    ListNode test (l1->val);
    res = &test;
    res->next = new ListNode(l2->val);

    return res;
}

I'm assuming that's what merge_two_lists looked like when you replaced that line. Notice that test is a local variable that is allocated on the stack. When the function returns res, test will go out of scope, so the function is returning an address of freed stack memory. What actually happens to that memory after it's been freed isn't defined in the C-standard, though. So in this case you got random garbage in the address of the merged list.

cschadl
  • 66
  • 1
  • 3
  • Oh, I see now, thank you. So, is this an example of the rule "use heap when you need the data in the variable after the function returns"? – user_185051 Feb 25 '20 at 22:01
  • 1
    Yes, that would be one reason that you would allocate memory on the heap. – cschadl Feb 26 '20 at 03:17