Loose-Info.com
Last Update 2024/01/09
TOP - 各種テスト - C言語 - プリプロセッサ指令

テスト概要

#include
#define
#define (引数付き)
#演算子 (文字列化演算子)
##演算子 (トークン連結演算子)
#undef
#if #elif #else #endif
#ifdef #ifndef
#line
#error
#pragma
_Pragma演算子
defined演算子
インクルードガードの有無による挙動の確認


#include


sample.c
/* 標準ライブラリヘッダファイル stdio.h の読み込み */ #include <stdio.h> /* 標準ライブラリヘッダファイル stdlib.h の読み込み */ #include <stdlib.h> /* プログラム特有のソースファイル sample.h の読み込み */ #include "sample.h" int main(void) { printf("VALUE1 = %d\n", VALUE1); return EXIT_SUCCESS; }
sample.h
/* マクロ名 VALUE1 の定義 */ #define VALUE1 1

-Eオプションでプリプロセス実行済みのコードをファイルに出力
$ gcc -Wall -E sample.c > sample_e.txt

出力されたsample_e.txt
# 0 "sample.c" # 0 "<built-in>" # 0 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 0 "<command-line>" 2 # 1 "sample.c" # 1 "/usr/include/stdio.h" 1 3 4 <--- 「stdio.h」 # 27 "/usr/include/stdio.h" 3 4 extern int printf (const char *__restrict __format, ...); extern int __uflow (FILE *); extern int __overflow (FILE *, int); # 909 "/usr/include/stdio.h" 3 4 # 3 "sample.c" 2 # 1 "/usr/include/stdlib.h" 1 3 4 <--- 「stdlib.h」 # 26 "/usr/include/stdlib.h" 3 4 # 1037 "/usr/include/stdlib.h" 2 3 4 # 1048 "/usr/include/stdlib.h" 3 4 # 5 "sample.c" 2 # 1 "sample.h" 1 <--- 「sample.h」 # 8 "sample.c" 2 # 9 "sample.c" int main(void) { printf("VALUE1 = %d\n", 1); <--- 「VALUE1」は「1」に置換 return # 13 "sample.c" 3 4 0 <--- 「EXIT_SUCCESS」は「0」に置換 # 13 "sample.c" ; }

-Eオプションで出力されたsample_e.txtのコンパイルを実行
$ gcc -Wall -x c sample_e.txt $ ./a.out VALUE1 = 1

#define


sample.c
#include <stdio.h> #include <stdlib.h> /* マクロ名を文字列として定義 */ #define SAMP_MACRO1 "abcdef" /* マクロ名を数式として定義 */ #define SAMP_MACRO2 (1 * 2) /* 置換テキストを空で定義 */ #define SAMP_MACRO3 /* マクロ名をコマンド行として定義 */ #define SAMP_MACRO4 printf("SAMP_MACRO4\n"); int main(void) { printf("SAMP_MACRO1 = %s\n", SAMP_MACRO1); printf("SAMP_MACRO2 = %d\n", SAMP_MACRO2); SAMP_MACRO3 SAMP_MACRO4 return EXIT_SUCCESS; }

-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.c # 16 "sample.c" int main(void) { printf("SAMP_MACRO1 = %s\n", "abcdef"); <--- 「SAMP_MACRO1」は「"abcdef"」に置換 printf("SAMP_MACRO2 = %d\n", (1 * 2)); <--- 「SAMP_MACRO2」は「(1 * 2)」に置換 <--- 「SAMP_MACRO3」は空 printf("SAMP_MACRO4\n"); <--- 「SAMP_MACRO4」は「printf("SAMP_MACRO4\n");」に置換 return # 23 "sample.c" 3 4 0 # 23 "sample.c" ; }

実行結果
$ gcc -Wall sample.c $ ./a.out SAMP_MACRO1 = abcdef SAMP_MACRO2 = 2 SAMP_MACRO4

#define (引数付き)


