embedded software boot camp

Formatted output when using C99 data types

Tuesday, February 1st, 2011 by Nigel Jones

Regular readers of this blog will know that I am a proponent of using the C99 data types. They will also know that I’m no fan of formatted output. Notwithstanding this, I do use formatted output (particularly vsprintf) on larger systems. Well if you use the C99 data types and you use formatted output, you will quickly run into a problem – namely what modifier do you give printf()  to print say a uint16_t variable? Now if you are working on an 8 or 16 bit architecture, then you’d probably be OK guessing that %u would work quite nicely. However if you are working on a 32 bit architecture, what would you use for say a uint_fast8_t variable? Well it so happens that the C99 folks were aware of this problem and came up with just about the ugliest solution imaginable.

inttypes.h

In order to solve this problem, you first of all need to #include a file inttypes.h. This header file in turn includes stdint.h so that you have access to the C99 data types. If you examine this file, you will find that it consists of a large number of definitions. An example definition might look like this:

#define PRId16 __INT16_SIZE_PREFIX__ "d"

If you are like me, when I first saw this I was a little puzzled. How exactly was this supposed to help? Well I’ll give you an example of its usage, and then explain how it works.

#include <inttypes.h>
#include <stdio.h>

void print_int16(int16_t value)
{
 printf("Value = %" PRId16, value);
}

So what’s going on here? Well let’s assume for now that __INT16_SIZE_PREFIX__ is in turn defined to be “h”.  Our code is converted by the preprocessor into the following:

#include <inttypes.h>
#include <stdio.h>

void print_int16(int16_t value)
{
 printf("Value = %" "h" "d", value);
}

At compile time, the successive strings “Value = %” “h” “d” are concatenated into the single string “Value = %hd”, so that we end up with:

#include <inttypes.h>
#include <stdio.h>

void print_int16(int16_t value)
{
 printf("Value = %hd", value);
}

This is legal syntax for printf. More importantly, the correct format string for this implementation is now being passed to printf () for an int16_t data type.

Thus the definitions in inttypes.h allow one to write portable formatted IO while still using the C99 data types.

Naming Convention

Examination of inttypes.h shows that a consistent naming convention has been used. For output, the constant names are constructed thus:

<PRI><printf specifier><C99 modifier><number of bits> where

<PRI> is the literal characters PRI.

<printf specifier> is the list of integer specifiers we all know so well {d, i, o, u, x, X}

<C99 modifier> is one of {<empty>, LEAST, FAST, MAX, PTR}

<number of bits> is one of {8, 16, 32,64 <empty>}. <empty> only applies to the MAX and PTR C99 modifiers.

Examples:

To print a uint_fast8_t in lower case hexadecimal you would use PRIxFAST8.

To print a int_least64_t in octal you would use PRIoLEAST64.

Formatted Input

For formatted input, simply replace PRI with SCN.

Observations

While I applaud the C99 committee for providing this functionality, it can result in some dreadful looking format statements. For example here’s a string from a project I’m working on:

wr_vstr(1, 0, MAX_STR_LEN, "%-+*" PRId32 "%-+4" PRId32 "\xdf", tap_str_len, tap, angle);

Clearly a lot of this has to do with the inherently complex formatted IO syntax. The addition of the C99 formatters just makes it even worse.

Personally I’d have liked the C99 committee to have bitten the bullet and introduced a formatted IO function that had the following characteristics:

  1. Explicit support for the C99 data types.
  2. No support for octal. Does anyone ever use the octal formatter?
  3. Support for printing binary – this I do need to do from time to time.
  4. A standard defined series of reduced functionality formatted IO subsets. This way I’ll know that if I restrict myself to a particular set of format types I can use the smallest version of the formatted IO function.

PC Lint

Regular readers will also know that I’m a major proponent of using PC-Lint from Gimpel. I was surprised to discover that while Lint is smart enough to handle string concatenation with printf() etc, it doesn’t do it with user written functions that are designed to accept format strings. For example, the function wr_vstr() referenced above looks like this:

static void wr_vstr(uint_fast8_t row, uint_fast8_t col, uint_fast8_t width, char const * format, ...)
{
 va_list  args;
 char  buf[MAX_STR_LEN];

 va_start(args, format);
 (void)vsnprintf(buf, MAX_STR_LEN, format, args);     /* buf contains the formatted string */

 wr_str(row, col, buf, width);    /* Call the generic string writer */

 va_end(args);                    /* Clean up. Do NOT omit */
}

I described this technique here. Anyway, if you use the inttypes.h constants like I did above, then you will find that PC-Lint complains loudly.

Final Thoughts

Inttypes.h is very useful for writing portable formatted IO with the C99 data types. It’s ugly – but it beats the alternative. I recommend you add it to your bag of tricks.

4 Responses to “Formatted output when using C99 data types”

  1. david collier says:

    Thanks Nigel.

    I have to admit I’ve bing using u8 and s32 without realisiung that there was a standard I cold be adhering to.

  2. groovyd says:

    for embedded systems i recommend writing your own printf/scanf routines since then you can also make portable formatted strings using any format identifiers you choose to mean whatever you want and to run easily 3 times faster then the library versions. i have written one which has options for not just left and right but center justifications as well. they are pretty simple routines to write and the performance/size/flexibility benefits are tremendous. also the library functions usually use heaps of ram.

    feel free to email me at embeddedgurus – at – groovydomain – dot – com if you would like the routines i wrote and use. would love to have a few sets of optimizing eyes over it.

  3. coderguy says:

    agreed on a need for %b. Tho this suffices.

    void toBinary(uint64_t x)
    {
    const int bitsToDisplay = 64;
    char s[bitsToDisplay+1];
    int i=bitsToDisplay;
    s[i–]=0x00;
    do
    {
    s[i–]=(x & 1) ? ‘1’:’0′;
    x>>=1;
    } while( x > 0);
    while(i>=0) s[i–]=’0′;
    printf(“0b%s\n”, s);
    }

  4. mjk says:

    PC-lint (at least since 9.00 k) supports this well:

    //lint -printf(4, wr_vstr)
    #include
    extern void wr_vstr(uint_fast8_t row, uint_fast8_t col, uint_fast8_t width, char const * format, …);
    int main ( int16_t argc, char const * const *argv ) {
    ` //lint -message(“PRIi64 is ” PRIi64 ” while PRIi16 is ” PRIi16)
    ` ` Info 865: PRIi64 is lli while PRIi16 is i
    ` wr_vstr(10, 20, 30, “%” PRIi64 ” %s”, argc, argv[0]);
    ` ` Warning 559: Size of argument no. 5 inconsistent with format
    }

Leave a Reply to coderguy