The missing asterisk

Welcome to Part 1 of the C Programming Pitfalls series!

Recently I stumbled over a refactoring error: I forgot adding an asterisk to change the pointed-to value. It took me about half an hour of debugging to find it, and when I found it I just thought "Well. This was just unnecessary". So I decided to mention this 'pitfall' in a small blog post. The code referenced in this post can be found in this repository.

A minimal C example

As an example, I want to consider a function, which tries to retrieve the next message from a fictional message storage. Inside the function, some logic determines if there is a message available or not. The message retrieval can fail, so the return value should signal the overall success. If there is a message available, the function sets the value of message accordingly. Otherwise, it sets the value of message to NULL.

#include <stdio.h>
#include <stdbool.h>

// Get the next message from e.g. a file.
// Getting a message might fail for some reason and there might be no message available.
bool get_next_message(char const **message)
{
    if (message == NULL)
    {
        return false;
    }

    // Simulate some logic which tells if there is a message available
    static bool message_available = true;

    if (message_available)
    {
        *message = "Hello, World!";

        // Simulate that the message is consumed
        message_available = false;
    }
    else
    {
        // Oops missing `*` here -> Leads to infinite loop in `main`
        message = NULL;
    }

    return true;
}

int main(void)
{
    char const *message = NULL;

    do
    {
        if (get_next_message(&message))
        {
            if (message != NULL)
            {
                printf("Message: %s\n", message);
            }
            else
            {
                printf("No message available\n");
                break;
            }
        }
        else
        {
            printf("Error: get_next_message() failed\n");
            return 1;
        }
    } while (message != NULL);

    printf("End of program\n");
    return 0;
}

So far so good, right? Well not exactly. As commented in get_next_message the asterisk is missing. Due to the missing * the value of the pointer itself is changed on the function stack. So instead of terminating, we now have an infinite loop in main. This error is obvious if you see it, but it is easy to overlook. Furthermore, the compiler does not even throw a warning. Just recently, I had to debug such an error, which can be cumbersome if the error is in a deeply nested code block.

Comparison to Rust

The function get_next_message in the C code does the following:

  • It checks if there is an existing message.
  • It return this optional message via an output parameter.
  • It does not use the return function, because retrieving the message can fail. So the return value signals, if the message retrieval is successful.

The Rust standard library provides special enums for representing optional values and success or failure:

  • Option
  • Result

I really want to recommend these YouTube videos to get familiar with Option and Result:

Rewriting the code from the c example in rust looks roughly like this:

// Get the next message from e.g. a file.
// Getting a message might fail for some reason and there might be no message available.
fn get_next_message() -> Result<Option<String>, ()> {
    use std::sync::Mutex;

    // Use lazy_static to create a static mutable variable.
    // Rust does not allow mutable static variables without using unsafe.
    lazy_static::lazy_static! {
        static ref MESSAGE_AVAILABLE: Mutex<bool> = Mutex::new(true);
    }

    let mut message_available = MESSAGE_AVAILABLE.lock().unwrap();

    if *message_available {
        *message_available = false;
        Ok(Some("Hello, world!".to_string()))
    } else {
        Ok(None)
    }

    // The Mutex is automatically unlocked when it goes out of scope via its Drop implementation.
}

fn main() -> Result<(), ()> {
    loop {
        match get_next_message() {
            Ok(Some(message)) => {
                println!("Message: {}", message);
            }
            Ok(None) => {
                println!("No message available");
                break;
            }
            Err(_) => {
                println!("Error: get_next_message() failed");
                return Err(());
            }
        }
    }

    println!("End of program\n");
    Ok(())
}

I want to mention some aspects I really like about this sample code:

  • Using Option inside Result clearly signals, that the function might fail. In case of success it is also clearly visible, that the returned value is optional.
  • Option and Result wrap the values. Rust forces the user to unwrap these to get the wrapped value again. Therefore, checking the result is mandatory to access the inner return value.

That's all for this blog post. Thanks for visiting! 😊