sample.c
#include <stdio.h> #include <stdlib.h> /* 引数付きマクロ */ #define SAMP_MACRO1(str1) fprintf(stdout, "str1 = %s\n", str1); /* 可変個数引数付きマクロ(識別子__VA_ARGS__使用) */ #define SAMP_MACRO2(...) fprintf(stdout, __VA_ARGS__); /* 2つの引数の演算式を置換テキストとして定義 */ #define SAMP_MACRO3(x, y) (x * y) /* 2つの引数をそれぞれ括弧で括った演算式を置換テキストとして定義 */ #define SAMP_MACRO4(x, y) ((x) * (y)) int main(void) { int a = 2; int b = 3; SAMP_MACRO1("abc") SAMP_MACRO2("str2 = %s\n", "samp_macro2") printf("SAMP_MACRO3 = %d\n", SAMP_MACRO3(a, b)); printf("SAMP_MACRO3 = %d\n", SAMP_MACRO3(a + b, b + 3)); printf("SAMP_MACRO4 = %d\n", SAMP_MACRO4(a + b, b + 3)); return EXIT_SUCCESS; }

-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.c # 16 "sample.c" # 16 "sample.c" int main(void) { int a = 2; int b = 3; fprintf( # 21 "sample.c" 3 4 stdout # 21 "sample.c" , "str1 = %s\n", "abc"); <--- 引数「str1」は「"abc"」に置換 fprintf( # 22 "sample.c" 3 4 stdout # 22 "sample.c" , "str2 = %s\n", "samp_macro2"); <--- 2つの可変個数引数はそれぞれ置換 printf("SAMP_MACRO3 = %d\n", (a * b)); <--- 引数aとbが式中で置換 printf("SAMP_MACRO3 = %d\n", (a + b * b + 3)); <--- 式中で置換された際に括弧が無いためb * bの演算が先行される printf("SAMP_MACRO4 = %d\n", ((a + b) * (b + 3))); <--- それぞれの引数に括弧がある場合、マクロの意図する演算順序となる return # 27 "sample.c" 3 4 0 # 27 "sample.c" ; }

実行結果
$ gcc -Wall sample.c $ ./a.out str1 = abc str2 = samp_macro2 SAMP_MACRO3 = 6 SAMP_MACRO3 = 14 SAMP_MACRO4 = 30

#演算子 (文字列化演算子)


sample.c
#include <stdio.h> #include <stdlib.h> /* # (文字列化)演算子(1) */ #define SAMP_MACRO1(str1, str2) #str1 " " #str2 /* # (文字列化)演算子(2) (識別子__VA_ARGS__使用) */ #define SAMP_MACRO2(...) #__VA_ARGS__ /* # (文字列化)演算子(3) */ #define SAMP_MACRO3(str1, str2) fprintf(stdout, #str1 " = " #str2 "\n", str1) int main(void) { int a = 1; int b = 2; printf("%s\n", SAMP_MACRO1(2*3, 4*5)); printf("%s\n", SAMP_MACRO2(2*3, 4-5, 6+7)); printf("%s\n", SAMP_MACRO2(2*3\n, "abc", 6+7)); SAMP_MACRO3(a + b, %d); return EXIT_SUCCESS; }

-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.c # 13 "sample.c" # 13 "sample.c" int main(void) { int a = 1; int b = 2; printf("%s\n", "2*3" " " "4*5"); <--- 「#」の付いた引数がそれぞれ二重引用符で囲まれて置換 printf("%s\n", "2*3, 4-5, 6+7"); <--- 可変個数引数全体が二重引用符で囲まれて置換 printf("%s\n", "2*3\n, \"abc\", 6+7"); <--- 可変個数引数中の二重引用符はバックスラッシュでエスケープされて置換 fprintf( # 22 "sample.c" 3 4 stdout # 22 "sample.c" , "a + b" " = " "%d" "\n", a + b); return # 24 "sample.c" 3 4 0 # 24 "sample.c" ; }

実行結果
$ gcc -Wall sample.c $ ./a.out 2*3 4*5 2*3, 4-5, 6+7 2*3 , "abc", 6+7 a + b = 3

##演算子 (トークン連結演算子)


sample.c
#include <stdio.h> #include <stdlib.h> #define A1 "sample1" #define A2 "sample2" #define A3 "sample3" /* ## (トークン連結)演算子 */ #define SAMP_MACRO(str1, str2) str1 ## str2 int main(void) { printf("%s\n", SAMP_MACRO(A, 1)); printf("%s\n", SAMP_MACRO(A, 2)); printf("%s\n", SAMP_MACRO(A, 3)); return EXIT_SUCCESS; }

