Why you should NOT cast

June 16, 2007

People often write code like the following, and it's just doomed to failure. Please do yourself a favor, run it, and never ever think again about casting from a pointer to void * or vice-versa.

It throws warnings? Include the f!cking header.

The following two examples will make problems on architectures where sizeof(int) != sizeof(void *), which includes AMD64, sparc64, IA64 and perhaps many more.

Example 1

The following two files, main.c and mmap_malloc.c need to remain separate. Compile with `gcc -Wall -ansi -pedantic -o test1 main.c mmap_malloc.c` and watch what happens. The value in x is truly invalid as far as memory regions are concerned and would yield a segfault for sure.

#include <stdio.h>
int main(void)
{
        char *x = (char *)mmap_malloc(10);
        printf("%p\n", x);
        return 0;
}

#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
void *mmap_malloc(size_t n)
{
        unsigned long long start;
        void *ret;
        int fd;
        if ((fd = open("/dev/zero", O_RDWR)) < 0)
                return NULL;
        start = 0x123456;
        start <<= 16;
        ret = mmap((void *)start, n, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
        close(fd);
        printf("ret=%p\n", ret);
        return ret;
}

Example 2

And here is one example — with the original malloc even — that definitely segfaulted for me. Compile with `gcc -Wall -fno-builtin-malloc -o test2 test2.c`. It is only visible in an environment, where sizeof(char *) != sizeof(int), e.g. x86_64, sparc64, and others. And you DO want to write portable code.

#include <stdio.h>
int main(void)
{
        unsigned int i;
        for (i = 0; ; ++i) {
                char *x = (char *)malloc(1048576); /* 1 MB */
                printf("%p ... after %u iterations\n", x, i);
                if(x == NULL)
                        break;
                *x = '\0';
        }
        return 0;
}