Thanks to those who answered, and now having read the relevant parts of the C99 standard, I came to an agreement with the somewhat surprising conclusion that storing an arbitrary value other than the EOF returned by fgetc() as a char type without loss of fidelity is not guaranteed. This is largely due to the fact that char cannot represent as many different values ββas unsigned char .
For its part, stdio functions ensure that if data is written to a (binary) stream and then read back, then writeback data will be compared with the original data. This, as it turned out, has much narrower consequences than I thought at first, but this means that fputs() should output a separate value for each selected char that it successfully displays, and that any fgets() conversion is used to store input bytes how char should convert the conversion exactly, if any, with which fputs() will return the input byte as its output. However, as far as I can tell, fputs() and fgets() allowed to crash on any input that they don't like, so there is no certainty that fputs () maps all possible char values ββto unsigned char .
In addition, although fputs() and fgets() work as if sequences of calls to fputc() and fgetc() were executed, respectively, it is not indicated what conversions they can perform between char values ββin memory and base unsigned char values ββin the stream. If the fputs() platform uses a standard integer transform for this purpose, then the correct inverse transform will be as I suggested:
int c = fgetc(stream); char buf; if (c >= 0) buf = (char) ((c > CHAR_MAX) ? (c - (UCHAR_MAX + 1)) : c);
This arises directly from the integer conversion rules, which indicate that integer values ββare converted to unsigned types by adding or subtracting an integer multiple type <target type> _MAX + 1, necessary to bring the result into the range of the target type, supported by restrictions on the representation of integer types. Its correctness for this purpose does not depend on the concrete representation of char values ββor on whether char treated as signed or unsigned.
However, if char cannot represent as many different values ββas unsigned char , or if there are char values ββthat fgets() refuses to print (for example, negative), then possible values ββof c possible, which could not be the result of converting char in the first place. The inverse conversion argument is not applicable to such bytes, and perhaps the meaning of the char corresponding to them does not even make sense. In any case, whether the given transformation is the correct inverse for the data written by fputs() seems to be an implementation. It is definitely determined by the implementation of whether buf = (char) c have the same effect, although it has so many systems.
In general, I am only struck by how many details of the C I / O behavior are implemented. This was a revelation for me.