-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.c # 11 "sample.c" # 11 "sample.c" int main(void) { printf("%s\n", "sample1"); 「A ## 1」 ---> 「A1」 ---> 「"sample1"」 printf("%s\n", "sample2"); 「A ## 2」 ---> 「A2」 ---> 「"sample2"」 printf("%s\n", "sample3"); 「A ## 3」 ---> 「A3」 ---> 「"sample3"」 return # 17 "sample.c" 3 4 0 # 17 "sample.c" ; }

実行結果
$ gcc -Wall sample.c $ ./a.out sample1 sample2 sample3

#undef


sample.c
#include <stdio.h> #include <stdlib.h> /* 最初のマクロ定義 */ #define SAMP_MACRO "sample_1" int main(void) { printf("%s\n", SAMP_MACRO); /* マクロ取り消し */ #undef SAMP_MACRO /* マクロ再定義 */ #define SAMP_MACRO "sample_2" printf("%s\n", SAMP_MACRO); return EXIT_SUCCESS; }

実行結果
$ gcc -Wall sample.c $ ./a.out sample_1 sample_2

#if #elif #else #endif


sample.c
#include <stdio.h> #include <stdlib.h> #define SAMP_MACRO1 2 #define SAMP_MACRO2 20 int main(void) { /* 条件コンパイル */ #if defined(__GNUC__) # if __GNUC__ >= 9 printf("__GNUC__ = %d\n", __GNUC__); # endif #endif #if SAMP_MACRO1 > 2 /* この条件はコンパイルされない */ printf("SAMP_MACRO1 > 2\n"); #else # if SAMP_MACRO2 > 20 /* この条件はコンパイルされない */ printf("(SAMP_MACRO1 <= 2) && (SAMP_MACRO2 > 20)\n"); # elif SAMP_MACRO2 == 20 printf("(SAMP_MACRO1 <= 2) && (SAMP_MACRO2 == 20)\n"); # endif #endif return EXIT_SUCCESS; }

実行結果
$ gcc -Wall sample.c $ ./a.out __GNUC__ = 12 (SAMP_MACRO1 <= 2) && (SAMP_MACRO2 == 20)

#ifdef #ifndef


sample.c
#include <stdio.h> #include <stdlib.h> #define SAMP_MACRO1 2 /* 置換テキストが空のマクロを定義 */ #define SAMP_MACRO2 int main(void) { /* __GNUC__が定義されていることを確認 */ #ifdef __GNUC__ printf("__GNUC__ = %d\n", __GNUC__); #endif /* SAMP_MACRO1が定義されていることを確認 */ #ifdef SAMP_MACRO1 /* SAMP_MACRO2が定義されていることを確認 */ # ifdef SAMP_MACRO2 /* SAMP_MACRO3が定義されていないことを確認 */ # ifndef SAMP_MACRO3 printf("#ifdef SAMP_MACRO1 ---> #ifdef SAMP_MACRO2 ---> #ifndef SAMP_MACRO3\n"); # endif # endif #endif return EXIT_SUCCESS; }

実行結果
$ gcc -Wall sample.c $ ./a.out __GNUC__ = 12 #ifdef SAMP_MACRO1 ---> #ifdef SAMP_MACRO2 ---> #ifndef SAMP_MACRO3

#line


sample1.c
#include <stdio.h> #include <stdlib.h> extern void func(void); int main(void) { /* 標準定義済みマクロ */ /* __FILE__ : 現在のファイル名 */ /* __LINE__ : 現在の行番号 */ #line 1 /* 次行を関数main()の1行目として定義 */ printf("%s main() %d行目\n", __FILE__, __LINE__); printf("%s main() %d行目\n", __FILE__, __LINE__); func(); #line 356 "/usr/include/stdio.h" printf("%s stdio.h %d行目\n", __FILE__, __LINE__); return EXIT_SUCCESS; }
sample2.c
#include <stdio.h> void func(void) { #line 1 /* 次行を関数func()の1行目として定義 */ printf("%s func() %d行目\n", __FILE__, __LINE__); }

実行結果
$ gcc -Wall sample1.c sample2.c $ ./a.out sample1.c main() 1行目 sample1.c main() 2行目 sample2.c func() 1行目 /usr/include/stdio.h stdio.h 356行目

#error


