Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is "char foo = 255" undefined behavior if char is signed?

Tags:

c

gcc

The following doesn't give me any warning whatsoever when compiled with gcc 4.5.2 on x86 machine with Linux:

char foo = 255; 

But when I use -pedantic, gcc says:

warning: overflow in implicit constant conversion

The way gcc acts is a tad strange and it makes me doubt if I really understand what's going on in this assignment. I think that if char is 8 bit long on POSIX and it's signed by default, it can't hold 255.

In C standard, it says that unsigned integer overflow results in overflow, but signed integer overflow is undefined. So is this assignment undefined behavior? And why does gcc act this way?

like image 915
user1042840 Avatar asked Sep 20 '13 17:09

user1042840


People also ask

Is signed integer overflow undefined?

12.2. 1 Basics of Integer Overflow In contrast, the C standard says that signed integer overflow leads to undefined behavior where a program can do anything, including dumping core or overrunning a buffer. The misbehavior can even precede the overflow.

Does Java have undefined behavior?

Some languages such as C/C++ define many constructs as undefined behavior, while other languages, for example Java, have less undefined behavior [7].


1 Answers

Summary: The result is implementation-defined and very likely to be -1, but it's complicated, at least in principle.

The rules regarding overflow are different for operators vs. conversions, and for signed types vs. unsigned types -- and the conversion rules changed between C90 and C99.

As of C90, overflow of an operator with signed integer operands ("overflow" meaning that the mathematical result cannot be represented in the expression's type) has undefined behavior. For unsigned integer operands, the behavior is well defined as the usual wraparound (strictly speaking the standard doesn't call this an "overflow"). But your declaration:

char foo = 255; 

doesn't use any operators (the = is an initializer, not an assignment), so none of that applies in this case.

If type char can represent the value 255 (which is true either of plain char is unsigned or if CHAR_BIT >= 9), then of course the behavior is well defined. The int expression 255 is implicitly converted to char. (Since CHAR_BIT >= 8, it's not possible for this particular case to invoke unsigned wraparound.)

Otherwise, the conversion yields a result that can't be stored in a char.

As of C90, the result of the conversion is implementation-defined -- which means that it's guaranteed to set foo to some value within the range of type char, and you can determine what that value is by reading the implementation's documentation, which is required to tell you how the conversion works. (I've never seen an implementation where the stored value is anything other than -1, but any result is possible in principle.)

C99 changed the definition, so that an overflowing conversion to a signed type either yields an implementation-defined result or raises an implementation-defined signal.

If a compiler chooses to do the latter, then it must document which signal is raised.

So what happens if an implementation-defined signal is raised? Section 7.14 of the standard says:

The complete set of signals, their semantics, and their default handling is implementation-defined

It's not entirely clear (to me) what the range of possible behaviors for the "default handling" of signals is. In the worst case, I suppose such a signal could terminate the program. You might or might not be able to define a signal handler that catches the signal.

7.14 also says:

If and when the function returns, if the value of sig is SIGFPE, SIGILL, SIGSEGV, or any other implementation-defined value corresponding to a computational exception, the behavior is undefined; otherwise the program will resume execution at the point it was interrupted.

but I don't think that applies, since an overflowing conversion is not a "computational exception" as the term is used here. (Unless the implementation-defined signal happens to be SIGFPE, SIGILL, or SIGSEGV -- but that would be silly).

So ultimately, if an implementation chooses to raise a signal in response to an overflowing conversion, the behavior (not just the result) is at least implementation-defined, and there might be circumstances in which it could be undefined. In any case, there doesn't seem to be any portable way to deal with such a signal.

In practice, I've never heard of an implementation that takes advantage of the new wording in C99. For all compilers I've heard of, the result of the conversion is implementation-defined -- and very probably yields what you'd expect from a 2's-complement truncation. (And I'm not at all convinced that this change in C99 was a good idea. If nothing else, it made this answer about 3 times as long as it would otherwise have needed to be.)

like image 170
Keith Thompson Avatar answered Sep 28 '22 16:09

Keith Thompson