このサイトでは、分析、カスタマイズされたコンテンツ、および広告に Cookie を使用します。このサイトを引き続き閲覧すると、Cookie の使用に同意するものと見なされます。
Hi, Developers,
straightapps.com
作成 June 12, 2020
トップページ > Windows トップ > _sprintf でリンクエラーを回避する
line
Windows 開発
line

ここでは、_sprintf でのリンクエラーを回避する方法について、書いています。

Visual C++ の古いプロジェクト、具体的には Visual C++ 2013 より前のプロジェクトで、 キャラクタバッファにフォーマット文字列を設定する sprintf 関数を利用していると、 Visual C++ 2015 以降 ( 実際には VC++ 2013 から VC++ 2015 しか試していませんが、こう言って構わないでしょう。 ) ではリンクエラーになってしまいます。

マイクロソフト公式ドキュメントにも書かれているように、 sprintf 関数では書き込み文字数を制限する方法がありませんので、 バッファオーバーランが起きる可能性があります。 バッファに書き込む最大文字数を指定できる _snprintf 関数や、 どれだけバッファがあれば足りるかを調べる _scprintf 関数の使用が推奨されています。

バッファオーバーランとは、例えば 16 バイトしか用意していないバッファに 20 文字(バイト)書き込みしてしまうと、 4バイト、 終端文字 NULL ( 文字列の終端を 0 で示すものです。16 バイトのバッファに 16 バイトの文字列を入れると、終端1バイト、値 0 があふれます。 ) を含めれば5バイト、隣接メモリを破壊します。 隣接メモリが何を表しているかはわかりませんが、 変数であった場合は値が壊れますし、関数の戻り先アドレスであればクラッシュします。 逆に、隣接メモリが数値を表す変数であり、0 がなくなれば、文字列が壊れます。 悪用もできるので、大変良くないものです。

ここでは、どのように回避すればよいか、検討しています。

▼ セクション一覧

D8016 コマンドライン オプションエラーについて
発生するリンクエラー LNK2019 / LNK1120

なお、本サイトのご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。

D8016 コマンドライン オプションエラーについて

投稿 June 12, 2020

ここでは、VC++ 2013 のプロジェクトを VC++ 2015 で開いてビルドするときのエラーについて書いていますが、 本題のリンクエラーが出る前に、

D8016 コマンドライン オプション '/ZI' と '/Gy-' は同時に指定できません

が出て、リンクに至りませんでしたので、先に触れておきます。

プロジェクトのプロパティを見ると、これらのコマンドライン オプションの意味がわかります。 「プロジェクト」メニューの一番下、 「<プロジェクト名> のプロパティ」を選ぶと表示されます。

/ZI は、左側 C/C++全般 で指定しています。 「デバッグ情報の形式」の「エディット コンティニュのプログラム データベース」のが選ばれている場合、 /ZI が設定されています。 ここに <別のオプション> と表示されている場合は、ダイアログ左上の「構成」が「すべての構成」になっているなど、 複数選択されている構成で異なるオプションが指定されていますので、「Debug」に変更するなどして、値を確認します。 これを「プログラム データベース」に変更すると、ビルドが通るようになります。 ただし、試していませんが、 エディット コンティニュ ( 例えばブレークポイントを指定して動作を止め、コードを編集して再開すると、そのコードだけビルド・リンクされ、実行時に動作を変更できる、という機能です。 ) はできなくなると考えられます。

/Gy- は、左側 C/C++コード生成 で指定しています。 「関数レベルでリンクする」の「いいえ」の設定が、/Gy- です。 「はい」にすると /Gy となり、この設定は回避できます。

同時に指定できないだけですので、両方変える必要はありません。 エディットコンティニュの機能を使用しないなら、最初の設定を変えるだけで、ビルドが通るようになります。

▲ページ先頭へ

発生するリンクエラー LNK2019 / LNK1120

投稿 June 12, 2020

ビルドが通るようになっても、_sprintf などの関数を使用していれば、エラーになります。

1>ライブラリ名.lib(モジュール名.obj) : error LNK2019: 未解決の外部シンボル _sprintf が関数 ..1.4_1.except で参照されました。
1>.\Debug\プロジェクト名.exe : fatal error LNK1120: 1 件の未解決の外部参照

自分のコード内にあるのであれば、 _snprintf 関数 (第2引数でコピーする最大文字数をを指定)や、 _snprintf_s 関数 (さらにバッファサイズも指定)に置き換えれば、安全になります。

唯一気をつけるべきことは、旧コードで実はバッファオーバーランして動作していた場合、 安全な関数に置き換えれば、文字列の末尾が切れるなど、従来と同じにはならない可能性です。 ただ、そんなことが発生するようなコードなら、いずれにせよ修正しないと危険ですが。

大量に使用されている場合でも、頑張って置き換えれば、それで OK です。

しかし、_sprintf 関数がライブラリで使用されている場合、特にそれがソースコードのない、外部のライブラリの場合、置き換えようがありません。 が、ライブラリごと別のものに変更するのも大変です。

こういう場合、次のようにすれば回避可能です。

プロジェクトのプロパティ

図のように、プロジェクトのプロパティを開き、 左側「リンカー」の「入力」を選択します。 右側の「追加の依存ファイル」には何も書かれていないと思いますので、 空白部分をクリックし、legacy_stdio_definitions.lib と記入すると、 古い呼び出しが有効となり、ビルドが通るようになります(図では Debug 版だけですが、Release 版でも同様です)。

もちろん、これは一時しのぎであり、 少なくともこれ以降の自分のコードでは、より安全な関数を使用すべきです。

とはいえ、どうにもならないときには、こういう方法が用意されていると嬉しいですね。

▲ページ先頭へ
line
関連トピックス
line

Visual Studio Community 2019 を共存インストールする(デスクトップ)

VS 2019 をメイン デスクトップ機に共存インストールした様子を、書いています。 システムドライブの容量を節約し、データドライブにインストールされるようにしています。

line
その他のおすすめ
line

Windows 開発トップ

Windows 開発関連の情報を、書いています。



© 2017-2020 StraightApps.com 無断転載を禁じます。No reproduction without permission.