3

Simple question on type conversion in C, assume this line of code:

signed char a = 133;

As the max value of a signed char is 128, does the above code have implementation defined behaviour according to the third rule of casting?

if the value cannot be represented by the new type and it's not unsigned, then the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Dan
  • 2,694
  • 1
  • 6
  • 19
  • It's undefined. Which means it depends on your implementation. Chances are it works, but it might fail tomorrow... – Aganju Mar 07 '21 at 19:33
  • @Aganju undefined behavior happens when you do arithmetic as far as I know. Since no arithmetic is going on here, no undefined behavior. – Aykhan Hagverdili Mar 07 '21 at 19:33
  • 4
    `implementation defined` is different from `undefined` behaviour. – Dan Mar 07 '21 at 19:34
  • 1
    Your quote seems to answer your question. What's not clear about it? – Aykhan Hagverdili Mar 07 '21 at 19:35
  • 1
    It's not casting, it's an [integer *conversion*](https://en.cppreference.com/w/c/language/conversion#Integer_conversions). Casting is explicit, conversion is implicit. However, the result is indeed implementation defined. – Some programmer dude Mar 07 '21 at 19:36
  • I thought maybe the quote is only referring to casting from say `int` to `char`, i.e a bigger data type to a smaller one. – Dan Mar 07 '21 at 19:37
  • @Dan it's not casting, it's conversion, and that exactly what's happening here, a conversion from `int` to `signed char`, a bigger to a smaller. – anastaciu Mar 07 '21 at 19:43
  • 2
    @Dan: `133` is an `int` constant. Using it to initialize an eight-bit `signed char` results in a conversion from `int` to `signed char`. – Eric Postpischil Mar 07 '21 at 20:09
  • @Dan Can you please provide the section in the standard with the "third rule of casting"? So far, casting is - as others already wrote - something different. As this is a language-lawyer question, you are expected to use established terminology. – too honest for this site Mar 07 '21 at 20:43
  • The first rule of casting is, don't talk about casting when it's not a cast! (This is an implicit conversion) – M.M Mar 07 '21 at 20:46
  • @toohonestforthissite: C 2018 6.3.1.3 3. The text in the question almost quotes it: “Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.” – Eric Postpischil Mar 07 '21 at 20:56
  • @EricPostpischil: I know you know (and you should know I know:-). I asked OP for a reason about casting, not conversion, btw. – too honest for this site Mar 07 '21 at 20:57
  • 2
    Off by one: "As the max value of a signed char is 128" is more like "As the max value of a signed char is 127" – chux - Reinstate Monica Mar 07 '21 at 21:19
  • The title is incorrect, this is assigning a positive signed value – M.M Mar 07 '21 at 21:28

2 Answers2

5

First of all, 133 is not unsigned. Since it will always fit in an int, it will be of type int, and signed (furthermore in C99+, all unsuffixed decimal constants are signed! To get unsigned numbers you must add U/u at the end).

Second, this isn't a cast but a conversion. A cast in C is explicit conversion (or non-conversion) to a certain type, marked with construct (type)expression. In this case you could write the initialization to use an explicit cast with

signed char a = (signed char)133;

In this case it would not change the behaviour of the initialization.

third, this is indeed an initialization, not an assignment, so it has different rules for what is an acceptable expression. If this initializer is for an object with static storage duration, then the initializer must be a certain kind of compile-time constant. But for this particular case, both assignment and initialization would do the conversion the same way.

Now we get to the point whether the 3rd integer conversion rule applies - for that you need to know what the 2 first ones are:

  1. the target type is an integer type (not _Bool) with the value representable in it (does not apply in this case since as you well know 133 is not representable if SCHAR_MAX is 127)

  2. the target type is unsigned (well it isn't)

so therefore we get to C11 6.3.1.3p3:

  1. Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

The question is whether it has implementation-defined behaviour - yes, the implementation must document what will happen - either how it calculates the result, or which signal it will raise in that occasion.

For GCC 10.2 the manuals state it:

  • The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (C90 6.2.1.2, C99 and C11 6.3.1.3).

    For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised.

The Clang "documentation" is a "little" less accessible, you just have to read the source code...

-3

This is implicit type conversion at assignment. 133 will be copied bit by bit into the variable a . 133 in binary is 10000101 which when copied into a will represent a negative number as, if leading bit is 1 then it represents a negative number. then the actual value of a will be determined by using 2's compliment method which comes out to be -123 (It also depends on how negative numbers are implemented for signed data types).

  • 3
    Whether the value is converted by copying bits or by another method is implementation-defined, as the C standard text quoted in the question says. Many C implementations may use the low bits of the value, but other reasonable behaviors including clamping (producing the minimum or maximum representable value, according to the direction of the overflow) or raising a signal. – Eric Postpischil Mar 07 '21 at 20:08
  • As of the current revision 2's complement is not required for integer encoding. This will change in the future with C23 as it appears. – too honest for this site Mar 07 '21 at 20:48
  • 1
    @toohonestforthissite note that the implementation can still define the result to anything, regardless of whether 2's complement mandated – M.M Mar 07 '21 at 21:05
  • 2
    @M.M Sure. It just needs to specify what. For DSPs e.g. capping at SCHAR_MAX could be even reasonable. – too honest for this site Mar 07 '21 at 21:07