8517


setlocale関数とwprintf関数を使用したプログラムが意図した通りに動作しません。

[code]

    printf("Hello\n");
    setlocale(LC_CTYPE, "");
    wprintf(L"今日は\n");
    printf("Goodbye\n");
    
[/code]

このプログラムで「今日は」だけが表示されません。  
1行目のprintfをコメントアウトすると「今日は」だけが表示されます。  
なぜこのような現象が発生するのでしょうか。

環境はDebian7、gccは4.7.2です。


CC BY-SA 3.0
2015-03-28T11:46:50.897
8507
2015-03-31T15:33:52.970
2015-03-29T01:51:52.247
4236
9052
question
linux c
setlocale使用時の挙動について


大雑把に言うと、ひとつのストリーム (今回の例では `stdout`)
に対して、`char`での入出力と`wchar_t`での入出力を混在させることができないからです。

ストリームは、 _byte-oriented_ というモードと _wide-oriented_ というモード のいずれかのモードを持ちます。  
それぞれ、簡単に言うと、バイト (`char`) 単位で入出力をするモードと、ワイド文字 (`wchar_t`) 単位で入出力するモードです。  
最初は未設定状態で、最初に呼ばれた入出力関数に合ったモードに設定されます。  
そして、一度どちらかに決まったら、それと合わない入出力を行うことはできません。片方しか表示されないのはそのためです。  
[コメントで補足があったため編集:ここから]  
~~(合わない入出力関数を使った結果は、無視される、ではなく未定義かも。規格には「shall not be
applied」としか書かれていないような……)~~  
厳密には、合わない入出力の結果は表示されない/入力されないのではなく未定義です。  
[編集:ここまで]

今回の場合、`printf()` がバイト単位の入出力、`wprintf()`がワイド文字単位の入出力を行う関数になります。  
最初に `prnitf("Hello\n")` を呼び出すことによって、`stdout` が _byte-oriented_ になり、後続の
`wsprintf()` が使えなくなります。  
一方、`prnitf("Hello\n")` をコメントアウトすると、 `wprintf(L"今日は\n")`
が最初に呼び出されることになり、`stdout` が _wide-oriented_ になり、`printf()`が使えなくなります。

`fwide()` という関数を使うことによって切り替えられそうですが、手元の man ページでは、一度決定したら変更できないとあります。  
ISO/IEC 9899 にはそのような記述が見当たらないのですが、見落しか glibc の制限か。  
いずれにせよ、現在の Linux の実装上では、`fwide()`で都度切り替えながら出力するというのは無理そうです。  
`freopen()`を使えば未設定の状態に戻るので、これを都度呼ぶという手もありますが、それよりは入出力をバイト単位かワイド文字単位かのどちらかに統一する方針にした方がよいかと思います。


CC BY-SA 3.0
2015-03-28T17:01:58.953
8517
2015-03-31T15:33:52.970
2015-03-31T15:33:52.970
7831
7831
8507
answer