-1

I have the following code to reverse a linked list. Why do I get a runtime error:

AddressSanitizer:DEADLYSIGNAL
=================================================================
==31==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000010 (pc 0x000000370a97 bp 0x7fffed631a10 sp 0x7fffed6318c0 T0)
==31==The signal is caused by a READ memory access.
==31==Hint: address points to the zero page.
    #4 0x7f165e0300b2  (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
AddressSanitizer can not provide additional info.
==31==ABORTING

IF I comment out the line "cout<<pre<<endl;"?

/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head) return head;
        ListNode* temp = head;
        ListNode* pre;
        cout<<pre<<endl;
        ListNode* cur = head;
        while(cur->next){
            temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
        cur->next = pre;
        return cur;
    }
};

if I don't comment out the line, cout would print a random address each time, and the program would successfully reverse the linked list without any errors. Link to the problem: https://leetcode.com/problems/reverse-linked-list/

taswell
  • 21
  • 2

2 Answers2

4

An uninitialized variable has an indeterminate value (except when the variable is declared in static memory, then it has an initial value of 0 if you don't set a value explicitly). See Default variable value.

In the case of pointers, trying to dereference a pointer that is not pointing at valid memory is undefined behavior.

In your example, you need to initialize pre to NULL for your code to work correctly, otherwise your list will end up corrupted, containing invalid pointers.

For example, say you start with this list:

----------       ----------       ----------
| val=1  |   /-> | val=2  |   /-> | val=3  |
| next=2 | -/    | next=3 | -/    | next=0 |
----------       ----------       ----------
    ^
    |
head, cur

After the 1st loop iteration, the list will now look like this:

----------       ----------       ----------
| val=1  |       | val=2  |   /-> | val=3  |
| next=P |       | next=3 | -/    | next=0 |
----------       ----------       ----------
                     ^
                     |
                    cur

After the next iteration, the list will now look like this:

----------       ----------       ----------
| val=1  | <-\   | val=2  |       | val=3  |
| next=P |    \- | next=1 |       | next=0 |
----------       ----------       ----------
                                      ^
                                      |
                                     cur

And then the loop ends, cur->next is updated one last time, so the list will finally look like this:

----------       ----------       ----------
| val=1  | <-\   | val=2  | <-\   | val=3  |
| next=P |    \- | next=1 |    \- | next=2 |
----------       ----------       ----------
                                      ^
                                      |
                                    return

Notice the issue?

You have effectively reversed the list, but where is next=P on the "last" node pointing?

  • If pre were not initialized, then next=P would be indeterminate (let's assume pre didn't randomly initialize to NULL, which would be extremely rare). Thus, any code that tries to traverse the list afterwards will not be able to determine where the list ends, and will try to access a node that doesn't exist!

  • If pre had been properly initialized to NULL instead, then next=P would be NULL as expected, terminating the list properly.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • but I'm not dereferencing anything am I? – taswell Sep 08 '21 at 00:42
  • @taswell You may not be, but LeetCode might. As mentioned set it to `NULL` or `nullptr`, as that is what the end of the linked list should/is expected to point to – Lala5th Sep 08 '21 at 00:48
  • 1
    @taswell Every use of `->` in your example is a dereference. Now, you may not be dereferencing bad `pre` pointers inside of `reverseList()` itself, but code that tries to use the list *AFTER* `reverseList()` exits will be accessing bad pointers (including `reverseList()` if you call it a second time on the same list). By NOT initializing `pre`, `reverseList()` is *corrupting* the list. – Remy Lebeau Sep 08 '21 at 00:49
  • I really appreciate your answer. I think I gave a bad title, which is kind of misleading. I actually know I should initialize it. But I'm confused why the program will successfully give me the reversed linked list IF I cout<
    2->1, and it stops right there.
    – taswell Sep 08 '21 at 02:07
  • @taswell Undefined behavior is unpredictable. And as my diagrams show, the *loop* stops after setting 1 as the last node, but the *list* DOES NOT end at 1 when `pre` is not `NULL` – Remy Lebeau Sep 08 '21 at 02:43
  • can you do me a favor and try my code in the leetcode problem? I'm still feeling you're not getting my point because you keep saying things I already know. I appreciate your effort to make me understand your point, though. Link: https://leetcode.com/problems/reverse-linked-list/ – taswell Sep 08 '21 at 02:55
  • @taswell "*can you do me a favor and try my code in the leetcode problem?*" - no. This is a Q&A site. My job is to answer questions. Your job is to debug your own code. "*I'm still feeling you're not getting my point*" - I understand your point just fine. What you don't seem to understand is that your code has UNDEFINED BEHAVIOR and can LITERALLY BEHAVE ANY WAY IT WANTS. Don't try to make sense of it. Just fix it. – Remy Lebeau Sep 08 '21 at 04:18
  • You're right. I put it on my local machine and got different result. Sometimes it's really hard to tell if it's not worth my time or if it's something I should dig a little deeper, possibly learning something fundamental during the process. – taswell Sep 09 '21 at 01:49
0

Consider your code:

        ListNode* pre;
        cout<<pre<<endl;
        ListNode* cur = head;
        while(cur->next){
            temp = cur->next;
            cur->next = pre;

pre is clearly uninitialized, and is used to initialize cur->next, which would be head->next. So, head->next would hold an uninitialized pointer value when the routine returns.

If there is code checking your Solution after calling it, that code would need to validate the an invalid pointer value, which would lead to undefined behavior.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • Thank you for your answer but I'm actually asking why printing out the pointer at the first will make the code work as expected. 1-2-3 will be reversed as 3-2-1 and no errors. – taswell Sep 08 '21 at 02:09
  • 1
    Undefined behavior means sometimes the code will work. The printing code may for some reason make the uninitialized variable behave like it is zero initialized. – jxh Sep 08 '21 at 04:05