Last Update 2023/07/04
テスト概要
#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
インクルードガードの有無による挙動の確認
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
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39
コード例・出力内容中の表記
・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。