2

Why compiling the following code in gcc does not produce any type mismatch warning? -1 is of type int, and f() expects type char:

void f(char c) {}
int main(void)
{
  f(-1);
  return 0;
}

Even if we explicitly specify the types, there is no warning:

void f(unsigned char c) {}
int main(void)
{
  f((signed int)-1);
  return 0;
}

What is curious: if we specify out-of-range value, the warning is printed:

void f(char c) {}
int main(void)
{
  f(65535);
  return 0;
}

warning: overflow in implicit constant conversion

gcc version 6.1.1

2501
  • 25,460
  • 4
  • 47
  • 87
Igor Liferenko
  • 1,499
  • 1
  • 13
  • 28
  • what happens if you make the function non-empty? – talonmies Sep 26 '16 at 05:22
  • @talonmies `void f(...) { c = 'x'; }` - makes no difference – Igor Liferenko Sep 26 '16 at 05:24
  • Well, look at the warning, "overflow in implicit constant conversion". Is there an overflow in your first example? If there isn't, why would compiler warn about something non-existent. – hyde Sep 26 '16 at 05:45
  • @hyde Because the types are different. Suppose that while testing stage I input values which may be converted, but in production user inputs something unexpected... This is the job of the compiler - to warn about argument type mismatch. Anyway, in case 2) the values cannot be converted, but no warning is issued... – Igor Liferenko Sep 26 '16 at 05:50
  • @hyde In case 2) I explicitly specify char as `unsigned`, so it can hold values from 0 to 255. Right? But then I convert -1 (integer type, 64-bit long on my machine) to the range of values 0..255. -1 is not in the range of 0..255. A warning must be given, because the types are different. – Igor Liferenko Sep 26 '16 at 06:03
  • 1
    @IgorLiferenko Misread code, deleting that comment. Testing when I get to compiler. But I suspect case 2 is just matter of insufficient warning enable ootions for compiler. – hyde Sep 26 '16 at 06:04
  • @hyde see UPDATE. – Igor Liferenko Sep 27 '16 at 08:32
  • @hyde I posted a separate question here https://stackoverflow.com/questions/39720004/why-eof-coincides-with-valid-char-value – Igor Liferenko Sep 27 '16 at 08:41

2 Answers2

5

An int can be converted to a char. In int is allowed to be converted to a char in both C and C++.

From the C11 Standard:

6.3.1.3 Signed and unsigned integers

1 When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

3 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.

From the C++11 Standard:

4.7 Integral conversions

1 A prvalue of an integer type can be converted to a prvalue of another integer type. A prvalue of an unscoped enumeration type can be converted to a prvalue of an integer type.

2 If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer ...

3 If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.

If char is a signed type, it can easily hold the value -1. Hence, the behavior is predictable. The integral value of c in f will be -1. When unsigned char is used, the value of c will be an implementation-defined value but it is still allowed under both the standards.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Is C standard the same as C++ standard ? – Igor Liferenko Sep 26 '16 at 05:31
  • 1
    @IgorLiferenko, it's not same but they follow these rules closely. – R Sahu Sep 26 '16 at 05:37
  • I specify explicitly `unsigned char`, but no warning is printed. Please explain how `unsigned char` can hold value `-1`. – Igor Liferenko Sep 26 '16 at 05:38
  • @IgorLiferenko, when the argument type is `unsigned char`, the value of the argument will be implementation-defined but it is still allowed. – R Sahu Sep 26 '16 at 05:40
  • It is not clear to me in which part of section 6.3.1.3 of the C standard this is stated. BTW, a link to the quoted place of the original standard would be helpful. – Igor Liferenko Sep 26 '16 at 05:43
  • @IgorLiferenko, it's paragraph 3. I have copied the contents of the paragraph in my answer. – R Sahu Sep 26 '16 at 05:44
  • Paragraph 3 does not cover case 2) in original post, because the "new type" is **unsigned** `char c`. How can case 2) be explained? – Igor Liferenko Sep 26 '16 at 05:53
  • @IgorLiferenko, you are right. I had left them out by mistake. They are in paragraph 2. – R Sahu Sep 26 '16 at 05:56
  • Do you know any good interpretation of the rules from section 6.3.1.3? It is not clear to me what they mean exactly. – Igor Liferenko Sep 26 '16 at 06:12
  • @IgorLiferenkol, searching for "Integer conversions in C" turns up interesting results. I am not sure whether any of them has any in-depth coverage. – R Sahu Sep 26 '16 at 06:20
1

Seems like a flaw in gcc's Wconversion warning option.

If enabled, this warning option warns for assignments:

int i = c; //where c is of type char

and passing variables to functions:

f(i); //where i is of type int

but does not warn for passing integer literals:

f(-1); //where -1 is of type int

According to the C standard the last example should also produce a warning.

Gcc is smart enough to recognize the integer literal fits into a char type, and doesn't warn, whereas if a value that doesn't fit is used, it does warn.

This is reasonable behavior, although a pedantic user would except a warning in the last example that should be silenced by a cast to type char.

Gcc actually includes a warning option to warn when a sign of an integer type is changed through implicit conversion. Use: -Wsign-conversion, and you will get a warning for the second example.

