Dear all,
Could someone please confirm whether the following code is UB or not ?
#include <sys/types.h>
The C standard does not define a standard header with that name, nor
does it define an identifier "off_t", as used below. The contents of
that header (and in particular, the definition it provides for off_t)
determine whether or not this code has undefined behavior.
The POSIX standard does define such a header, which does define a type
named off_t. If you're relying upon that standard, the answers I give
below should be relevant, but you'll get better answers to your question
in a forum devoted to it, such as comp.unix.programmer.
int main(int argc, char **argv)
{
/* Cause a compile-time error if off_t is smaller than 64 bits */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
The << operator has undefined behavior unless the left operand is
unsigned, or is a signed type capable of representing (in this case) 1^62.
I have access to a copy of the POSIX standard, but I haven't been able
to locate a precise specification of the requirements for off_t, only
requirements that it be defined in various headers - I might not be
looking in the right place. I did find that POSIX allows for the
possibility that off_t is only 32 bits, which can be tested by checking
whether _POSIX_V6_ILP32_OFF32 or _POSIX_V7_ILP32_OFF32 are #defined in
<unistd.h>, or by passing _SC_V6_ILP32_OFF32 or _SC_POSIX_V7_ILP32_OFF32
to sysconf(), or by passing the corresponding _CS* strings to confstr().
Given the way that it's used, I suspect that off_t is required to be a
signed type, which is in fact the case on my system. If it's also 32
bits, then (off_t)1 << 62 has undefined behavior.
Other than that issue, I don't see any other problems with that expression.
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
return 0;
}
It does not compile using clang 3.0 (as expected), however it does compile using gcc 4.6 and gcc 4.7.
I think that the -1 subscript was intended to create a constraint
violation, for which a diagnostic is required, which would make it
reasonable to expect the compilation to fail (though, strictly speaking,
an implementation is allowed to continue compiling if it wants to).
However, unless off_t is allowed to be unsigned, which seems unlikely,
the result of -1 cannot occur if evaluation of the shift expressions has
defined behavior. If the behavior is undefined, a diagnostic is not
mandatory, and you cannot justify expecting the compilation to fail.