carraysstringncurses

Ncurses - why doesn't printing a string using mvprintw() not work when multiple arguments in function call are present?


I've just started using ncurses. An array validity defined as

static const char* validity[] = {
    "invalid",
    "valid"
};

allows me to map the 0 to invalid and the 1 to valid where necessary in order to make a more readable output in my application.

I also have the following lines in my code:

if(data->pos != NULL) {
    mvprintw(4, 0,
             "Position: X %5d    Y %5d    Z %5d    \n",
             data->pos->x, data->pos->y, data->pos->z);
    // ...
}

This allows me to output a 3D coordinate that is stored inside a struct data, which is updated multiple times every second and filled with new coordinates. It works without any issues.

Next thing I have is use mvprintw(...) to output a single string argument:

if(data->detected != NULL) {
    mvprintw(5, 0,
             "Detected: %s\n",
             booleans[data->detected]);
}

where booleans is very similar to the validity array but with true and false strings inside to map boolean values to strings. This works too!

However the following code outputs a strange result:

if(data->validity_check != NULL) {
    mvprintw(11, 0,
             "Validity:     %d [%s]\n",
             data->validity_check->timestamp,
             validity[data->validity_check->valid]);
}

validity_check is just another struct that contains a timestamp (as long integer) and a valid flag which can be either 0 or 1.

The output should look for example like

Validity:    123456789 [invalid]

but instead I get

Validity:    123456789 [(null)]

I was quite surprised by this and made an output using printf() to see if valid actually contained valid data or not (that's the problem with C-arrays - no check for out of bounds). The result: it did. The values were jumping between 0 and 1 as expected. For some reason however the second argument appeared to be "broken". I even removed the retrieval from the array and used a hard-coded argument value but still no change.

Further investigation made me rewrite this part of the code like this:

if(data->validity_check != NULL) {
    mvprintw(11, 0,
             "Validity:     %d",
             data->validity_check->timestamp);
    printw(" [%s]\n", validity[data->validity_check->valid]);
}

It worked without any issues and I got the desired result. However I have no idea what I'm doing wrong here. I have looked the documentation multiple times but I'm probably missing something since I can't find anything that can explain this behaviour.


Solution

  • You write:

    validity_check is just another struct that contains a timestamp (as long integer) and a valid flag which can be either 0 or 1.

    If timestamp is a long int, you should use the %ld format instead of just %d:

    if (data->validity_check != NULL) {
        mvprintw(11, 0,
                 "Validity:     %ld [%s]\n",
                 data->validity_check->timestamp,
                 validity[data->validity_check->valid]);
    }
    

    The printf family of functions are variadicfunctions and the format string must tell them exactly which type to use. (This is diffferent to normal functions, where the type of arguments is known and types get narrows or promoted as appropriate.)

    That's a common type of error, but enabling warnings should tell you about such format mismatches, at least in GCC and Clang.