31683


本家の質問で観たコードで、  
`stdout = fopen("out.txt", "w");`  
の様に書いた場合の挙動について質問されていました。  
コードの目的としてはstdoutを利用しているであろうprintfのような関数の出力をコンソールではなくてファイルに出力するというものです。（そういう風に理解しています）  
（※本家の質問自体をもう一度ここで質問しているのではないことに注意してください）  
このような場合`freopen`を利用するべきというのは知っていましたが、  
（つまり`freopen`を使用してのという意味での変更は可能）  
動作を再現してみようと思ってgccでコンパイルを試みましたが、

> error: lvalue required as left operand of assignment

のようにエラーになりコンパイル自体ができませんでした。  
stdio.hの中身を観てみるとマクロで`&ファイル構造体`のようになっていて、  
なるほどコンパイラの言うとおりだと思ったのですが、  
本家の質問者はコンパイルして実行しているわけで

## stdout は変更可能なのかどうなのか？

という疑問が湧きました。  
自分なりに規格を調べて見た結果  
freopenの説明の一部に

> 272) The primary use of the freopen function is to change the file  
>  associated with a standard text stream (stderr, stdin, or stdout), as  
>  those identifiers need not be modifiable lvalues to which the value  
>  returned by the fopen function may be assigned.

「これらの識別子(つまりstdin,stdout,stderr)は変更可能でなくてもよい。」  
とあって、  
私の解釈としては、  
**stdoutは(実装上)変更できてもよい(つまり代入可能)し、できなくてもよい。**

でも変更不能の場合がある以上  
プログラムとして  
`stdout = fopen("out.txt", "w");`  
のようなコードは書くべきでは無い  
ということです。

私の解釈・意見はともかく、本家の質問者の場合は実際に変更できているわけで、  
変更出来る実装だった場合、

このような使用は有効ですか？それとも無効ですか？  
実際に変更できようができまいが、このような使用を試みること自体が無効？  
（つまり実質的にこのような代入による変更は不能 ※私の意見はこれ）  
規格上このような使用は動作未定義だとされていれば無効だとはっきりしますし、  
あるいは実装定義であるということなら状況により使用可能（有効）だということになるかと思いますが、  
よくわからないので質問しました。


CC BY-SA 3.0
2017-01-06T12:59:34.963
31670
2017-01-07T07:58:54.447
2017-01-06T13:09:58.643
5044
5044
question
c
stdoutは変更可能か？


`dup2()`を使ってファイルディスクリプタに、別のファイルディスクリプタを割り付けるというのはどうでしょうか？

いちおうWindowsとLinuxで動きましたが、言語やOS的に許されてるのかまでは、分かりません。

[code]

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
        FILE *fp = fopen("hoge.txt", "w");
        dup2(fileno(fp), fileno(stdout));
    
        printf("Hello, World\n");
    
        fclose(fp);
        return 0;
    }
    
[/code]


CC BY-SA 3.0
2017-01-06T15:03:53.100
31673
2017-01-06T15:03:53.100
3337
31670
answer


GNU C Library ではそれらの標準入出力に関わる変数を変更することを許容しています。

<https://www.gnu.org/software/libc/manual/html_node/Standard-Streams.html>

> In the GNU C Library, stdin, stdout, and stderr are normal variables  
>  which you can set just like any others. For example, to redirect the  
>  standard output to a file, you could do:
[code]

>     fclose (stdout);
>     stdout = fopen ("standard-output-file", "w");
>  
[/code]

>
> Note however, that in other systems stdin, stdout, and stderr are  
>  macros that you cannot assign to in the normal way. But you can use  
>  freopen to get the effect of closing one and reopening it.

ただし、注意書きされているように他のシステムでは有効でない可能性があるため、移植性の高いコードを書きたい場合はおススメできません。（代わりに`freopen`を使うように、とも書かれています。）

参考までに[musl libc](http://www.musl-
libc.org/)のstdio.hではこれらの変数に`const`が追加されて再代入できないようになっています。

[code]

    extern FILE *const stdin;
    extern FILE *const stdout;
    extern FILE *const stderr;
    
[/code]

* * *

追記

[STDIN のマニュアル](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/stdin.3.html)
によると「シンボル stdin stdout stderr はすべてマクロとして定義されているので、これらへの代入
は移植性を保証されない。」と書かれています。


CC BY-SA 3.0
2017-01-06T15:07:08.683
31674
2017-01-07T07:58:54.447
2017-01-07T07:58:54.447
2391
2391
31670
answer


いやもう質問自体の中に答えが書いてありますが、あえて蛇足するなら

JIS X3010:2003 7.19 `<stdio.h>`  
7.19.1 概説  
`stderr` `stdin` `stdout` は "`FILE` へのポインタ" をもつ式とする  
※オイラの注釈：「式」としか書かれていない。すなわち「変更可能」であるとは明記されていない

7.19.5.4 `freopen` の注釈 (228)  
(228) これらのストリームを表す識別子は snip 変更可能な左辺値とは限らない

なので、言語仕様書上の解釈は  
\- 「できる」と明言されていないことは「できない」と解釈する必要があることから、  
`stdout` 等を変更しようとする試みは未定義の動作である  
\- 未定義の動作の中には「プログラマが勝手に期待している動作」が含まれる

`stdout` に代入できない処理系では、コンパイルエラーになる  
`stdout` に代入できる処理系であって、代入を行った結果  
\- `stdout` のリダイレクトを行ったかの如く振る舞う (プログラマが勝手に期待した動作と同じ動作)  
\- 何も起こらない (リダイレクトされないまま正常動作を継続)  
\- おかしな動作をする (`stdout` に出力したらエラー発生とか)  
のどれが起きても「言語仕様書上は」問題ないと読む必要がありそうです。


CC BY-SA 3.0
2017-01-06T23:04:03.687
31683
2017-01-06T23:04:03.687
8589
31670
answer