sample.c
#include <stdio.h> #include <stdlib.h> int main(void) { #if defined(__GNUC__) # if __GNUC__ >= 9 /* 次行でエラー発生・処理停止 */ #error "__GNUC__ >= 9" # endif #endif printf("sample\n"); return EXIT_SUCCESS; }

実行結果
$ gcc -Wall sample.c sample.c: In function ‘main’: sample.c:9:2: error: #error "__GNUC__ >= 9" <--- この時点で処理停止 9 | #error "__GNUC__ >= 9" | ^~~~~

#pragma


sample.c
#include <stdio.h> #include <stdlib.h> int main(void) { /* コンパイラメッセージとして文字列を表示 */ #pragma message "sample message" /* 警告メッセージを生成 */ #pragma GCC warning "sample warning" printf("sample\n"); return EXIT_SUCCESS; }

実行結果
$ gcc -Wall sample.c sample.c: In function ‘main’: sample.c:7:9: note: ‘#pragma message: sample message’ 7 | #pragma message "sample message" | ^~~~~~~ sample.c:10:21: warning: sample warning 10 | #pragma GCC warning "sample warning" | ^~~~~~~~~~~~~~~~ $ ./a.out sample

_Pragma演算子


sample.c
#include <stdio.h> #include <stdlib.h> /* _Pragma演算子をマクロで使用 */ #define WARNING_STR2(s) PRAGMA_WARNING(GCC warning #s) #define PRAGMA_WARNING(s) _Pragma(#s) int main(void) { /* #pragmaで直接記述 */ #pragma GCC warning "sample warning 0" /* _Pragma演算子で記述 */ _Pragma ("GCC warning \"sample warning 1\"") /* _Pragma演算子を使用したマクロで記述 */ WARNING_STR2(sample warning 2) printf("sample\n"); return EXIT_SUCCESS; }

実行結果
$ gcc -Wall sample.c sample.c: In function ‘main’: sample.c:11:21: warning: sample warning 0 11 | #pragma GCC warning "sample warning 0" | ^~~~~~~~~~~~~~~~~~ sample.c:14:13: warning: sample warning 1 14 | _Pragma ("GCC warning \"sample warning 1\"") | ^~~~~~~~~~~~~~~~~~ sample.c:17:13: warning: sample warning 2 17 | WARNING_STR2(sample warning 2) | ^~~~~~~~~~~~~~~~~~ $ ./a.out sample

defined演算子


sample.c
#include <stdio.h> #include <stdlib.h> #define SAMP_MACRO1 2 /* 置換テキストが空のマクロを定義 */ #define SAMP_MACRO2 int main(void) { /* __GNUC__が定義されていることを確認 */ #if defined(__GNUC__) printf("__GNUC__ = %d\n", __GNUC__); #endif /* SAMP_MACRO1とSAMP_MACRO2が定義されていることを確認 */ #if defined(SAMP_MACRO1) && defined(SAMP_MACRO2) printf("#if defined(SAMP_MACRO1) && defined(SAMP_MACRO2)\n"); #endif return EXIT_SUCCESS; }

実行結果
$ gcc -Wall sample.c $ ./a.out __GNUC__ = 12 #if defined(SAMP_MACRO1) && defined(SAMP_MACRO2)

インクルードガードの有無による挙動の確認


sample.c
#include <stdio.h> #include <stdlib.h> #include "sample1.h" #include "sample2.h" int main(void) { struct st s1 = {1}; printf("s1.mem = %d\n", s1.mem); return EXIT_SUCCESS; }
samp0.h
struct st { int mem; };
sample1.h
#include "samp0.h"
sample2.h
#include "samp0.h" <--- 上記「sample2.h」と同じ「samp0.h」のインクルード指令

実行結果
$ gcc -Wall sample.c In file included from sample2.h:1, from sample.c:5: samp0.h:1:8: error: redefinition of ‘struct st’ <--- 構造体の再定義に関するエラー発生 1 | struct st { int mem; }; | ^~ In file included from sample1.h:1, from sample.c:4: samp0.h:1:8: note: originally defined here 1 | struct st { int mem; }; | ^~

samp0.hにインクルードガードを追加
#ifndef SAMP0_H #define SAMP0_H struct st { int mem; }; #endif /* SAMP0_H */

実行結果
$ gcc -Wall sample.c $ ./a.out s1.mem = 1

実行環境

GNU bash, version 5.1.16
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39


コード例・出力内容中の表記

・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。