Casting in C
Richard Heathfield
Last updated: 7 November 2007
Acknowledgements: Philip Potter noticed a bug in my "correct" printf example of why casting does not add clarity to the code. The bug, which is now fixed, simply highlights yet another peril of casting!

Back

Implicit conversions

In the C programming language, there are two kinds of conversions between expressions of different types. The first is supplied by the compiler itself. This is known as an implicit conversion. Here's an example of its use:

long int foo = 314159265L;
double bar = foo;

This kind of conversion is, of course, very natural. In fact, an example of such a conversion occurs as early as page 12 of K&R2 (where the conversion is from float to double). Such conversions are woven into the very fabric of the language, and are so common and natural that we often fail to notice them.

Explicit conversions (casts)

There is, however, another way to convert an expression from one type into another; this second method is known as an explicit conversion, or cast. Here's an example of a cast:

IsLowerCase = islower((unsigned char)*p);

This cast is a good one, by the way. There are circumstances in which explicit conversions, or casts, are a convenience which allows us to write better, cleaner code than would otherwise be the case. And yet casts are poorly understood.

For some reason, casting is very popular among C programmers. It's as if a cast is a magic wand, that can magically transform one type into another, irrespective of semantics, logic, or common sense. In fact, there is very little (if any) sense in casting an expression for which a perfectly adequate implicit conversion already exists. There are very few exceptions to this rule of thumb. For example, consider this code:

#include <math.h>
long GetHypotenuse(long height, long base)
{
  return sqrt(height * height + base * base);
}

Some compilers will complain about this code, arguing that sqrt returns a double, and that assigning such a value to a long can result in a loss of information. Well, that's true. But it's also true that, if such a loss of information is intended, then there's no problem (because we're only interested in the integer part of the result); and if such a loss of information is not intended, then we really ought to pay attention to the compiler's complaint.

Casts as diagnostic suppressors

We can often suppress the warning by using a cast:

#include <math.h>
long GetHypotenuse(long height, long base)
{
  return (long)sqrt(height * height + base * base);
}

The cast, in effect, tells the compiler: "I know what I'm doing! So shut up already!" Many C programmers do this (even those who only think that they know what they're doing). But is the cast justified? Well, in this case, there is a (perhaps spurious) justification, not in technical terms but purely in terms of getting a clean compilation. Alas, this excuse is used to justify a great many unnecessary casts. Yes, it's true that a nice clean compilation is good to see. But if we achieve that state only by gagging the compiler, without understanding the possible implications of such gagging, then we are running the risk of suppressing important and useful diagnostic information. In fact, we are merely indulging, and maybe even deceiving, ourselves. That first example is innocuous enough, but precisely the same logic (getting a clean compilation) can lure us into adding casts that actually hide problems, instead of fixing them.

Casts as bug-hiders

The canonical example of this counter-productive diagnostic suppression is the malloc function. As I'm sure you know, malloc is prototyped in <stdlib.h> as void *malloc(size_t); -- that is, malloc is declared to be a function taking a size_t as a parameter and returning a void pointer (i.e. a pointer to an object whose type is not known).

Many years ago, before C was standardised by ANSI, malloc returned char *, rather than void *; this was the most rational choice at the time, because the void type didn't actually exist then (except, perhaps, as an extension on some compilers). No implicit conversions between various pointer types were supplied, so it was necessary to cast the return value of malloc into the pointer type that you required:

#include <stdlib.h>

T *foo(int x)
{
  T *new = (T *)malloc(sizeof *new); /* ancient history */
  if(new)
  {
    new->zog = x;
  }
  return new;
}

But this ceased to be true in 1989 -- a good 15 years ago as I write this. ANSI C introduced the void * pointer type, and gave it the very useful property of being able to represent (without loss of information) any object pointer whatsoever.

Consequently, it is no longer necessary to cast the return value of malloc. But is it wise?

Clearly, if your code must be portable to compilers that pre-date the ANSI C Standard of 1989, then you have no choice but to cast. Fair enough. But this is true only in a vanishingly small number of cases. By far the majority of C code written today does not need to cater for prehistoric compilers. So we can, for the most part, ignore that reason for adding the cast, and look for other advantages and disadvantages.

All code should either do something good, or stop something bad from happening. Now, what good does a malloc cast do? One argument that is occasionally raised in defence of the cast is that "the cast indicates the type to which the return value is being assigned, so it makes the code more self-documenting". But if that is true, then why do we not use casts more often? Consider this example program from K&R2, page 12:

#include <stdio.h>

/* print Fahrenheit-Celsius table
    for fahr = 0, 20, ..., 300; floating-point version */
main()
{
    float fahr, celsius;
    int lower, upper, step;

    lower = 0;      /* lower limit of temperature table */
    upper = 300;    /* upper limit */
    step = 20;      /* step size */

    fahr = lower;
    while(fahr <= upper) {
        celsius = (5.0/9.0) * (fahr-32.0);
        printf("%3.0f %6.1f\n", fahr, celsius);
        fahr = fahr + step;
    }
}

Now let's add those "self-documenting" casts:

#include <stdio.h>

/* print Fahrenheit-Celsius table
    for fahr = 0, 20, ..., 300; floating-point version */
main()
{
    float fahr, celsius;
    int lower, upper, step;

    lower = (int)0;      /* lower limit of temperature table */
    upper = (int)300;    /* upper limit */
    step = (int)20;      /* step size */

    fahr = (float)lower;
    while((float)fahr <= (int)upper) {
        celsius = (((float)
                   ((float)5.0/(float)9.0)) *
                    (float)((float)
                     (((float)fahr-(float)32.0))));

        (int)(((int (*)(const char *, ...))
             printf)((const char *)"%3.0f %6.1f\n",
               (float)fahr, (float)celsius));

        fahr = (float)((float)fahr + (float)step);
    }
}

Believe it or not, those casts are all "correct". And yes, the code works just fine. But suddenly the code isn't quite as easy to read, is it? So much for self-documentation.

Well, all right -- what about C++? In C++, it is necessary to cast void * into an object pointer type, because the implementation is forbidden from providing an implicit conversion.

Yes, that's absolutely true, but it's also utterly irrelevant. C and C++ are very different languages! They are divided by a common syntax. Nobody in their right mind would dream of saying "I always wrap printf in a function named writeln, to maintain compatibility with Pascal", would they? But because C and C++ have superficial similarities at the syntax level, some people seem to think it's necessary to write C code that compiles with a C++ compiler. Well, it isn't.

If you are using a C++ compiler, then whether you like it or not, you're writing C++ code, not C code. The rules are different. If you wish to write C++ code that casts malloc (instead of using the perfectly serviceable new allocator, or the STL's std::vector template), then that's entirely up to you; good luck to you, and I wish you all joy in your use of C++. This discussion is not directed at C++ users (except, perhaps, to remind them that they are not writing in C, even if they think they are).

