3

Does anybody know Why argument type of putchar(), fputc() and putc() is not char, but argument type of putwchar(), fputwc() and putwc() is wchar_t? See also this and this.

Community
  • 1
  • 1
Igor Liferenko
  • 1,499
  • 1
  • 13
  • 28
  • As your second link says its because `wchar_t` has to be able to store meta values such as EOF, but `char` does not. So the ones that would take `char` need to take `int` instead so that meta values can be transmitted. This is fairly common in c. – LambdaBeta Nov 21 '16 at 03:58
  • @LambdaBeta the first sentence of the second link flatly contradicts the rest of the answer and is factually wrong. – n. m. could be an AI Nov 21 '16 at 04:57
  • @LambdaBeta why on earth **wint_t** was introduced then? – Igor Liferenko Nov 21 '16 at 05:11
  • @IgorLiferenko: `wint_t` was introduced because `wchar_t` might be a type subject to 'default promotion' rules when passed to `printf()` et al. The standard notes that `wint_t` might be the same type as `wchar_t`, but if `wchar_t` is a (16-bit) `short`, `wint_t` might be (32-bit) `int`. – Jonathan Leffler Nov 21 '16 at 05:14
  • @JonathanLeffler "`wchar_t` might be a type subject to 'default promotion' rules when passed to `printf()` et al" - please add this with an example as the answer to http://stackoverflow.com/questions/40601645/how-to-change-wchar-h-to-make-wchar-t-the-same-type-as-wint-t so that I could upvote – Igor Liferenko Nov 21 '16 at 05:18
  • I've added 'default promotion' rules to the answer — quoting the standard. – Jonathan Leffler Nov 21 '16 at 05:46

2 Answers2

7

The answer is 'legacy' (or 'history'). Before the C90 standard, there were no function prototypes and all arguments to all functions were subject to default promotion rules, so a char was automatically passed as an int (short was promoted to int too, and float to double, and similarly for unsigned types). The standard couldn't afford to break existing code, so it kept that type for these functions. It makes very little difference in practice. The value you pass will be treated as a character type even if you pass a value that's out of range. The specification of fputc(int c, FILE *stream) says:

The fputc function writes the character specified by c (converted to an unsigned char) to the output stream pointed to by stream

Default promotion rules

§6.5.2.2 Function calls

¶6 If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.

¶7 … The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

Integer promotions are defined in §6.3.1

¶2 The following may be used in an expression wherever an int or unsigned int may be used:

  • An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.
  • A bit-field of type _Bool, int, signed int, or unsigned int.

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.58) All other types are unchanged by the integer promotions.

¶3 The integer promotions preserve value including sign. As discussed earlier, whether a 'plain' char is treated as signed is implementation-defined.

58) The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses.

The integer ranks are defined in ¶1 of the section in 10 bullet points.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • @chux I checked and indeed, `putchar(EOF);` outputs `0xff` – Igor Liferenko Nov 21 '16 at 05:25
  • @IgorLiferenko: Yes, but checking what it does on your system doesn't really prove anything. The standard says that `putchar(c)` is equivalent to `putc(c, stdout)`, which in turn is very nearly equivalent to `fputc(c, stdout)`. `fputc` is specified to convert its first argument to `unsigned char`. `EOF` is typically `-1` (though that's not guaranteed), and converting that value to `unsigned char` yields `UCHAR_MAX`, which is typicaly `0xff`. In any case, there's not much point in passing `EOF` to `putchar()`. – Keith Thompson Nov 22 '16 at 01:41
  • @KeithThompson It was to illustrate the comment which was removed, that that the argument to `fputc()` is converted to `unsigned char` no matter if `int` or `signed char` variable is passed to `fputc()`. And I used `EOF` in the role of a negative number, because it is known to be `-1` on my system. – Igor Liferenko Nov 22 '16 at 02:53
0

I think that Jonathan's answer is too simple. Things are slightly more rational. I think that none of the library functions which handle single characters work with char (only with int), because even though some of them do not use EOF, we cannot make its type char without getting type conversion warnings like

void f(char c) { ...
...
char x = 't';
f((unsigned char)x);
...

warning: conversion to ‘char’ from ‘unsigned char’ may change the sign of the result

(because people usually typecast to unsigned char to ensure portability of the code, considering the fact that the signedness of char is undefined.) So the only option was to make it int.

Igor Liferenko
  • 1,499
  • 1
  • 13
  • 28