0

This all could be because I'm just not understanding some basic things about what undefined behaviour is or how it manifests itself. The code in the snippet below is what I've got so far, it creates a linked list. I used the online IDE IDEone run it.

The main thing I'm not understanding is whether printf("\nhead value at the end = %d", (*head).value); just above return in the main function is producing UB or not.

That line prints out a 0, I expect UB since the clean_list function frees memory previously allocated to each list node (unless I'm doing that function wrong).

Shouldn't I get an error or something like that about undefined behaviour during runtime, or at the very least should the program not crash? Or could it be IDEone's safety measures that prevent UB? Or am I just not understanding something with regards to UB?

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

struct node {
    int value;
    struct node* next;
};
typedef struct node node_t;

node_t* create_linked_list(unsigned int number_of_nodes) {
    node_t* head = NULL;
    
    for (unsigned int i = 1; i < number_of_nodes + 1; ++i) {
        node_t* node = malloc(sizeof(node_t));
        node->value = i;
        node->next = head;
        head = node;
    }

    return head;
}

void print_list(node_t* head) {
    node_t* temp = head;
    
    while (temp != NULL) {
        printf("%d - ", temp->value);
        temp = temp->next;
    }

    printf("\n");
}

void clean_list(node_t* head) {
    node_t* current = head;
    
    while (current != NULL) {
        node_t* temp = current;
        current = current->next;
        free(temp);
    }
    
    printf("\nclean_list complete \n");
}

int main(void) {
    node_t* head = create_linked_list(15);
    
    printf("head address at beginning = %p\n", head);
    printf("head value at the beginning = %d\n\n", (*head).value);
    
    // Prints a list from 1 to 15.
    print_list(head);
    
    clean_list(head);
    
    printf("\nhead address at the end = %p", head);
    printf("\nhead value at the end = %d", (*head).value);
    
    return 0;
}
  • 4
    The whole point of undefined behavior is that it is *undefined*: you can't count on it to be any particular behavior. – Scott Hunter Feb 16 '21 at 21:47
  • 4
    Yes, it's undefined behavior. "_Shouldn't I get an error or something_", no, that would be defined behavior. – Brian61354270 Feb 16 '21 at 21:47
  • 2
    Undefined behaviour isn't something specific in that it always crashes or behaves a certain way. It's *undefined*. It could work nine times out of ten and crash on the tenth. It could eat all the food in your fridge. There's also no way of automatically detecting this at compile time short of re-engineering C from the ground up, like basically making it Rust. – tadman Feb 16 '21 at 21:48
  • Ahh right understood, thanks for that. – tomekpryjma Feb 16 '21 at 21:52
  • 1
    There are compiler and runtime tools like valgrind and AddressSanitizer that can make it *more likely* that such instances will fail in a predictable way (with an error message / backtrace). But they come at a substantial perfornance cost, and still can't catch 100% of all errors. – Nate Eldredge Feb 16 '21 at 23:19

0 Answers0