C言語プリプロセッサ | 2024.07.11 |
#include ファイル内容の取り込み |
#include <stdio.h>
#include <sys/time.h>
#include "mydef.h"
int main()
{
struct timeval t;
gettimeofday(&t, NULL);
printf("hello %lu\n", t.tv_sec);
}
#include <sys/time.h>
#include "mydef.h"
int main()
{
struct timeval t;
gettimeofday(&t, NULL);
printf("hello %lu\n", t.tv_sec);
}
<~>はコンパイラのヘッダファイルインストール場所からの相対パス、"~"はソースファイルの位置からの相対パスを指定する。
C言語プリプロセッサ | 2024.07.12 |
#include C++のヘッダファイルとstd名前空間 |
C++の標準ライブラリ関数はstd名前空間に属しており、それらを定義するヘッダファイルには「.h」が付かない。
#include <iostream>
int main()
{
std::cout << "Hello" << std::endl;
return 0;
}
int main()
{
std::cout << "Hello" << std::endl;
return 0;
}
※ | C++98で標準ライブラリをstd名前空間に属するものと定め、「.h」の付かないヘッダファイルを導入した。 |
C言語プリプロセッサ | 2024.07.11 |
#include C++のヘッダファイル(C言語互換) |
C++でC言語のヘッダファイルをincludeできるが、それらは全てグローバル名前空間での定義となる。C++では、それらをstd名前空間内に定義しなおしたヘッダファイルを使うことが推奨されている。それらはC言語のヘッダファイル名の「.h」を取り除き、頭文字にcを付けた名前になっている。
#include <cstdio>
#include <cstring>
int main()
{
std::printf("Hello\n");
char buf[20];
std::strcpy(buf, "World");
std::puts(buf);
return 0;
}
#include <cstring>
int main()
{
std::printf("Hello\n");
char buf[20];
std::strcpy(buf, "World");
std::puts(buf);
return 0;
}
※ | 実際はstd::は付けなくてもよい。C言語との互換性のためにグローバル名前空間としても定義されている。 |
C言語プリプロセッサ | 2024.07.11 |
#define 定義だけのマクロ |
値を定義しないで定義しただけのマクロは、何もない文字に置き換わる。主に#ifdefなどで定義が存在するかどうかでの判断条件になる。
#include <stdio.h>
#define DEBUG
#ifdef DEBUG
#define ERR "Error "
#else
#define ERR
#endif
int main()
{
printf(ERR"Hello\n");
return 0;
}
#define DEBUG
#ifdef DEBUG
#define ERR "Error "
#else
#define ERR
#endif
int main()
{
printf(ERR"Hello\n");
return 0;
}
Error Hello
DEBUGのdefine行を削除すると、表示は「Error」が消えて「Hello」だけになる。
C言語プリプロセッサ | 2024.07.13 |
#define マクロ定数定義 |
マクロ定数は、大文字にする習慣があるが文法的な強制ではない。
マクロ定数の定義の中に、前出のマクロが含まれていてもよい。
#define VERSION "Ver 2.5.10"
#define BUFSIZE (1024)
#define MAX_BUFSIZE (BUFSIZE*128)
#define MIN_INT -(((__int64)1<<30))
#define PI (3.1416)
#define True 1
#define False 0
#define BUFSIZE (1024)
#define MAX_BUFSIZE (BUFSIZE*128)
#define MIN_INT -(((__int64)1<<30))
#define PI (3.1416)
#define True 1
#define False 0
特に演算が含まれる数値に展開するマクロ定義は(~)で囲むことが勧められる。数式の中に展開されたとき、周囲との演算の優先順位が変わるかもしれないからである。マクロは単純な置換なので、マクロ定義中の演算結果に置き換わるわけではない。
C言語プリプロセッサ | 2024.07.11 |
#define マクロ関数 |
#defineのマクロ関数の引数は、マクロ展開が可能ならばデータの形式は問わない。
#include <stdio.h>
#include <math.h>
#define POW2(x) (int)pow(x,2)
#define IFGT(x,y,z) if(x>y) z=1;else z=0;
#define ARR(a,b) a[b]
#define swap16(x) ((x>>8)&0xff)+(x<<8)
#define ERRMSG(msg) printf("Error: %s\n", msg)
#define CHKEQ(a,b) if(a!=b) goto end;
int main()
{
int a, b;
unsigned short c = 0xaabb;
char *er[] = {"syntax", "exception"};
c = swap16(c);
printf("%x\n", c);
a = POW2(4);
IFGT(a, 10, b)
CHKEQ(b, 0)
end:
ERRMSG(ARR(er, 1));
return 0;
}
#include <math.h>
#define POW2(x) (int)pow(x,2)
#define IFGT(x,y,z) if(x>y) z=1;else z=0;
#define ARR(a,b) a[b]
#define swap16(x) ((x>>8)&0xff)+(x<<8)
#define ERRMSG(msg) printf("Error: %s\n", msg)
#define CHKEQ(a,b) if(a!=b) goto end;
int main()
{
int a, b;
unsigned short c = 0xaabb;
char *er[] = {"syntax", "exception"};
c = swap16(c);
printf("%x\n", c);
a = POW2(4);
IFGT(a, 10, b)
CHKEQ(b, 0)
end:
ERRMSG(ARR(er, 1));
return 0;
}
bbaa
Error: exception
Error: exception
マクロ関数になにも定義しない場合は、空の定義となり適用箇所は空に置換される(その部分が消える)。
#ifdef DEBUG
#define LOG_PRINT(x) printf("trace: %s\n", x)
#else
#define LOG_PRINT(x)
#endif
#define LOG_PRINT(x) printf("trace: %s\n", x)
#else
#define LOG_PRINT(x)
#endif
C言語プリプロセッサ | 2024.07.11 |
#define 複数行のマクロ定義 |
長い定義、複数行の定義を行末で「\」で折り返すことができる。
#include <stdio.h>
#include <stdlib.h>
#define ASSERT_IFNULL(x) \
do { \
if (!(x)) { \
printf("NULL %s %d\n", \
__FUNCTION__, __LINE__);\
exit(0); \
} \
} while (0)
int main()
{
void *p = NULL;
ASSERT_IFNULL(p);
return 0;
}
#include <stdlib.h>
#define ASSERT_IFNULL(x) \
do { \
if (!(x)) { \
printf("NULL %s %d\n", \
__FUNCTION__, __LINE__);\
exit(0); \
} \
} while (0)
int main()
{
void *p = NULL;
ASSERT_IFNULL(p);
return 0;
}
NULL main 17
#define VLEVEL (LVLWARN | LVLINFO | \
LVLERR)
LVLERR)
C言語プリプロセッサ | 2024.07.11 |
#define 可変長引数のマクロ関数 |
マクロ関数の可変長引数はC99で導入された。「...」の0個以上の引数は__VA_ARGS__の箇所に置き換わる。
#include <stdio.h>
#define DEBUG1(fmt, ...) printf("LOG: " fmt "\n", __VA_ARGS__)
#define DEBUG2(...) printf("LOG: " __VA_ARGS__)
// GNU extension
#define DEBUG3(fmt, ...) printf("LOG: " fmt, ##__VA_ARGS__)
#define DEBUG4(args...) printf("LOG: " args)
int main()
{
DEBUG1("error code %d %s", 100, "syntax");
DEBUG2("error code %d %s\n", 200, "null pointer");
DEBUG3("error code 300\n");
DEBUG4("error code %d %s\n", 400, "unexpected");
return 0;
}
#define DEBUG1(fmt, ...) printf("LOG: " fmt "\n", __VA_ARGS__)
#define DEBUG2(...) printf("LOG: " __VA_ARGS__)
// GNU extension
#define DEBUG3(fmt, ...) printf("LOG: " fmt, ##__VA_ARGS__)
#define DEBUG4(args...) printf("LOG: " args)
int main()
{
DEBUG1("error code %d %s", 100, "syntax");
DEBUG2("error code %d %s\n", 200, "null pointer");
DEBUG3("error code 300\n");
DEBUG4("error code %d %s\n", 400, "unexpected");
return 0;
}
LOG: error code 100 syntax
LOG: error code 200 null pointer
LOG: error code 300
LOG: error code 400 unexpected
LOG: error code 200 null pointer
LOG: error code 300
LOG: error code 400 unexpected
DEBUG3、DEBUG4はGNU拡張である。
##__VA_ARGS__は、可変長引数が何も指定されず0個の場合に、直前の余計に残る「,」を削除する。GNU拡張を使わないならば、DEBUG2のように「fmt」相当の文字列も可変長引数の中に含めればよい。
DEBUG4は、可変長部分の名前を任意に付けている。※
##__VA_ARGS__は、可変長引数が何も指定されず0個の場合に、直前の余計に残る「,」を削除する。GNU拡張を使わないならば、DEBUG2のように「fmt」相当の文字列も可変長引数の中に含めればよい。
DEBUG4は、可変長部分の名前を任意に付けている。※
※ | GCCがC99前から使っていた方法。 |
C言語プリプロセッサ | 2024.07.11 |
#A マクロ関数引数を文字列化 |
#はマクロ引数をクォーテーションで囲って文字列定数化する。このとき、その引数が変数やほかのマクロ定数であっても、その内容が持つ値は解釈しない。単純にマクロの引数に書いてあるものを"~"で囲むだけである。
#include <stdio.h>
#define err(n) puts("error code=" #n)
int main()
{
int a = 200;
err(100);
err(a);
return 0;
}
#define err(n) puts("error code=" #n)
int main()
{
int a = 200;
err(100);
err(a);
return 0;
}
error code=100
error code=a
error code=a
C言語プリプロセッサ | 2024.07.11 |
A ## B トークン連結 |
##は、前後のトークンを連結して置き換える。前後の連結なので、##を定義の先頭や末尾に配置できない。
#include <stdio.h>
#define LOG(f1, f2, s) f1##_##f2(s)
void stdo_log(char *msg)
{
puts(msg);
}
int main()
{
LOG(stdo, log, "Hello");
return 0;
}
#define LOG(f1, f2, s) f1##_##f2(s)
void stdo_log(char *msg)
{
puts(msg);
}
int main()
{
LOG(stdo, log, "Hello");
return 0;
}
##がトークンを連結するときの手順は、
① ##の前後をつなぐ
② その結果が更にマクロ展開できる部分があるなら展開する
② その結果が更にマクロ展開できる部分があるなら展開する
なので、次のVERSIONは210になることを期待したいが「MJMN」になる。
#define MJ 2
#define MN 10
#devine VERSION MJ##MN
#define MN 10
#devine VERSION MJ##MN
C言語プリプロセッサ | 2024.07.11 |
#if #elif #else #endif 条件付きコンパイル |
#ifや#elifの条件式は、
① 評価が0なら区間をコンパイルしない、0以外ならする。
② 右辺が整数定数になる比較演算と論理演算ができる。
② 右辺が整数定数になる比較演算と論理演算ができる。
#include <stdio.h>
#define ENABLED 10
#define VER 350
int main()
{
#if ENABLED
printf("Enabled\n");
#endif
#if VER >= 400
printf("version %4.2f\n", ((double)VER)/100);
#elif VER < 400 && VER >= 350
printf("version %4.2f\n", ((double)VER)/100);
#elif VER == 100
printf("version 1.00\n");
#else
printf("beta %3.2f\n", ((double)VER)/100);
#endif
return 0;
}
#define ENABLED 10
#define VER 350
int main()
{
#if ENABLED
printf("Enabled\n");
#endif
#if VER >= 400
printf("version %4.2f\n", ((double)VER)/100);
#elif VER < 400 && VER >= 350
printf("version %4.2f\n", ((double)VER)/100);
#elif VER == 100
printf("version 1.00\n");
#else
printf("beta %3.2f\n", ((double)VER)/100);
#endif
return 0;
}
C言語プリプロセッサ | 2024.07.11 |
#indef #ifndef 定義で判断する条件コンパイル |
#ifdef、#ifndefは、続くマクロの定義が存在するかどうかでコンパイル可否を決める。#ifのような0と非0の判断ではなく、どんな設定値でも設定値がなくても定義があるかないかで判断する。定義は#defineによるもの以外に、コンパイルオプションの-Dで指定される場合がある。
#include <stdio.h>
#define ENABLE
int main()
{
#ifdef ENABLE
printf("Enable\n");
#endif
#ifndef DISABLE
printf("not Disable\n");
#endif
return 0;
}
#define ENABLE
int main()
{
#ifdef ENABLE
printf("Enable\n");
#endif
#ifndef DISABLE
printf("not Disable\n");
#endif
return 0;
}
$ gcc -o test -DDEBUG test.c
$ ./test
Enable
Debug
not Disable
$ ./test
Enable
Debug
not Disable
C言語プリプロセッサ | 2024.07.11 |
#if defined 条件付きコンパイル |
defined(マクロ)は、マクロ定義が存在する場合に0以外、定義が存在しない場合に0を返し、!define()はその否定を返す。#if defined()、#if !defined()は、#ifdefと#ifndefの代替になる。
#include <stdio.h>
#define ENABLE
int main()
{
#if defined(ENABLE)
printf("Enable\n");
#endif
#if !defined(DISABLE)
printf("not Disable\n");
#endif
return 0;
}
#define ENABLE
int main()
{
#if defined(ENABLE)
printf("Enable\n");
#endif
#if !defined(DISABLE)
printf("not Disable\n");
#endif
return 0;
}
defined()のマクロ名は()を使わずにdefinedに続けて書いてもよい。
#if defined ENABLE
#endif
#elif !defined DISABLE
#endif
#endif
#elif !defined DISABLE
#endif
C言語プリプロセッサ | 2024.07.11 |
#undef マクロ定義消去~再定義 |
#undefはすでに存在するマクロ定義を未定義にする。存在しない定義の場合は無視する。ヘッダファイルの中で既に定義されている可能性のあるマクロは#undefで破棄してから再定義する。
#include <stdio.h>
#include <string.h>
#define BUFSIZE 256
#undef BUFSIZE
#define BUFSIZE 16
int main()
{
#define CPY(a, b) memcpy(a, b, sizeof(a))
char buf[BUFSIZE];
CPY(buf, "Hello");
#undef CPY
#define CPY(a, b) strcpy(a, b)
CPY(buf, "Hello");
return 0;
}
#include <string.h>
#define BUFSIZE 256
#undef BUFSIZE
#define BUFSIZE 16
int main()
{
#define CPY(a, b) memcpy(a, b, sizeof(a))
char buf[BUFSIZE];
CPY(buf, "Hello");
#undef CPY
#define CPY(a, b) strcpy(a, b)
CPY(buf, "Hello");
return 0;
}
C言語プリプロセッサ | 2024.07.11 |
__FILE__ __LINE__ など 定義済みマクロ定数 |
それぞれ、__FILE__(ファイル名)、__LINE__(行番号)、__func__(現在の関数名)を格納している。
__DATE__と__TIME__は「コンパイル時」の日付時刻である(実行時ではない)。
__DATE__と__TIME__は「コンパイル時」の日付時刻である(実行時ではない)。
#include <stdio.h>
#define TRACE() \
printf("trace %s %s (%d)\n", \
__FILE__, __func__, __LINE__);
void the_func()
{
TRACE()
}
int main()
{
printf("update %s %s\n", __DATE__, __TIME__);
the_func();
TRACE()
return 0;
}
#define TRACE() \
printf("trace %s %s (%d)\n", \
__FILE__, __func__, __LINE__);
void the_func()
{
TRACE()
}
int main()
{
printf("update %s %s\n", __DATE__, __TIME__);
the_func();
TRACE()
return 0;
}
update Jul 13 2024 18:50:26
trace test04.c the_func (10)
trace test04.c main (17)
trace test04.c the_func (10)
trace test04.c main (17)
※ | __func__はC90から導入された。 |
C言語プリプロセッサ | 2024.07.11 |
__STDC__ __STDC_VERSION__ Cバージョンを示す定数 |
__STDC__はANSI準拠なら1を返す。
__STDC_VERSION__は準拠バージョンによって以下のlong整数に置き換わる。
__STDC_VERSION__は準拠バージョンによって以下のlong整数に置き換わる。
C95 = 199409L
C99 = 199901L
C11 = 201112L
C17 = 201710L
C99 = 199901L
C11 = 201112L
C17 = 201710L
※ | コンパイルオプションに-std=c11のように指定すると__STDC_VERSION__がそれに応じた値になる。 |
#include <stdio.h>
int main()
{
#if __STDC__
printf("This is ANSI C %ld\n", __STDC_VERSION__);
#endif
return 0;
}
int main()
{
#if __STDC__
printf("This is ANSI C %ld\n", __STDC_VERSION__);
#endif
return 0;
}
This is ANSI C 201710
C言語プリプロセッサ | 2024.07.11 |
#line 行番号/ファイル名の途中変更 |
#lineは行番号とファイル名を変更する。
#line 新行番号 [新ファイル名]
新ファイル名を省略した場合は、行番号のみ変更する。#lineが現れた時点のソースコードから下について変更が適用される。
#include <stdio.h>
void the_func()
{
printf("%s %d\n", __FILE__, __LINE__);
}
int main()
{
the_func();
#line 100 "other.c"
printf("%s %d\n", __FILE__, __LINE__);
the_func();
return 0;
}
void the_func()
{
printf("%s %d\n", __FILE__, __LINE__);
}
int main()
{
the_func();
#line 100 "other.c"
printf("%s %d\n", __FILE__, __LINE__);
the_func();
return 0;
}
test.c 5
other.c 100
test.c 5
other.c 100
test.c 5
C言語プリプロセッサ | 2024.07.11 |
#error #warning エラー中断と警告 |
#errorはその場でエラーとしてコンパイルを中断し、#warningは警告を表示してコンパイルを続行する。どちらも続けてエラーや警告の内容を書くことができる。
#include <stdio.h>
#define VER 90
int main()
{
#ifndef VER
#error version not set
#elif VER < 100
#warning beta version
#endif
printf("Hello\n");
return 0;
}
#define VER 90
int main()
{
#ifndef VER
#error version not set
#elif VER < 100
#warning beta version
#endif
printf("Hello\n");
return 0;
}
C言語プリプロセッサ | 2024.07.11 |
重複include防止 |
同じヘッダファイルを重複してincludeすることを防止できる。
#ifndef HDRNAME_H
#define HDRNAME_H
struct ST {
};
#endif /* HDRNAME_H */
#define HDRNAME_H
struct ST {
};
#endif /* HDRNAME_H */
C言語プリプロセッサ | 2024.07.11 |
#pragma once 重複include防止 |
ヘッダファイルの先頭に#pragma onceを置くと、そのヘッダが多重にincludeされないようになる。
#pragma once
struct ST {
};
struct ST {
};
C言語プリプロセッサ | 2024.07.11 |
#if 0 コメントアウト |
#if 0~#endifの間はコンパイルされないので、無効にしたい区間のコメントアウトになる。#if 1にすると有効になるので#elseと組み合わせて使いたい区間を交互に切り替えることができる。
#include <stdio.h>
int main()
{
#if 0
This software may be distributed under the terms of the BSD license.
See README for more details.
#else
printf("Hello\n");
#endif
return 0;
}
int main()
{
#if 0
This software may be distributed under the terms of the BSD license.
See README for more details.
#else
printf("Hello\n");
#endif
return 0;
}
C言語プリプロセッサ | 2024.07.11 |
#pragma pack 構造体アライメント調整 |
#pragmaのpack()は、構造体のアライメントのバイト数を変更できる。pack(1)とした場合、構造体の定義をバイト単位で直列化した状態でメモリに配置される。
#pragma pack(境界バイト数)
#include <stdio.h>
#pragma pack(1)
struct ST1 {
char buf[13];
int a;
};
#pragma pack(4)
struct ST2 {
char buf[13];
int a;
};
int main()
{
printf("pack 1 size=%lu\n", sizeof(struct ST1));
printf("pack 4 size=%lu\n", sizeof(struct ST2));
return 0;
}
#pragma pack(1)
struct ST1 {
char buf[13];
int a;
};
#pragma pack(4)
struct ST2 {
char buf[13];
int a;
};
int main()
{
printf("pack 1 size=%lu\n", sizeof(struct ST1));
printf("pack 4 size=%lu\n", sizeof(struct ST2));
return 0;
}
pack 1 size=17
pack 4 size=20
pack 4 size=20
また、pack()にpushとpopを指定することで一時的に変更してデフォルトに戻す操作ができる。
#pragma pack(push, 境界バイト数)
#pragma pack(pop) デフォルトに戻す
#pragma pack(pop) デフォルトに戻す
#include <stdio.h>
#pragma pack(push, 1)
struct ST1 {
char buf[13];
int a;
};
#pragma pack(pop)
struct ST2 {
char buf[13];
int a;
};
int main()
{
printf("pack 1 size=%lu\n", sizeof(struct ST1));
printf("pack 4 size=%lu\n", sizeof(struct ST2));
return 0;
}
#pragma pack(push, 1)
struct ST1 {
char buf[13];
int a;
};
#pragma pack(pop)
struct ST2 {
char buf[13];
int a;
};
int main()
{
printf("pack 1 size=%lu\n", sizeof(struct ST1));
printf("pack 4 size=%lu\n", sizeof(struct ST2));
return 0;
}
※ | この例ではデフォルトのアライメントはintの4バイトになる |