3
votes
#include <stdio.h>

void main()
{
    unsigned char a = 0xfb;
    char b = 0xfb;
    printf("case1 : %d, %d", a, b); // case1

    char x=90, y=60, z=100, h;
    h = x*y/z;
    printf("\ncase2 : %d", h);      // case2

    int m = 32768, n = 65536, o = 2, i;
    i = m*n/o;
    printf("\ncase3 : %d", i);      // case3
}

result

case1 : 251, -5
case2 : 54
case3 : -107341824

In case1, identifier b is compiled as -5, which is not syntax error thought char only accept -128~127 value. So, first question is identifier b is firstly saved as int data type by end of its translation?(When translation is end, b will be saved in char.)

In case2, x, y is promoted as int. So h has right result value. But in case3, m, n aren't promoted as unsigned int(maybe). Identifier I doesn't have ordinary value(2^30). C99 says that

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.

Being based on C99, value of h is natural, but dealing with m*n/o is overflowed. It's not natural because it's opposed to C99. This is my second query.

3
Please don't post images of code - copy the text into the question.Ken Y-N
Promotion is not occurring in your "case 3" - as m and n are already defined as int. Thus, for i = m * n / o you are simply seeing integer overflow.Adrian Mole
@Adrian Mole According to 'If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.', I thought m and n are relevant with 'otherwise, it is converted to an unsigned int.' So I can't understand promotion isn't occurring in case3.op ol
In that case, there is no promotion - just a conversion (or two) between signed and unsigned.Adrian Mole
m * n exceeds the storage size for int (by one), the intermediate result cannot be represented by int. Why is unsigned integer overflow defined behavior but signed integer overflow isn't?David C. Rankin

3 Answers

4
votes

In case1, identifier b is compiled as -5, which is not syntax error thought char only accept -128~127 value. So, first question is identifier b is firstly saved as int data type by end of its translation?(When translation is end, b will be saved in char.)

In case 1, the initializer for variable b, the constant 0xfb, represents a value of type int with value 251 (decimal). In the C abstract machine model, this value is converted to b's type (char) when the initializer runs, which for a block-scope variable is when execution reaches that declaration (not during translation). If indeed the range of char in your implementation is -128 - 127 then you have signed chars that cannot represent the initializer value, resulting in implementation-defined behavior.

So, again referencing the abstract machine model, nothing is stored in b at the end of translation. It is a block-scope variable, so it does not exist until execution reaches its declaration. The translated program does need somehow to store b's initial value, but C does not specify in what form it should do so. At no time during translation or execution does b contain a value of type int, however.

When the arguments to the printf call are evaluated, b's value is read and then converted to int (because it is a variadic argument). Arguably, then, it is ok to use a %d field to print it, but if you want to be certain to print the pre-conversion value then you should really use %hhd instead (though in your case, that will almost certainly print the same result).

In case2, x, y is promoted as int. So h has right result value.

More specifically, in case 2, the values of x, y, and z are promoted to int during evaluation of the expression x*y/z, and each operation produces an int result. The multiplication does not overflow the chosen type, int, and the overall result is in the range of type char, so the conversion to char applied upon assignment to h is unremarkable.

But in case3, m, n aren't promoted as unsigned int(maybe). Identifier I doesn't have ordinary value(2^30).

In case 3, m, n, and o already have type int, so they are not promoted, and the arithmetic expressions compute results of the same type (int). The result of sub-expression m*n is not in the range of type int, so undefined behavior ensues, pursuant to paragraph 6.5/5 of the Standard:

If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.

It is true that

C99 [and C11] says that

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.

, but that is irrelevant here. It is part of the description of the of the "integer promotions", which apply to the operands of an expression, based on their types.

Being based on C99, value of h is natural, but dealing with m*n/o is overflowed. It's not natural because it's opposed to C99. This is my second query.

You seem to expect that the intermediate expression m*n will be evaluated to produce a result of type unsigned int, so that it does not overflow, but this is not supported by the standard. The usual arithmetic conversions, including the integer promotions, are based only on the characteristics of operand types, including their signedness and value ranges. Operators that are subject to the usual arithmetic conversions, including *, use them to determine a common type for all operands and the result.

Your m and n already being the same type, and that type being int, no conversions / promotions apply. The result of the multiplication would also be an int if m and n's values were not such that their product (as an int) is undefined. In fact, however, the operation overflows type int, yielding undefined behavior.

4
votes

In case 1, initializing a char, which is signed on your platform, with the value 0xfb, greater than CHAR_MAX, is not a syntax error. The effect is implementation defined and unsurprisingly 0xfb is converted to the value -5 which has the same 8-bit pattern. a and b are promoted to int when passed as variable arguments to printf because this is the specified behavior in such a case.

In case 2, x, y and z are promoted to int, because on your platform type int can represent all values of type char. The computation is performed with int arithmetic, producing a result of 54, which is in the range of type char, so can be stored into h without a problem. h is promoted to int when passed to printf as above.

In case 3, m and n are not promoted because they have type int already. The promotion rules apply to each operand, not to the result of the operation as if it was performed with arbitrary precision: m*n is performed with int arithmetic and overflows on your platform where int is presumably 32 bits wide. The behavior is undefined. It so happens that the result is -2147483648 so dividing it by 2 yields -107341824 but this is not guaranteed by the C Standard.

For the computation to be performed accurately, at least m or n must have a larger type or be cast as such: i = (unsigned)m * n / o; would produce 107341824 on your platform, but beware that type int might have fewer than 32 bits. For a portable expression, you would need to use type unsigned long, which is specified as having at least 32 value bits, for both the cast and the type of i.

Finally, main should be defined as int main() or int main(void) or int main(int argc, char *argv[]) or a compatible prototype. void main() is incorrect.

0
votes
#include <stdio.h>

int main()
{
    unsigned char a = 0xfb;
    char b = 0xfb;
    printf("case1 : %d, %d", a, b); // case1

    char x=90, y=60, z=100, h;
    h = x*y/z;
    printf("\ncase2 : %d", h);      // case2

    unsigned long long int  m = 32768, n = 65536, o = 2,i;
    i = m*n/o;
    printf("\ncase3 : %llu", i);      // case3

}

in this type you can get answer [0, +18,446,744,073,709,551,615] range