-3

I saw this code in C++ - it is used to add two linked lists.

The addTwoNumbers function defines a head which is a struct pointer and its next member is a struct. Eventually the function returns the next member of head. Both were initialized on the stack (without using malloc) so how is it possible that the returned value points to a valid address after function execution frame was removed from the stack? Thanks ahead for explanation :)

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

ListNode *somFunc()
{
  ListNode *tail = new ListNode(2);
  ListNode *head = new ListNode(12, tail);
  return head->next;
}

int main(int argc, char **argv)
{
  ListNode *res = somFunc();
  return 0;
};
Stas Ezersky
  • 343
  • 2
  • 10
  • The logic in the shown code is quite ...hard to decipher. But in general case, [undefined behavior means anything can happen](https://stackoverflow.com/questions/32132574/does-undefined-behavior-really-permit-anything-to-happen), so just because the shown code doesn't crash or give the wrong results does not mean that it's working correctly. – Sam Varshavchik Jun 27 '21 at 17:26
  • `ListNode` has no destructor so (1) it's going to leak and (2) this lets all the saved stack pointers like `ListNode two(4, &three);` work, as `next` is never deleted. – Richard Critten Jun 27 '21 at 17:28
  • @OP *I saw this code in C++ - it is used to add two linked lists.* -- FYI -- You could have eliminated all of the code in that function and replaced it with: `ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {ListNode *head = new ListNode(0); return head->next;}` -- There would have been no difference in the reasoning why `next` is valid. – PaulMcKenzie Jun 27 '21 at 17:32

2 Answers2

2
int main(int argc, char **argv)
{
  ListNode three(3);
  ListNode two(4, &three);
  ListNode one(2, &two);

  ListNode six(3);
  ListNode five(4, &six);
  ListNode four(2, &five);

All of these nodes have automatic storage indeed. And when the function main returns, those nodes are destroyed. However, you aren't returning any pointers from main.


The nodes created in addTwoNumbers have dynamic storage:

ListNode *head = new ListNode(0);
...
curr->next = new ListNode(sum);
...
curr->next = new ListNode(1);

These objects exist until the pointers are explicitly freed using delete. Thus, the pointers remain valid outside of that function. Those pointers are never deleted in the program though, so it leaks memory.

Now, the answer to your question:

Why returning a pointer to a struct initialized without malloc doesn't fail in C++

  1. You're returning a pointer that remains valid even after the function returns.
  2. Returning a pointer that will become invalid won't "fail". The returned pointer simply becomes invalid.
  3. Returning a pointer may only "fail" if it has an indeterminate value and even then only because behaviour of the program is undefined, and thus "failure" isn't guaranteed.
eerorika
  • 232,697
  • 12
  • 197
  • 326
0

Neither head nor next is initialized on the stack. They both use dynamic memory using new. In C++, malloc is generally avoided.

That being said, there's quite a few memory leaks in the code (as RichardCritten pointed out in the comments). Not sure where you received this code, but it is quite bad.

ChrisMM
  • 8,448
  • 13
  • 29
  • 48