If you wish to use C code in a C++ project, that's easy to do, without casting malloc. Use a C compiler to compile the C code (duh!), and then use a linker to link the C code to the C++ code. C++ supplies the extern "C" construct for precisely this purpose.

So far, we have found no good reasons for casting. But are there any good reasons why we should not cast? Yes, there are.

Firstly, as I said earlier, all code should either do something good or stop something bad happening. Casting malloc does neither, so the cast is dead code, and dead code has no place in a C program.

Secondly, casting malloc can actually hide a serious bug. Let me say quickly that the cast doesn't cause the bug. But if the bug is there, the cast can conceal its presence.

The bug in question is that of failing to provide a valid function prototype for malloc. The function returns a void *, of course, but the C compiler doesn't know that, unless you tell it. The best way to tell it is to #include <stdlib.h> which provides a prototype which the compiler uses to do type-checking and which it can exploit for code generation purposes.

Let's consider a couple of ways in which things can go wrong. They both hinge on the wording of section 3.3.2.2 of the ANSI C Standard of 1989, which is as follows:


  If the expression that precedes the parenthesized argument list
  in a function call  consists solely of an identifier, and if no
  declaration  is visible  for this identifier, the identifier is
  implicitly  declared  exactly  as  if, in  the innermost  block
  containing the function call, the declaration

    extern int  identifier();

  appeared.

The following footnote applies to the above text:


 30. That is, a function with external linkage and no information
     about its parameters  that returns an  int. If in fact it is
     not  defined  as  having type "function returning int ," the
     behavior is undefined.

Whilst footnotes are not normative text, they are useful in helping us to understand the intent of the committee, and sensible implementors will generally observe them, so it makes sense for us to take what they say very seriously.

Or, if you're not convinced by that, consider 3.1.6.2(2) of the Standard, which says: "All declarations that refer to the same object or function shall have compatible type; otherwise the behavior is undefined."

Consider the situation from the point of view of the compiler writer. As part of his implementation, he provides a standard C library. As part of his C library, he implements the malloc function. He almost certainly uses his own C compiler to do this. The C compiler generates object code for malloc, and this object code is placed into the standard C library, which he then releases. Of course, this object code is based firmly on a malloc that returns void *.

When you write a C program that calls malloc, the C implementation doesn't have to compile malloc, because it is already compiled. All it has to do is link the standard library (or at least, the parts of it that you actually use) to your program. So nothing has changed, as far as the library is concerned. The malloc function returns void *, and that's that.