2501
  • 25,460
  • 4
  • 47
  • 87
  • Where the bug should be reported? – Igor Liferenko Sep 26 '16 at 06:42
  • @IgorLiferenko I'm confidant to say that gcc developers will not consider this a bug. It is reasonable behavior, i.e. more users would be inconvenienced by the change, than not. – 2501 Sep 26 '16 at 06:43
  • How `-1` fits into `unsigned char` type (which ranges from 0 to 255)? – Igor Liferenko Sep 26 '16 at 06:46
  • To your remark about "gcc being smart": does gcc reference mention this feature? – Igor Liferenko Sep 26 '16 at 06:49
  • I tried to use `-Wall` in the beginning, but no warning was given. Isn't `-Wall` supposed to enable all warnings? – Igor Liferenko Sep 26 '16 at 06:52
  • @IgorLiferenko No. – 2501 Sep 26 '16 at 06:52
  • I get the warning only in the second case, but not in the first. Why? – Igor Liferenko Sep 26 '16 at 06:58
  • @IgorLiferenko Yes, you're right, there is no sign conversion in the first example. – 2501 Sep 26 '16 at 07:00
  • The question remains: how `-1` fits into `unsigned char` type (which ranges from 0 to 255)? Also, I'm confused: in example 1) `-1` *is* signed. But you say that it is not... – Igor Liferenko Sep 26 '16 at 07:06
  • @IgorLiferenko *-1 is signed. But you say that it is not...* Where did I say that? – 2501 Sep 26 '16 at 07:06
  • By default `char` means `unsigned char`. And as you say that there is no sign conversion in the first example, this means that `-1` must be unsigned... – Igor Liferenko Sep 26 '16 at 07:08
  • *how -1 fits into unsigned char type* By using implicit conversions. Assuming you read the other answer it should have been clear. – 2501 Sep 26 '16 at 07:08
  • @IgorLiferenko Type char is not unsigned by default. It can be signed or unsigned, and it is signed on your machine. Therefore no sign conversion. – 2501 Sep 26 '16 at 07:09
  • Please give a link to any example which explains "implicit conversions". And in which standard it is stated that `char` is `signed` by default? – Igor Liferenko Sep 26 '16 at 07:13
  • @IgorLiferenko I never said char is signed by default. – 2501 Sep 26 '16 at 07:16
  • "... and it is signed on your machine". Where are the default values of "char" stated? – Igor Liferenko Sep 26 '16 at 08:17
  • @IgorLiferenko *char*, *signed char* and *unsigned char* are actually 3 distinct types in C, where the signedness of just *char* is implementation defined. – hyde Sep 26 '16 at 08:35
  • @IgorLiferenko To get more warnings, use *-Wall -Wextra* for both *gcc* and *clang*. IMNSHO that is a reasonable default warning level for any new code, or code which you can fix to produce no warnings with these options. – hyde Sep 26 '16 at 08:38
  • @hyde If `char` is supposed to store character values, how can it possibly be signed by default? Do you know about any systems which make `char` signed by default and what difference it makes in handling characters and strings in comparison with systems where `char` is unsigned by default? – Igor Liferenko Sep 26 '16 at 08:45
  • @hyde Is there a test program which will show if on my machine `char` is signed of unsigned by default? – Igor Liferenko Sep 26 '16 at 08:47
  • @hyde Now I understand: `char` is signed by default on my machine, but in character and string handling functions it is treated as unsigned: `char a=-1; printf("%c %d\n",a,a);` – Igor Liferenko Sep 26 '16 at 09:42
  • @IgorLiferenko Note that *printf* is not a very good example, because it is variable argument list function, so *char* will be converted to and passed as *int* anyway... – hyde Sep 26 '16 at 13:08
  • @IgorLiferenko as about signedness of *char*, just think of 8 bit character codes in HEX, and problem disappears (both -1 and 255 are 0xFF as 8 bit HEX number on any current CPU). – hyde Sep 26 '16 at 13:35
  • @hyde you say that `255` and `-1` are the same. But `EOF` is equal to `-1`. This is a contradiction, because the value of `EOF` must not coincide with any valid 8-bit character. I'm totally stymied now... – Igor Liferenko Sep 27 '16 at 05:31
  • @IgorLiferenko 255 and -1 are same for *8 bit char codes*. Functions which return `EOF` to indicate error/EOF return `int`, and I think (would have to check to be 100% sure) C standard requires that `int` is wider than `char`. – hyde Sep 27 '16 at 08:19
  • @hyde I would ask if you two could please use chat: https://chat.stackoverflow.com/ Thanks. – 2501 Sep 27 '16 at 08:20
  • @IgorLiferenko The update is a completely different question that must be asked here: https://stackoverflow.com/questions/ask Please do not edit the current question to change it's meaning as this will break the Q&A format and make the answers irrelevant. – 2501 Sep 27 '16 at 08:35
  • I asked the question here https://stackoverflow.com/questions/39720004/why-eof-coincides-with-valid-char-value – Igor Liferenko Sep 27 '16 at 08:42