5
votes

I spent many hours debugging a problem that turned out to be caused by two source files including two header files in a different order. One of those headers defined _FILE_OFFSET_BITS to 64, and the other header file included <sys/types.h>, which defined off_t to be either 32 or 64 bits long, depending on the setting of _FILE_OFFSET_BITS. I've included below a short example of this situation. This was on x86_32 Linux (both Debian unstable and CentOS 4.8).

Neither gcc -Wall main.c other.c, nor Solaris 9 lint, nor splint detects this situation.

Does anyone know of a software tool that can detect this situation?

main.c

#define _FILE_OFFSET_BITS 64
#include <sys/types.h>
#include <stdio.h>

#include "header.h"

int
main(int argc, char **argv) {
    struct foo bar = {(off_t) 0, "foo"};

    showproc(&bar);
    printf("sizeof(off_t) in main.c is %d\n", sizeof(off_t));
    return 0;
}

other.c

#include <sys/types.h>
#define _FILE_OFFSET_BITS 64
#include <stdio.h>

#include "header.h"

void
showproc(const struct foo *p)
{
        if (p->offset == 0) {
            if (p->s == NULL)
                puts("NULL pointer reference");
            else
                printf("Structure value is %s\n", p->s);
        }
        printf("sizeof(off_t) in other.c is %d\n", sizeof(off_t));
}

header.h

struct foo {
        off_t           offset;
        const char *    s;
};

extern void showproc(const struct foo *);

Program Output

NULL pointer reference
sizeof(off_t) in other.c is 4
sizeof(off_t) in main.c is 8
2
I'm not sure how a software tool would detect that you did that accidentally -- often headers have #ifdefs you can use to intentionally cause behavior like thisMichael Mrozek
Yep, that would actually even be useful most of the time. Still... tried to go through the gcc and cpp man pages and didn't find anything.haylem
This has nothing to do with typechecking. The type is the same in both cases, off_t. It's a matter of dependencies between macros and conditional compilation with #if.Jens

2 Answers

4
votes

I would recommend putting defines that modify headers like that in the makefile instead of in the code. Otherwise you can't be sure which compilation units have one definition or the other, as you've experienced.

Sometimes modifying headers with macros is the intended behavior (e.g. using headers as templates) and sometimes it isn't (like in your case), so it is hard to produce meaningful warnings from a tool, I think.

2
votes

If you need to define something in a header file, make sure that anything that uses it includes that header. That includes other headers.