【MFC】警告 C4996 ‘strcpy’ エラーの解決策: This function or variable may be unsafe の付き合い方

古いC/C++コードを Visual Studio でビルドすると、strcpysprintf などに対して C4996 が出ることがあります。/WX(警告をエラーとして扱う)を有効にしているプロジェクトでは、スクリーンショットのように Error List 上でそのまま「エラー」として止まります。

error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

対象になるのは strcpysprintffopen などです。Visual Studio では通常は warning C4996 として出ますが、/WX を有効にしているプロジェクトでは error C4996 として扱われ、ビルドが止まります。

この記事では、C4996 が出る理由 と、書き換えて直す方法抑制して進める方法 の使い分けを整理します。


目次

なぜ strcpy は「危険 (unsafe)」なのか?

Visual Studio のエラー一覧で strcpy に対する C4996 がエラーとして表示されている画面

昔ながらの関数(特に文字列操作系)には、「バッファの最大サイズを受け取る引数がない」 という設計上の弱点があります。

char dest[10];
strcpy(dest, "This string is much larger than 10 bytes!"); // バッファサイズを超える書き込み

関数は dest の大きさがたった10バイトであることを知らないため、延々とメモリ領域を上書きし続け、隣接する大切なデータやプログラム状態を破壊してしまいます(バッファオーバーラン脆弱性)。
Microsoftはこれを深刻なセキュリティホールと重く見て、Visual Studio 2005以降、これらの旧関数に軒並み「非推奨 (C4996)」のマークを付けました。

これを解決するためのアプローチは大きく2つあります。


アプローチA: 安全な代替関数(_s系への書き換え)

Microsoftが推奨している「正しい」解決策です。This function or variable may be unsafe. Consider using strcpy_s instead. と言われている通り、関数名の末尾に _s (Secure) が付いた代替関数に置き換えます。

1. 新しい関数の書き方

_s 系の関数は、引数に「バッファの最大サイズ」を渡す仕様になっています。これにより、バッファオーバーランが発生する前にエラーとして安全に弾くことができます。

【strcpy の場合】

// ❌ 旧(危険)
char dest[10];
strcpy(dest, src);

// ⭕ 新(安全)
char dest[10];
// 第2引数にバッファサイズ(要素数)を追加
strcpy_s(dest, _countof(dest), src); 

【sprintf の場合】

// ❌ 旧(危険)
char buf[100];
sprintf(buf, "Value = %d", val);

// ⭕ 新(安全)
char buf[100];
sprintf_s(buf, _countof(buf), "Value = %d", val);

【fopen の場合】

// ❌ 旧(危険)
FILE* fp = fopen("test.txt", "w");

// ⭕ 新(安全)
FILE* fp;
errno_t err = fopen_s(&fp, "test.txt", "w"); // ポインタのポインタを渡す仕様変更に注意
if (err != 0) { /* エラー処理 */ }

2. MFCなら CString を使おう!

MFC プロジェクトでは、C言語ライクな char 配列操作を減らし、CString を使う方が管理しやすくなります。C4996 の対象になる関数を避けやすくなり、文字列操作も整理しやすくなります。

// ⭕ MFCの正しい姿
CString strDest;
strDest.Format(_T("Value = %d"), val); // 自動拡張されるので安全!

アプローチB: 警告を抑制する

ただし、既存コードの量が多い場合や、サードパーティ製ライブラリの内部で C4996 が出ている場合は、すぐにすべてを書き換えられないこともあります。

その場合は、警告を抑制する設定を入れてビルドを先に通します。メッセージにも To disable deprecation, use _CRT_SECURE_NO_WARNINGS. と書かれている通り、_CRT_SECURE_NO_WARNINGS を定義すると C4996 は抑制できます。

方法1: プロジェクト全体で一括抑制する(推奨)

プロジェクトプロパティの「プリプロセッサの定義」に _CRT_SECURE_NO_WARNINGS を追加している画面
  1. ソリューションエクスプローラーでプロジェクトを右クリックして「プロパティ」を開きます。
  2. 構成プロパティ > C/C++プリプロセッサ を開きます。
  3. 「プリプロセッサの定義」に以下の一文を追加します(既存の文字に ; をつけて追記)。
    _CRT_SECURE_NO_WARNINGS
  4. 「適用」→「OK」を押してリビルドします。

これで、プロジェクト全体のC4996警告が消えます。

方法2: 特定のファイルだけピンポイントで抑制する

もし「自分が書くコードは _s を使って綺麗に保ちたいが、外部から持ってきたこの old_lib.cpp だけは警告を消したい」という場合は、該当ファイルの先頭(#include より上)に以下のマクロを定義します。

// stdafx.h / pch.h より上に書くか、ファイルの先頭に書く
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable: 4996)

#include "stdafx.h"
#include <string.h>
// ...

#pragma warning(disable: 4996) は、特定のファイルやブロック内だけで 4996 を無効化する方法です。


まとめ:どう対応するべきか?

  • [ ] 新規で書くコードでは、strcpy_sCString を使います。
  • [ ] MFCプロジェクトでは、可能な範囲で C言語関数から離れ、CString のメソッド群(Format, Left, Right等)へ置き換えます
  • [ ] 巨大なレガシーシステムの移行中や、外部ライブラリを組み込む際は、_CRT_SECURE_NO_WARNINGS をプリプロセッサに定義して一時的に抑制します。

C4996 は、古い C 言語系関数の扱いを見直すきっかけになります。新規コードでは安全な関数や CString へ置き換え、既存コードや外部ライブラリでは必要に応じて抑制を使い分けてください。

目次