なんか人気のfopen_s関数について

2017年4月13日 オフ 投稿者: khws4v1

前に書いた記事のPV伸びすぎなのと、最近fopen_s関数の存在を思い出したので改めてfopen_s関数について記事を書きます。
ちなみに前のfopen_s関数の記事は大学(.ac.jpドメイン)からのアクセスが結構あるので、fopen関数の代わりにfopen_s関数を教えている大学はそれなりに存在することが推測できます。

fopen_s関数について

fopen_s関数はC11で標準Cライブラリに取り入れられた関数です。
基本的にはfopen関数と同じで、ファイルを開くという役割も変わっていません。
ただし、戻り値と引数は変わっているので単純に置換することはできません。

実はfopen_s関数自体はC11以前からMicrosoft Visual C++(MSVC)の独自拡張で存在していました。
MSVCの独自拡張が標準化されたという感じです。

C11で標準化され、MSVC以外でも使えそうなfopen_s関数ですが、fopen_s関数の実装は任意です。
処理系によってはfopen_s関数は存在しません
例えばglibcはfopens_s関数を実装していません。

fopen_s関数の使い方

まずは引数と戻り値から。

fopen_s関数はファイルポインタのアドレスを引数に取り、戻り値がerrno_tになっています。
ファイルオープンに成功した場合の戻り値は0です。
ファイルオープンに失敗すればfopen関数同様にファイルポインタは空ポインタ(NULL)になり、errnoが戻り値になります。

fopen_s関数もそうですが、C11から追加された末尾に_sが付いている関数の実装は任意で、実装されている処理系では__STDC_LIB_EXT1__マクロが定義されます。
使うには該当する関数のヘッダファイルをincludeする前に__STDC_WANT_LIB_EXT1__マクロを1にする必要があります。

これはすでに_s系の関数があるMSVCで関数名が衝突しないようにするためだそうです。
__STDC_WANT_LIB_EXT1__マクロは0だと_s系の関数が宣言・定義されません。

fopen_sとfopenの違い

引数と戻り値以外にも違いがあります。
しばしば「セキュリティが強化されている」とだけ説明されますが、あまりにも抽象的すぎる説明です。

戻り値でエラーの種類が分かるからセキュリティが〜という説明も見るんですけど多分違います。
fopen関数でもerrnoで取得できますからね。

fopen_sとfopenの最大の違いはファイルを排他モードで開くかどうかです。
例えば以下の2つのコードはfopen_sとfopenの違いしかありませんが、異なる結果になります。

動作結果
http://rextester.com/JMGR52902
http://rextester.com/KOZ39339

C11のfopen_sはファイルモードのwの前にuを加えると共有モードでファイルを開けますが、MSVCの実装はC11に準拠していないので使えません。
まあxも使えないんですけど。

fopen_s関数が使われる理由

ここでとても単純なソースコードを載せます。

ファイルを開いて、閉じるだけで何もしません。

これをVisual Studio 2015とそれに付いているVisual C++でコンパイルします。
gccやclangだとコンパイルが通るにも関わらず、コンパイルに失敗します。

つまり、fopen_s関数が使われる理由はVisual Studioでエラーになるからです。

ちなみに直接clコマンドを使ってコンパイルするとエラーにはなりません。

Visual Studioでfopen_s関数を使わなくてもいい方法

_CRT_SECURE_NO_WARNINGSマクロを毎回定義する

エラーメッセージにあるように_CRT_SECURE_NO_WARNINGSマクロを定義するとC4996が無効化されます。

SDLチェックを無効化する

MSDNを参照してください。

/sdl (追加のセキュリティ チェックの有効化)
https://msdn.microsoft.com/ja-jp/library/jj161081.aspx#Anchor_4

SDLチェックは規定で無効になっているそうですが、Visual Studioでプロジェクトを作ると有効にされています。

SDLチェックを無効化すると警告C4996がエラーにならなくなります。

プリプロセッサの設定を変更する

  1. メニューの「プロジェクト(P)」→プロパティ(P)でプロジェクトの設定を開く
  2. 構成プロパティ→C/C++→プリプロセッサ
  3. プリプロセッサの定義に「_CRT_SECURE_NO_WARNINGS」を追加する
    セミコロン区切りなので注意

これで勝手にマクロが定義されます。

個人的な意見

インターネットではfopenを使うべきという人もいれば、fopen_sを使うべきという人もいます。
書籍だとほぼfopenの方を使っているのに、入門者によく勧められるVisual Studioだとfopenはエラーになります。
入門者なら間違いなく混乱するでしょう。

その混乱ぶりはYahoo!知恵袋を見れば明らかです。
https://chiebukuro.yahoo.co.jp/search/?p=fopen_s&flg=3&class=1&ei=UTF-8&fr=common-navi&dnum=2078297622

適当に「fopen_sの方が正しい」と教えるとgccやclangなどでコンパイルするときにエラーになって余計に混乱します。
絶対にやめてください
現に僕の周りの人間が混乱してます。
C11でもfopen_sはoptionalだからな。

あと、ろくに調べもせず安易な理由でにVisual StudioでC言語を教えないでください。
fopen関数使っただけでエラーになる訳あり統合開発環境であるということを理解して使ってください。
本当に頼みますよ