C Tips and Tricks, Part I

C is a finicky language that takes some extra care to avoid shooting yourself in the foot. This is part I in a series covering some tips, best practices and common pitfalls when working with C.

This post focuses on how to construct constant and volatile pointers, and how to avoid a common pitfall when dealing with floating point constants.

Const and volatile pointers demystified:

Have you ever been confused by const pointers in C? A constant integer is easy, but when you want a pointer to a constant value, or a constant pointer to a mutable value things can get a bit more confusing.

There are just two things you need to know:

1. You can put the const keyword on either side of the type it modifies without changing the meaning of the type. So

const uint32_t x = ...;

is equivalent to:

uint32_t const x = ...;

Which is annoying, but it’s C, what do you expect?

2. C types “read” from right to left.

so the type:

const uint32_t * const

reads as “a constant pointer to a uint32_t that is constant“, but that sounds awkward, so let’s switch where the const goes:

uint32_t const * const

which reads: “a constant pointer to a constant uint32_t.” This is the same type but it reads much better :)

So if you want a mutable pointer to a constant uint32_t:

uint32_t const * x = ...;
Reads as “pointer to constant uint32_t

Here we can change the value of x, but not *x:

 x = something; -> O.K.
*x = something; -> error!>

or constant pointer to a mutable uint32_t:

uint32_t * const x = ...;
Reads as “constant pointer to uint32_t”

Here we can’t change x, but we can change *x:

 x = something; -> error!
*x = something; -> O.K.

or a constant pointer to a constant uint32_t:

uint32_t const * const x = ...;
Reads as “constant pointer to a constant uint32_t

Here we can’t change x, or *x.

 x = something; -> error!
*x = something; -> error!

Most of the syntactic rules for const apply to the volatile keyword as well. So the type:

uint32_t const * volatile

reads as “volatile pointer to a constant (but non-volatile) uint32_t” and

uint32_t const volatile * volatile

reads as “volatile pointer to a volatile constant uint32_t

Hopefully that should help clear things up :)

Avoid macros for floating point constants

In C, you should never use a macro to define a floating point constant. There is no advantage in doing so (over a plain constant) and it can cause unexpected behavior.

For example, consider the following snippet of code:

/* set default control setting */
float control_setting = THIRTY_PERCENT;

if (THIRTY_PERCENT == control_setting)
{
printf("Control is still at the default setting.\n");
}

One would normally expect this to print “Control is still at the default setting.” Unfortunately this isn’t what happens.

The trouble here is that 0.3 is not fully representable in a binary. Decimal literals in C have type double by default. When we store THIRTY_PERCENT (0.3) in control_setting the double approximation of 0.3 gets rounded down to fit in float. When we do the comparison C’s implicit conversion rules cause control_setting to get promoted to double, but control_setting already contains a less precise approximation of 0.3 and so the comparison fails.

One possible alternative is to append the appropriate type specifier to the end of the literal like so:

#define THIRTY_PERCENT 0.3f

However, it’s better to avoid the preprocessor whenever it’s reasonable to do so. In this case (and for most constants) I prefer to use static const instead.

static const float THIRTY_PERCENT = 0.3;

Technically this doesn’t really solve the problem. You might actually want the type of THIRTY_PERCENT to be a double, in which case you’d run into the same problem!  It’s part of the inherent dangers when doing floating point comparisons, especially in a language that performs implicit type coercions, but at least now the type of THIRTY_PERCENT is more obvious, which can help a lot when debugging.

Conversation
  • Matt Shirilla says:

    Thanks for taking the time to explain in this well presented post. I am learning c/c++ as I go, and this will go in my file of examples that I refer to for help.

    • Job Vranish says:

      I’m glad it was useful! Also, if you’re doing C++, note that const has internal linkage by default, so the “static” keyword can be omitted.

  • […] Why you really should put const on the right side of your uint32_tIn my last installment of this series, I mentioned how C types “read” from right to left, and that const/volatile types are […]

  • Comments are closed.