以下は目次です。
何らかのヘッダファイルをインクルードしなくても使える、あらかじめ定義されているマクロを、事前定義マクロといいます。事前定義マクロには以下のものがあります。
__FILE__は、このマクロを使用したソースファイルの名前を表す文字列リテラルに置換されます。
#include <stdio.h>
int main(void)
{
( __FILE__ );
puts}
実行結果:
main.c
__FILE__ によって置換された結果は文字列リテラルですから、puts関数に渡せばそのまま出力できます。
なお、ファイル名を強制的に変更する #line指令という命令があり、その影響を受けます。これは後で説明します。
__LINE__は、このマクロを使用したファイル上の行の番号を表す整数型の定数に置換されます。
#include <stdio.h>
int main(void)
{
( "%d\n", __LINE__ );
printf}
実行結果:
5
なお、行番号を強制的に変更する #line指令という命令があり、その影響を受けます。これは後で説明します。
__STDC__は、処理系が標準規格に準拠していれば 1 に置換されます。準拠していない場合には、そもそもこのマクロが定義されていません。0 などの値に置換されるわけではないので、使い方に注意してください。
#include <stdio.h>
int main(void)
{
#ifdef __STDC__
( "標準規格に準拠しています。" );
puts#else
( "標準規格に準拠していません。" );
puts#endif
}
実行結果 (Visual Studio):
標準規格に準拠していません。
実行結果 (clang):
標準規格に準拠しています。
Visual Studio、clang とで実行結果が異なっています。Visual Studio での結果は意外かもしれません。
Visual Studio では、プロジェクトのプロパティを開き、「構成プロパティ」→「C/C++」→「言語」を選択し、「言語拡張を無効にする」を「はい(/Za)」に変更すれば、__STDC__ が定義されます。
__STDC_VERSION__ は、処理系がどの標準規格に対応しているかを表す、long int型の定数に置き換わります。
以下の表のようになっています。
対応規格 結果 | |
---|---|
C99 | 199901L |
C95 | 199409L |
C89以前、または規格に非対応 このマクロが定義されてい | ない |
#include <stdio.h>
int main(void)
{
#ifdef __STDC_VERSION__
( "%ld\n", __STDC_VERSION__ );
printf#else
( "__STDC_VERSION__ が定義されていません。\n" );
printf#endif
}
実行結果 (Visual Studio):
__STDC_VERSION__ が定義されていません。
実行結果 (clang):
201112
Visual Studio では、__STDC__ の定義の有無とも関係なく、つねに __STDC_VERSION__ は定義されないようです。clang では、__STDC_VERSION__ が定義されているので、値が出力されます。
Visual Studio ではできませんが、本来的には、__STDC__ の結果と、__STDC_VERSION__ の結果とを組み合わせて、対応規格を知ることができます。
__STDC__ が定義されていて、__STDC_VERSION__ が 199901L C99規格に対応 | |
__STDC__ が定義されていて、__STDC_VERSION__ が 199409L C95規格に対応 | |
__STDC__ が定義されていて、__STDC_VERSION__ が定義されていない C89規格に対応 | |
__STDC__ が定義されていない。 C言語の標準規格に合 | 致していない。 |
C11 では、__STDC_VERSION__ の置換結果は 201112L です。
__STDC_HOSTED__ は、処理系が、ホスト処理系であれば 1 に、フリースタンディング処理系であれば 0 に置換されます。
ホスト処理系とは、プログラムの実行が OS の支援によって行われる環境のことです。このような環境では、C言語の標準規格にあるすべての機能が使用できます。
フリースタンディング処理系とは、OS の支援無しでプログラムが実行される環境で、C言語の標準規格にある機能の一部しか使用できません。
#include <stdio.h>
int main(void)
{
( "%d\n", __STDC_HOSTED__ );
printf}
実行結果 :
1
C11 で、事前定義マクロに __STDC_MB_MIGHT_NEQ_WC__が加わりました。
このマクロについては、第47章で取り上げます。
事前定義識別子は、(そのままですが)事前定義マクロと違ってマクロではありません。いわば、変数を暗黙的に宣言しているのと似ており、スコープの概念があります。
C99 で定義されている事前定義識別子は __func__ だけです。
__func__は、関数の定義内でのみ使用でき、その関数名の文字列リテラルを得られます。より具体的には、関数の冒頭部分に、以下のコードがあるかのようにコンパイルされます。
static const char __func__[] = "関数名";
使用例は以下のようになります。
#include <stdio.h>
void func(void);
int main(void)
{
( __func__ );
puts
();
func}
void func(void)
{
( __func__ );
puts}
実行結果:
main
func
#line指令は、行番号とファイル名を強制的に変更する効果があり、__FILE__ と __LINE__ の置換結果に影響を与えます。「#」で始まっていることから分かるように、これはプリプロセッサディレクティブの一種であり、プリプロセスの段階で処理されます。
#include <stdio.h>
int main(void)
{
#line 1000
( "%s %d\n", __FILE__, __LINE__ );
printf
#line 2000 "test.c"
( "%s %d\n", __FILE__, __LINE__ );
printf
#line 3000
( "%s %d\n", __FILE__, __LINE__ );
printf}
実行結果:
main.c 1000
test.c 2000
test.c 3000
#line で指定された行番号が、その次の行の行番号になります。ぱっと見だと、上のプログラムの printf関数を呼び出している行は、1001行目、2001行目、3001行目になりそうに見えるかもしれませんが、そうはなりません。プリプロセッサディレクティブは、必ず1行使ってしまうので、こういう仕様でないと困りますが。
行番号に続けて、文字列リテラルも記述した場合には、その内容をファイルの名前と認識し、__FILE__ の置換結果を変更します。ファイル名を指定しない場合は、現在の名称のまま変更しません。なお、ファイル名だけの変更はできません。
#line が確実に効果をもたらす対象は、__FILE__ と __LINE__ の置換結果だけですが、大抵は、処理系が出力するエラーメッセージなどにも影響を与えます。先ほどのサンプルプログラムに、わざとコンパイルエラーを起こさせて試してみます。
#include <stdio.h>
int main(void)
{
#line 1000
( "%s %d\n", __FILE__, __LINE__ );
printf
#line 2000 "test.c"
( "%s %d\n", __FILE__, __LINE__ );
printf+ 1;
a
#line 3000
( "%s %d\n", __FILE__, __LINE__ );
printf}
Visual Studio 2017 の場合、次のようなエラーメッセージを出力しました。
1>test.c(2001) : error C2065: 'a' : 定義されていない識別子です。
clang 5.0.0 の場合も、同様の意味のエラーメッセージが出力されます。
test.c:2001:2: error: use of undeclared identifier 'a'
#pragma指令(プラグマ指令)は、処理系へ何らかの命令を与える手段として用意されています。形式としては、次のようになります。
#pragma 命令
#pragma という機能自体は標準規格で定められたものですが、「命令」の箇所に与える内容は処理系定義です。ですから、どんなことができるのかは、使用している処理系のドキュメントを確認する必要があります。
「命令」の部分に記述した内容を処理系が理解できる場合は、処理系が定義する意味どおりに処理されます。「命令」を理解できない場合は、単に無視されます。もし、複数の処理系に対応したプログラムを作るのなら、必要に応じて、#ifdef で切り分けるなどしないと、うまく動作しないでしょう。
#pragma の実例を挙げておきます。Visual Studio や clang では、#pragma once という指令を使うと、インクルードガード(第24章)を行えます。
#ifndef MY_HEADER_H_INCLUDED
#define MY_HEADER_H_INCLUDED
// ヘッダの本体
#endif
と書く代わりに、次のように記述します。
#pragma once
// ヘッダの本体
このようにヘッダファイルの先頭に記述しておくだけで、インクルードガードと同等のことが行えます。こちらの方が、記述量が減り、マクロ名が衝突することを考慮しなくてよくなりますが、処理系によっては動作しない可能性があります。
問題① プログラムの実行直後に、そのソースファイルをコンパイルしたときの日付・時刻を出力してみてください。
問題② ソースコード上のある地点で、ソースファイルの名前と行数を出力できるデバッグ関数を次のように作成しました。しかし、この関数には実用上の問題があります。問題点を指摘してください。
void printLog()
{
( "File: %s Line: %d\n", __FILE__, __LINE__ );
printf}
問題③ 自分の使っているコンパイラに用意されている pragma指令について調べてください。
return 0;
を削除(C言語編全体でのコードの統一)’2018/9/7 「C11 (__STDC_MB_MIGHT_NEQ_WC__)」の項を追加。
’2018/4/23 「C99(__func__)」の項の内容を修正。より具体的な動作を追記。VisualStudio 2015 で使用できないという記述を削除(使用できる)
≪さらに古い更新履歴を展開する≫
Programming Place Plus のトップページへ