9

I am trying to print out a Ș by passing in its corresponding decimal value into printf. The output is nothing at all. Why doesn't my code work?

#include <stdio.h>
int main()
{
    printf("%lc",536);
    return 0;
}
Mike
  • 961
  • 6
  • 19
  • 43
  • I can't tell if this is a dup or not, but [you may need to set the locale on Linux](http://stackoverflow.com/a/10007521/1270789). – Ken Y-N Jan 22 '17 at 23:20
  • 1
    also, try an fflush(NULL) after the printf. may or may not change anything, but at least you'll be sure it's being printf'ed. – John Forkosh Jan 23 '17 at 02:15
  • 2
    Check return value. `int cnt = printf("%lc", ( wint_t ) 536); printf("%d\n", cnt);` Was it 1 or a negative value (error)? An error indicates `printf()` can not handle that character. – chux - Reinstate Monica Jan 23 '17 at 05:20
  • 1
    It depends on your OS, compiler and environment. Calling `setlocale("", LC_ALL)` at the beginning of your program is sort of mamdatory if you want any kind of Unicode support from the C library. It may or may not be enough. – n. m. could be an AI Jan 23 '17 at 07:21
  • 2
    I guess Unicode support from the C library is a bit of a stretch. In some cases you get away with simply emitting UTF-8 without caring about anything and let the terminal sort it out, but usually the best way is to be aware of the OS and whatever is between your program and the screen and deal with it accordingly by talking to the OS instead of C. – Joey Jan 23 '17 at 09:29
  • What operating system, compiler and terminal do you use? – Jens Jan 23 '17 at 09:31
  • @Joey The C standard provides `wchar_t` and associated library functions. Whether or not they are adequate for working with Unicode data varies from implementation to implementation, but the common ones all more or less work, provided you are willing to jump through one or more hoops. – n. m. could be an AI Jan 23 '17 at 15:36

3 Answers3

7

On macOS Sierra 10.12.2 with GCC 6.3.0, if I run this program (compiled from mb37.c into mb37):

#include <locale.h>
#include <stdio.h>
#include <wchar.h>      /* wint_t */

int main(void)
{
    setlocale(LC_ALL, "");
    printf("%lc\n", (wint_t)536);
    return 0;
}

the output is:

$ ./mb37
Ș
$

That, I believe, is the desired output. If the setlocale() line is removed, then no output at all is produced — not even a newline. The locale used is en_US.UTF-8; my terminal handles UTF-8 too. The locale name is found by capturing and printing the return value from setlocale() — a regular string.

The wint_t cast is semi-optional; it so happens that a 64-bit compilation without the cast or the <wchar.h> header also produces the same output, but there is a mild coincidence that wint_t is the same as int. That takes some tracking; wint_t is defined as __darwin_wint_t which is defined as __darwin_ct_rune_t which is defined as int. To be portably correct, the cast is necessary. On some systems, it may not be necessary (and macOS Sierra is one such system).

The newline in the printf() is not 100% necessary, but if it is omitted, the next prompt immediately follows the U+0218 LATIN CAPITAL LETTER S WITH COMMA BELOW. It is better to ensure that the output ends with a newline.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I just found out that *"Unicode code point"* `0x0218` *(hexadecimal)* is the same number as *"XML entity"* `536` *(decimal)*. So if you perfer to print directly Unicode code point, you could just use hexadecimal value like this: `printf("%lc\n", (wint_t)0x0218);` This makes much more sense because afterall we are talking about UTF-8 and Unicode... Nobody talks about XML, so why would anyone use XML decimal number instead of hexadecimal Unicode??? – 71GA Apr 19 '21 at 08:56
  • One more thing. On Linux I used your program, but created a for loop that prints all the decimals from `32` till `0x10FFFF` *(just like you do it)*. I compiled the program 2x where once I used casting `(wchar_t)` and once I did not. I then executed both executables and redirected output to two separate files. I opened two files in `vimdiff` which says they are identical! So what is the point of casting? – 71GA Apr 19 '21 at 12:10
4

The l length specifier applied to a c field descriptor indicates that the corresponding argument is of type wint_t (declared in wchar.h). In your code, the argument is of type int, which might or might not be the same. If it indeed isn't the same then the behavior is undefined. You can obtain a wint_t by casting ...

    printf("%lc", (wint_t) 536);

; that's the safest and most portable way to express a wint_t constant.

Additionally, there is a potential question of character sets here. That's a matter of the environment in which your program runs, not so much the program itself. It is conceivable that your program indeed outputs the character in question, in some encoding, but the terminal in which you're running doesn't know how to handle it, or maybe just doesn't have a glyph for it. You should be able to test for that by redirecting the output to a file, and afterward examining the file's contents (possibly as a binary file).

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • I made all the changes you suggested and outputted it to a file, but the file is empty (0 bytes). When I change the number to 126, it prints a `~` so there isn't anything else funky going on.. – Mike Jan 22 '17 at 23:44
  • @Mike, Perhaps your C implementation is old or buggy. Which are you using? – John Bollinger Jan 23 '17 at 00:05
-1

There is no requirement in C that a line without a newline character at the end will be printed. Try "%lc\n".

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • 2
    I think you're mistaken, @gnasher729. If `stdout` is line-buffered (the default) or fully buffered then the output may not appear *immediately*, but it will appear when the file is flushed, which happens when it is closed, which happens when the program calls the `exit()` function or returns from `main()`, as the OP's program indeed does. – John Bollinger Jan 22 '17 at 23:34
  • @JohnBollinger Can you prove that `stdout` is closed when the program exits? I didn't find anything in section 7.19 of the standard, and I remember that unfinished lines don't have to be written out. – Roland Illig Jan 22 '17 at 23:48
  • 1
    @RolandIllig: 7.22.4.4 The `exit` function: ¶4 _Next, all open streams with unwritten buffered data are flushed, all open streams are closed, and all files created by the tmpfile function are removed._ and §5.1.2.2.3 Program termination: ¶1 _If the return type of the `main` function is a type compatible with `int`, a return from the initial call to the `main` function is equivalent to calling the `exit` function with the value returned by the `main` function as its argument;…_. (That's C11 — aka ISO/IEC 9899:2011.) – Jonathan Leffler Jan 22 '17 at 23:59
  • 1
    @RolandIllig, also [7.21.3/5](http://port70.net/~nsz/c/c11/n1570.html#7.21.3p5): "If the main function returns to its original caller, or if the exit function is called, all open files are closed (hence all output streams are flushed) before program termination." So yes, I can prove that the standard requires it (albeit not that the program conforms to the standard in this regard). – John Bollinger Jan 23 '17 at 00:02