In your program, let's just hypothesise for a moment that you forgot to #include <stdlib.h>, so you have no prototype for malloc. The C compiler, on encountering your malloc call, will therefore follow the wording of 3.3.2.2 of the ANSI C Standard, and presume that malloc returns int. It will therefore generate code that assumes the return value of malloc is an int. But you don't assign that value to an object of type int; rather, you assign it to a pointer!

Under normal circumstances, this would require the compiler to issue a diagnostic. That's because the code would violate a constraint (see 3.3.16.1), and the compiler must issue a diagnostic if the program contains any constraint violations. But the cast forces the code to satisfy, rather than violate, the constraint. Consequently, no diagnostic is required.

The wording of the diagnostic is sometimes rather unfortunate. Consider the wording of the gcc diagnostic for this situation:


   initialization makes pointer from integer without a cast

The wording is actually correct, because (by 3.3.2.2) gcc is right to assume that an undeclared function returns int, and it's right in thinking that you are trying to stick this int value into a pointer, but it's a mite misleading, because it leads you to think that the correct fix is to add a cast!

With the cast in place, you may not get a diagnostic at all. So what will happen?

Well, of course, it might just work swimmingly well despite the lack of a prototype. But we can't know that. And even if it does, we have no guarantee that the same code will also work correctly if we were to switch to a different compiler.

What sorts of things can go wrong? I offer you two (but by no means the only two) possibilities. Firstly, what if sizeof(void *) > sizeof(int)? This is not just a theoretical possibility. It is certainly true for, say, typical MS-DOS programs using a large memory model. Here's how it would break: malloc returns a void *, but the compiler is required to turn this value into an int. There aren't enough bits in the int to store the whole value, so some information is lost. The int is then coerced back into a pointer, but the lost information cannot now be retrieved, and if those lost bits actually affect the value (say, they weren't just a bunch of 0s), then the effect is that the pointer object receives an incorrect value -- that is, instead of pointing to the allocated memory block, it points somewhere completely different instead!

What else could go wrong? Well, consider an implementation which has separate registers for pointers and integers. On such an implementation, the library code for malloc will, of course, store the return value, a pointer, in a pointer register. The compiler, however, doesn't know this (because we didn't have a prototype for malloc), so it will actually collect its return value from an integer register. This is a bit like going to the wrong Post Office when collecting a parcel, seeing a parcel that looks about the right size, and grabbing it on the assumption that it's the right one. And yet it can't be the right one, because you're looking in the wrong place.

Again, the outcome is that your pointer object does not get the correct value.

As a consequence, we must conclude that to cast malloc is dangerous, and that a competent C programmer simply should not do it.

Incidentally, precisely the same argument applies to any function that returns a pointer; functions such as bsearch, strcpy, memmove, other standard library functions returning pointers, and of course any of your own custom functions that return pointers.

Under what circumstances is casting correct?

Very few. Casting is almost always wrong, and the places in which it is correct are rarely the ones you would guess.

One situation in which casting is a good idea is when you are calling any of the functions prototyped in <ctype.h>. These functions take an int as input, but the value stored in that int must either be EOF or a value that can be represented in an unsigned char. Assuming that you're not daft enough to pass EOF to such a function, then, it makes sense to cast the value you are passing, unless you have some excellent reason for knowing that it's bound to be in the appropriate range. So, for example, you could reasonably call toupper in this way:


  ch = toupper((unsigned char)ch);

What you don't have to do is worry about is casting to int (in this case). Let the ordinary C promotion mechanism handle that for you.

When you are passing a value to the "tail" of a variadic function, you must get the type just right, because the normal promotions won't be done, which is in turn because the compiler has no type information to work with. If the variadic function takes a T *, and you have a void * which you happen to know points to an object of type T, that's fine, but you must cast the pointer, to yield an expression of type T *. Conversely, if the function expects a void *, you should cast to void * unless the pointer you have is already of that type. Thus, when you call printf with a %p format specifier, your matching pointer either should be a void * already, or should be cast to one:


  printf("Pointer value: %p\n", (void *)MyTPointer);

For the same basic reason, you should cast a size_t when printing it. I generally use unsigned long for this purpose:


  printf("Size: %lu\n", (unsigned long)sizeof MyTObject);

Summary

One of the characteristics of an expert C programmer is that he or she knows in what circumstances a cast is required and in what circumstances it is at best redundant and at worst a source of problems. Most programmers, however, are guilty of "cargo cult" programming where casts are concerned. Don't do that. Be an expert. Know why you are casting, whenever you cast, and remember when maintaining your own or other people's code that almost all casts in existing code should not actually be there.