Last Update 2024/01/09
概要
#include
sample.cpp
/* 実装で提供される標準ライブラリヘッダファイル iostream の読み込み */
#include <iostream>
/* プログラム特有のソースファイル sample.h の読み込み */
#include "sample.h"
int main()
{
Smpl s = {1, 2};
std::cout << "s.i = " << s.i << std::endl;
std::cout << "s.j = " << s.j << std::endl;
}
sample.h
struct Smpl
{
int i;
int j;
};
-Eオプションでプリプロセス実行済みのコードをファイルに出力
$ gcc -Wall -E sample.cpp -lstdc++ > sample_e.txt
出力されたsample_e.txt
# 0 "sample.cpp"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "sample.cpp"
# 1 "/usr/include/c++/12.2.0/iostream" 1 3 <--- 「iostream」
# 36 "/usr/include/c++/12.2.0/iostream" 3
# 37 "/usr/include/c++/12.2.0/iostream" 3
# 1 "/usr/include/c++/12.2.0/x86_64-pc-linux-gnu/bits/c++config.h" 1 3
# 296 "/usr/include/c++/12.2.0/x86_64-pc-linux-gnu/bits/c++config.h" 3
︙
# 834 "/usr/include/c++/12.2.0/ostream" 2 3
# 40 "/usr/include/c++/12.2.0/iostream" 2 3
# 1 "/usr/include/c++/12.2.0/istream" 1 3
︙
# 1017 "/usr/include/c++/12.2.0/istream" 2 3
# 41 "/usr/include/c++/12.2.0/iostream" 2 3
namespace std __attribute__ ((__visibility__ ("default")))
{
# 60 "/usr/include/c++/12.2.0/iostream" 3
extern istream cin;
extern ostream cout;
extern ostream cerr;
extern ostream clog;
extern wistream wcin;
extern wostream wcout;
extern wostream wcerr;
extern wostream wclog;
static ios_base::Init __ioinit;
}
# 3 "sample.cpp" 2
# 1 "sample.h" 1
# 1 "sample.h" <--- 「sample.h」
struct Smpl
{
int i;
int j;
};
# 6 "sample.cpp" 2
int main()
{
Smpl s = {1, 2};
std::cout << "s.i = " << s.i << std::endl;
std::cout << "s.j = " << s.j << std::endl;
}
-Eオプションでプリプロセス実行済みのコードをファイルに出力
$ gcc -Wall -x c++ sample_e.txt -lstdc++
$ ./a.out
s.i = 1
s.j = 2
#define
sample.cpp
#include <iostream>
/* マクロ名を文字列として定義 */
#define SAMP_MACRO1 "abcdef"
/* マクロ名を数式として定義 */
#define SAMP_MACRO2 (1 * 2)
/* 置換テキストを空で定義 */
#define SAMP_MACRO3
/* マクロ名をコマンド行として定義 */
#define SAMP_MACRO4 std::cout << "SAMP_MACRO4" << std::endl;
int main()
{
std::cout << "SAMP_MACRO1 = " << SAMP_MACRO1 << std::endl;
std::cout << "SAMP_MACRO2 = " << SAMP_MACRO2 << std::endl;
SAMP_MACRO3
SAMP_MACRO4
}
-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.cpp -lstdc++
︙
# 15 "sample.cpp"
# 15 "sample.cpp"
int main()
{
std::cout << "SAMP_MACRO1 = " << "abcdef" << std::endl; <--- 「SAMP_MACRO1」は「"abcdef"」に置換
std::cout << "SAMP_MACRO2 = " << (1 * 2) << std::endl; <--- 「SAMP_MACRO2」は「(1 * 2)」に置換
<--- 「SAMP_MACRO3」は空
std::cout << "SAMP_MACRO4" << std::endl; <--- 「SAMP_MACRO4」は「std::cout << "SAMP_MACRO4" << std::endl;」に置換
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
SAMP_MACRO1 = abcdef
SAMP_MACRO2 = 2
SAMP_MACRO4
#define (引数付き)
sample.cpp
#include <iostream>
/* 引数付きマクロ */
#define SAMP_MACRO1(str1) std::cout << "str1 = " str1 << std::endl;
/* 可変個数引数付きマクロ(識別子__VA_ARGS__使用) */
#define SAMP_MACRO2(...) v(__VA_ARGS__);
/* 2つの引数の演算式を置換テキストとして定義 */
#define SAMP_MACRO3(x, y) (x * y)
/* 2つの引数をそれぞれ括弧で括った演算式を置換テキストとして定義 */
#define SAMP_MACRO4(x, y) ((x) * (y))
static int n = 0; // カウント用
template<class T> void v(T x)
{
std::cout << "Type = " << typeid(T).name()
<< " --- args[" << n << "] = " << x << std::endl;
n++;
}
template<class T, class... Args> void v(T x, Args... args)
{
v<T>(x);
v<Args...>(args...);
}
int main()
{
int a = 2;
int b = 3;
SAMP_MACRO1("abc")
SAMP_MACRO2(1, 2, 1.23, 'a', 1.23456789012345678L)
std::cout << "SAMP_MACRO3 = " << SAMP_MACRO3(a, b) << std::endl;
std::cout << "SAMP_MACRO3 = " << SAMP_MACRO3(a + b, b + 3) << std::endl;
std::cout << "SAMP_MACRO4 = " << SAMP_MACRO4(a + b, b + 3) << std::endl;
}
-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.cpp -lstdc++
︙
# 15 "sample.cpp"
# 15 "sample.cpp"
static int n = 0;
template<class T> void v(T x)
{
std::cout << "Type = " << typeid(T).name()
<< " --- args[" << n << "] = " << x << std::endl;
n++;
}
template<class T, class... Args> void v(T x, Args... args)
{
v<T>(x);
v<Args...>(args...);
}
int main()
{
int a = 2;
int b = 3;
std::cout << "str1 = " "abc" << std::endl; <--- 引数「str1」は「"abc"」に置換
v(1, 2, 1.23, 'a', 1.23456789012345678L); <--- 可変個数引数はそれぞれ置換
std::cout << "SAMP_MACRO3 = " << (a * b) << std::endl; <--- 引数aとbが式中で置換
std::cout << "SAMP_MACRO3 = " << (a + b * b + 3) << std::endl; <--- 式中で置換された際に括弧が無いためb * bの演算が先行される
std::cout << "SAMP_MACRO4 = " << ((a + b) * (b + 3)) << std::endl; <--- それぞれの引数に括弧がある場合、マクロの意図する演算順序となる
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
str1 = abc
Type = i --- args[0] = 1
Type = i --- args[1] = 2
Type = d --- args[2] = 1.23
Type = c --- args[3] = a
Type = e --- args[4] = 1.23457
SAMP_MACRO3 = 6
SAMP_MACRO3 = 14
SAMP_MACRO4 = 30
#演算子 (文字列化演算子)
sample.cpp
#include <iostream>
/* # (文字列化)演算子(1) */
#define SAMP_MACRO1(str1, str2) #str1 " " #str2
/* # (文字列化)演算子(2) (識別子__VA_ARGS__使用) */
#define SAMP_MACRO2(...) #__VA_ARGS__
/* # (文字列化)演算子(3) */
#define SAMP_MACRO3(str1) std::cout << #str1 " = " << str1 << std::endl
int main()
{
int a = 1;
int b = 2;
std::cout << SAMP_MACRO1(2*3, 4*5) << std::endl;
std::cout << SAMP_MACRO2(2*3, 4-5, 6+7) << std::endl;
SAMP_MACRO3(a + b);
}
-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.cpp -lstdc++
︙
# 12 "sample.cpp"
# 12 "sample.cpp"
int main()
{
int a = 1;
int b = 2;
std::cout << "2*3" " " "4*5" << std::endl; <--- 「#」の付いた引数がそれぞれ二重引用符で囲まれて置換
std::cout << "2*3, 4-5, 6+7" << std::endl; <--- 可変個数引数全体が二重引用符で囲まれて置換
std::cout << "a + b" " = " << a + b << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
2*3 4*5
2*3, 4-5, 6+7
a + b = 3
##演算子 (トークン連結演算子)
sample.cpp
#include <iostream>
#define A1 "sample1"
#define A2 "sample2"
#define A3 "sample3"
/* ## (トークン連結)演算子 */
#define SAMP_MACRO(str1, str2) str1 ## str2
int main()
{
std::cout << SAMP_MACRO(A, 1) << std::endl;
std::cout << SAMP_MACRO(A, 2) << std::endl;
std::cout << SAMP_MACRO(A, 3) << std::endl;
}
-Eオプションを付けてコンパイルした場合の出力結果
bash-5.1$ gcc -Wall -E sample.cpp -lstdc++
︙
# 10 "sample.cpp"
# 10 "sample.cpp"
int main()
{
std::cout << "sample1" << std::endl; 「A ## 1」 ---> 「A1」 ---> 「"sample1"」
std::cout << "sample2" << std::endl; 「A ## 2」 ---> 「A2」 ---> 「"sample2"」
std::cout << "sample3" << std::endl; 「A ## 3」 ---> 「A3」 ---> 「"sample3"」
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
sample1
sample2
sample3
#undef
sample.cpp
#include <iostream>
/* 最初のマクロ定義 */
#define SAMP_MACRO "sample_1"
int main()
{
std::cout << SAMP_MACRO << std::endl;
/* マクロ取り消し */
#undef SAMP_MACRO
/* マクロ再定義 */
#define SAMP_MACRO "sample_2"
std::cout << SAMP_MACRO << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
sample_1
sample_2
#if #elif #else #endif
sample.cpp
#include <iostream>
#define SMPL_MACRO1 2
#define SMPL_MACRO2 20
int main()
{
/* 条件コンパイル */
#if defined(__GNUC__)
# if __GNUC__ >= 9
std::cout << "__GNUC__ = " << __GNUC__ << std::endl;
# endif
#endif
#if SMPL_MACRO1 > 2
/* この条件はコンパイルされない */
std::cout << "SMPL_MACRO1 > 2" << std::endl;
#else
# if SMPL_MACRO2 > 20
/* この条件はコンパイルされない */
std::cout << "(SMPL_MACRO1 <= 2) && (SMPL_MACRO2 > 20)" << std::endl;
# elif SMPL_MACRO2 == 20
std::cout << "(SMPL_MACRO1 <= 2) && (SMPL_MACRO2 == 20)" << std::endl;
# endif
#endif
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
__GNUC__ = 12
(SMPL_MACRO1 <= 2) && (SMPL_MACRO2 == 20)
#ifdef #ifndef
sample.cpp
#include <iostream>
#define SMPL_MACRO1 2
/* 置換テキストが空のマクロを定義 */
#define SMPL_MACRO2
int main()
{
/* __GNUC__が定義されていることを確認 */
#ifdef __GNUC__
std::cout << "__GNUC__ = " << __GNUC__ << std::endl;
#endif
/* SMPL_MACRO1が定義されていることを確認 */
#ifdef SMPL_MACRO1
/* SMPL_MACRO2が定義されていることを確認 */
# ifdef SMPL_MACRO2
/* SMPL_MACRO3が定義されていないことを確認 */
# ifndef SMPL_MACRO3
std::cout << "#ifdef SMPL_MACRO1 ---> #ifdef SMPL_MACRO2 ---> #ifndef SMPL_MACRO3" << std::endl;
# endif
# endif
#endif
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
__GNUC__ = 12
#ifdef SMPL_MACRO1 ---> #ifdef SMPL_MACRO2 ---> #ifndef SMPL_MACRO3
#line
sample1.cpp
#include <iostream>
extern void func(void);
int main()
{
/* 標準定義済みマクロ */
/* __FILE__ : 現在のファイル名 */
/* __LINE__ : 現在の行番号 */
#line 1 /* 次行を関数main()の1行目として定義 */
std::cout << __FILE__ << " main() " << __LINE__ << "行目" << std::endl;
std::cout << __FILE__ << " main() " << __LINE__ << "行目" << std::endl;
func();
#line 356 "/usr/include/stdio.h"
std::cout << __FILE__ << " stdio.h " << __LINE__ << "行目" << std::endl;
}
sample2.cpp
#include <iostream>
void func()
{
#line 1 /* 次行を関数func()の1行目として定義 */
std::cout << __FILE__ << " func() " << __LINE__ << "行目" << std::endl;
}
実行結果
$ gcc -Wall sample1.cpp sample2.cpp -lstdc++
$ ./a.out
sample1.cpp main() 1行目
sample1.cpp main() 2行目
sample2.cpp func() 1行目
/usr/include/stdio.h stdio.h 356行目
#error
sample.cpp
#include <iostream>
int main()
{
#if defined(__GNUC__)
# if __GNUC__ >= 9
/* 次行でエラー発生・処理停止 */
#error "__GNUC__ >= 9"
# endif
#endif
std::cout << "sample" << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
sample.cpp:8:2: error: #error "__GNUC__ >= 9" <--- この時点で処理停止
8 | #error "__GNUC__ >= 9"
| ^~~~~
#pragma
sample.cpp
#include <iostream>
int main()
{
/* コンパイラメッセージとして文字列を表示 */
#pragma message "sample message"
/* 警告メッセージを生成 */
#pragma GCC warning "sample warning"
std::cout << "sample" << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
sample.cpp:9:21: warning: sample warning
9 | #pragma GCC warning "sample warning"
| ^~~~~~~~~~~~~~~~
sample.cpp: In function ‘int main()’:
sample.cpp:6:17: note: ‘#pragma message: sample message’
6 | #pragma message "sample message"
| ^~~~~~~~~~~~~~~~
$ ./a.out
sample
_Pragma演算子
sample.cpp
#include <iostream>
/* _Pragma演算子をマクロで使用 */
#define WARNING_STR2(s) PRAGMA_WARNING(GCC warning #s)
#define PRAGMA_WARNING(s) _Pragma(#s)
int main()
{
/* #pragmaで直接記述 */
#pragma GCC warning "sample warning 0"
/* _Pragma演算子で記述 */
_Pragma ("GCC warning \"sample warning 1\"")
/* _Pragma演算子を使用したマクロで記述 */
WARNING_STR2(sample warning 2)
std::cout << "sample" << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
sample.cpp:10:21: warning: sample warning 0
10 | #pragma GCC warning "sample warning 0"
| ^~~~~~~~~~~~~~~~~~
sample.cpp:13:13: warning: sample warning 1
13 | _Pragma ("GCC warning \"sample warning 1\"")
| ^~~~~~~~~~~~~~~~~~
sample.cpp:16:13: warning: sample warning 2
16 | WARNING_STR2(sample warning 2)
| ^~~~~~~~~~~~~~~~~~
$ ./a.out
sample
defined演算子
sample.cpp
#include <iostream>
#define SMPL_MACRO1 2
/* 置換テキストが空のマクロを定義 */
#define SMPL_MACRO2
int main()
{
/* __GNUC__が定義されていることを確認 */
#if defined(__GNUC__)
std::cout << "__GNUC__ = " << __GNUC__ << std::endl;
#endif
/* SAMP_MACRO1とSAMP_MACRO2が定義されていることを確認 */
#if defined(SMPL_MACRO1) && defined(SMPL_MACRO2)
std::cout << "#if defined(SMPL_MACRO1) && defined(SMPL_MACRO2)" << std::endl;
#endif
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
__GNUC__ = 12
#if defined(SMPL_MACRO1) && defined(SMPL_MACRO2)
定義済みマクロ(実装依存値含む)
sample.cpp
#include <iostream>
int main()
{
std::cout << "定義済みマクロ(実装依存値含む)" << std::endl;
std::cout << "コンパイラがC++のときに定義 __cplusplus = " << __cplusplus << std::endl;
std::cout << "現在のソースファイルのプリプロセス実行の日付 __DATE__ = " << __DATE__ << std::endl;
std::cout << "現在のソースファイル名 __FILE__ = " << __FILE__ << std::endl;
std::cout << "ソースファイル内の「現在の」行番号 __LINE__ = " << __LINE__ << std::endl;
std::cout << "現在のソースファイルのプリプロセス実行時刻 __TIME__ = " << __TIME__ << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -E -lstdc++ > sample_e.txt <--- プリプロセスの実行結果をファイルへ出力
$ cat sample_e.txt <--- 出力結果の表示
︙
# 2 "sample.cpp" 2
# 3 "sample.cpp"
int main()
{
std::cout << "定義済みマクロ(実装依存値含む)" << std::endl;
std::cout << "コンパイラがC++のときに定義 __cplusplus = " << 201703L << std::endl;
std::cout << "現在のソースファイルのプリプロセス実行の日付 __DATE__ = " << "Dec 30 2023" << std::endl;
std::cout << "現在のソースファイル名 __FILE__ = " << "sample.cpp" << std::endl;
std::cout << "ソースファイル内の「現在の」行番号 __LINE__ = " << 9 << std::endl;
std::cout << "現在のソースファイルのプリプロセス実行時刻 __TIME__ = " << "22:39:51" << std::endl;
}
$ gcc -Wall -x c++ sample_e.txt -lstdc++ <--- 出力結果を使用してコンパイルを引き続き実行
$ ./a.out
定義済みマクロ(実装依存値含む)
コンパイラがC++のときに定義 __cplusplus = 201703
現在のソースファイルのプリプロセス実行の日付 __DATE__ = Dec 30 2023
現在のソースファイル名 __FILE__ = sample.cpp
ソースファイル内の「現在の」行番号 __LINE__ = 9
現在のソースファイルのプリプロセス実行時刻 __TIME__ = 22:39:51
実行環境
GNU bash, version 5.1.16
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39
コード例・出力内容中の表記
・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。