C言語のコンパイラ警告 C4937について解説
c言語で発生する警告C4937は、コンパイラがディレクティブへの引数として、単一アンダースコアと二重アンダースコアの表記を区別できない場合に表示されます。
例えば、__cdecl
や__forceinline
のような記述が該当し、OpenMPなど特定のディレクティブを使用する際に注意が必要です。
警告 C4937の仕様と処理の仕組み
コンパイラのディレクティブ引数処理
コンパイラは、ディレクティブへの引数として渡されたテキスト情報を内部的に処理する際、複数の表記方法―具体的には単一アンダースコアと二重アンダースコア―を同一視して扱います。
これにより、意味としては同一であっても、異なる記述形式が混在すると識別ができなくなり、警告 C4937 が発生することがあります。
特に、OpenMPなどのコンパイラディレクティブでは、この仕様が影響を与え、意図しない警告を生む場合があります。
記述形式の違い:単一アンダースコアと二重アンダースコア
C言語やC++の環境では、特殊なキーワードが単一アンダースコアと二重アンダースコアの形式で定義されていることがあります。
たとえば、Microsoftの拡張では __cdecl や __forceinline といった記述が利用されます。
これらの形式は、システムやコンパイラの実装の歴史的背景に起因するものですが、ディレクティブの引数として利用する際には、どちらの形式も同じ意味として解釈され、コンパイラは両者を区別できません。
そのため、混在する記述がある場合に C4937 警告が発生します。
OpenMPディレクティブにおける適用例
以下のサンプルコードは、OpenMPディレクティブ内の引数として、間違った形式(二重アンダースコアの使用)と正しい形式(単一アンダースコアの使用)を記述した例です。
/openmp および /W4 オプションでコンパイルすると、前者の記述で警告 C4937 が発生します。
#include <stdio.h>
#include <omp.h>
int main(void) {
// 警告発生例: 二重アンダースコアを使用すると警告 C4937 が生成される
#pragma omp critical( __leave )
{
printf("【警告発生例】この部分は警告対象です。\n");
}
// 正しい記述例: 単一アンダースコアで記述すれば警告は発生しません
#pragma omp critical(leave)
{
printf("【警告解消例】正しい記述です。\n");
}
return 0;
}
### 警告発生例
### 警告解消例
警告発生条件と検証事例
C4937警告が発生する状況
警告 C4937 は、ディレクティブの引数で単一アンダースコア形式と二重アンダースコア形式の両方が混在して用いられた場合に発生します。
コンパイラはこれらの表記を区別せず、同一の意味として扱うため、冗長な記述が存在することを警告する仕組みとなっています。
たとえば、OpenMPディレクティブ内で使用されるキーワードが対象となります。
コード例に見る不適切な記述方法
以下のサンプルコードは、不適切な記述方法として二重アンダースコア形式を含むため、警告が発生する例です。
#include <stdio.h>
#include <omp.h>
int main(void) {
// 不適切な形式: __startup などの二重アンダースコアを含む記述は警告対象
#pragma omp parallel(__startup)
{
printf("並列実行中 (不適切な記述)。\n");
}
return 0;
}
並列実行中 (不適切な記述)。
正しい記述方法との比較
正しい記述方法としては、ディレクティブの引数で単一アンダースコア形式のみを用いることが挙げられます。
以下のサンプルコードは、正しい形式に修正した例です。
#include <stdio.h>
#include <omp.h>
int main(void) {
// 正しい形式: 単一アンダースコアを用いて記述
#pragma omp parallel(startup)
{
printf("並列実行中 (正しい記述)。\n");
}
return 0;
}
並列実行中 (正しい記述)。
環境別動作の確認
コンパイラの動作は、使用するオプションによっても影響を受けます。
特に、/Za オプションを指定すると、Microsoftの拡張が無効化され、二重アンダースコア形式のみが有効とされるため、記述方法に対する注意が必要です。
/Zaオプション使用時の注意事項
/Za オプションは、コンパイラ拡張を禁止してISO C規格に厳格に準拠するための設定です。
このモードでは、二重アンダースコア形式の名前は許容されないため、ディレクティブやその他の拡張された記述を使用する場合に影響が出ます。
プロジェクトの要件に合わせて、使用するオプションや記述形式を統一することが推奨されます。
対応策と記述改善方法
正しいディレクティブ引数の指定方法
対応策としては、ディレクティブ引数において一貫した記述形式を採用することが重要です。
特に、プログラミング環境が拡張された名前を許容する場合でも、混在形式を避け、シングルアンダースコア形式に統一することで、C4937 警告を回避できます。
具体的な記述例の検証
以下は、正しいディレクティブ引数の指定方法を示すサンプルコードです。
OpenMPディレクティブに統一形式で記述することで、警告を発生させずにコンパイルできる例となっています。
#include <stdio.h>
#include <omp.h>
int main(void) {
// OpenMPディレクティブの引数は統一された形式で指定
#pragma omp parallel(single_thread)
{
printf("【統一形式】警告なしで実行可能です。\n");
}
return 0;
}
### 統一形式
コンパイラオプション選定のポイント
すべてのコードベースで統一した記述形式を採用する以外に、コンパイラオプションの調整も選択肢の一つです。
たとえば、/openmp オプションの利用に加えて、言語拡張の許容設定や /Za オプションの利用状況を確認し、適切な設定をプロジェクト全体で統一することが、無用な警告を避ける上で有効です。
他の回避手段の検討と留意点
場合によっては、プロジェクトのニーズに応じて警告レベルの調整や特定の警告を無効化する対策も考えられます。
しかし、これらの方法は根本的な原因の解決にはならないため、できるだけ正しい形式に修正することが推奨されます。
また、コンパイラごとの仕様に依存するため、複数の開発環境で動作確認を行うことが重要です。
まとめ
本記事では、コンパイラ警告 C4937 の仕組みと、その原因となるディレクティブ引数の記述方法の違い(単一アンダースコアと二重アンダースコア)を解説しました。
OpenMPの例を用い、不適切な形式と正しい形式の比較や、/Zaオプション使用時の注意点を示しました。
統一された記述方法と適切なコンパイラオプションの選定で警告回避が可能となります。