コンパイル

Intel C++ Compiler

Intel C++ Compilerとは、インテルが開発及び販売しているコンパイラである。実行に必要なライブラリやリンカなどは付属していないため、他のコンパイラと組み合わせて使用する。WindowsではMicrosoft Visual C++、LinuxではGCCが必要である。

識別子

変数や関数、記号定数の名前のことを識別子と呼びます。識別子は自由に命名できますが、以下の制約があります。

予約語

予約語とはC言語において固有の意味を持つ単語です。そのため、予約語は識別子として用いることができません。C言語の予約語を以下に示します。

変数

変数とは、プログラムで扱われる数値や文字列などの値を一時的に記憶しておく領域のことです。変数を識別するために、ひとつひとつの変数に固有の名前を付けておきます。これを変数名といいます。

変数を使用する前に、どのような変数を使用するか、その変数名や変数に記憶できるデータ型を明らかにしておくことを変数の宣言といいます。

C 言語で変数を宣言する書式を次に示します。

[ 記憶クラス ] データ型 変数名 [ = 初期値 ] ;

記憶クラスおよび初期値は省略することができます。

記憶クラスおよびデータ型が同じ変数は、カンマで区切って複数宣言することができます。

[ 記憶クラス ] データ型 変数名 [ = 初期値 ] , 変数名 [ = 初期値 ] , 変数名 [ = 初期値 ] ;

変数の記憶クラス

記憶クラスとは、宣言する変数をどこに記憶するかを指定します。

自動変数

自動変数はスタック領域に変数の領域が確保される。自動変数を宣言するには、記憶クラスに auto を指定する。

auto int n;

変数を宣言する際、記憶クラスの指定を省略した場合は自動変数となる。

int n;

自動変数を宣言する際、明示的に初期化を行わなければ、初期値は不定となる。

静的変数

静的変数は、プログラム実行中に常に同じ場所に配置され、値を保持します。

静的変数を宣言するには、記憶クラスに static を指定します。静的変数を宣言する例を次に示します。

static int n;

静的変数を宣言する際、明示的に初期化を行わなければ、初期値は0になります。

記憶クラス 初期値
自動変数 不定
静的変数 0

レジスタ変数

変数の領域が確保される物理的な記憶装置は主記憶装置である。主記憶装置よりもCPU内のレジスタの方が高速にアクセスできるため、変数の領域を確保する物理的な記憶装置をレジスタに指定した変数がレジスタ変数である。ただし、可能な限りレジスタに確保するよう要求するだけで、必ずしもレジスタに確保される保障はない。

レジスタ変数を宣言するには、記憶クラスに register を指定する。

register int n;

データ型

基本データ型

C 言語の基本データ型には次のような種類があります。

char
8ビット符号付き整数型
short int
16ビット符号付き整数型
short
short intと同じ
int
16ビットまたは32ビット符号付き整数型
long int
32ビットまたは64ビット符号付き整数型
long
long intと同じ
long long
64ビット符号付き整数型 (Solaris)
enum
列挙型
float
単精度浮動小数点数型 (32ビット)

float 型では単精度浮動小数点演算ができます。

float f1, f2, f3;
f1 = f2 + f3;

旧仕様 (K&R) では次のような順番で演算されました。

  1. 両オペランド(f2とf3)を倍精度化
  2. 倍精度(double型)で演算
  3. 倍精度の結果を単精度化
  4. 単精度の結果を f1 に格納

しかし、ANSI ではすべて単精度で演算されます。

double
倍精度浮動小数点数型 (64ビット)
long double
128ビット浮動小数点数型 (Solaris)
void
void は引数または戻り値が無いことを表す特殊なデータ型です。たとえば、引数を取らない関数は次のように定義します。
int getchar(void);

戻り値を返さない関数は次のように定義します。

void exit(int status);

void * は汎用の(型に依存しない)ポインタ型を表します。たとえば、汎用のポインタ型を引数に取る関数は次のように定義します。

void free(void *ptr);

汎用のポインタ型を返す関数は次のように定義します。

void *malloc(size_t size);

汎用型ポインタは型を特定することができないため、ポインタが指し示す値を直接参照することができません。他のポインタ型に変換してから参照します。

void *p;
int *i;

p = malloc(sizeof(int));
*p = 0; /* NG */
i = p;
*i = 0; /* OK */

void ** のようにvoid型のポインタへのポインタにすることはできません。

派生データ型

基本データ型の他にシステムコールやライブラリ関数で使用される派生データ型があります。 実際は何らかの基本データ型なのですが、移植性を高めるために別の名前が付けられています。

派生データ型はマクロで定義されているため、ヘッダーファイルをインクルードする必要があります。派生データ型によってインクルードするヘッダーファイルの名前は異なりますが、たいていの派生データ型は sys/types.h をインクルードすることで使用できるようになります。

派生データ型には、次のようなものがあります。

clock_t
システムの時間をクロック刻み (clock tick) で表す型
dev_t
デバイス番号に使用される型
off_t
ファイルサイズとオフセット(先頭からの相対位置)用に使用される型
ptrdiff_t
2つのポインタの減算結果を示す符号付き整数型
size_t
メモリー内のオブジェクトのサイズ(バイト単位)用に使用される型
ssize_t
バイト数またはエラーのどちらを返すこともある関数によって使用される符号付きサイズ型
time_t
秒単位の時間用に使用される型
intptr_t
ポインタを格納するために十分なサイズの符号付き整数型 (Solaris)
uintptr_t
ポインタを格納するために十分なサイズの符号なし整数型 (Solaris)

union (共用体)

union (共用体)は、同一のデータ領域を複数の異なるデータ型が共用するようにしたものです。

union タグ名 {
  変数宣言のリスト
};

以下の場合には、タグ名を省略することが可能です。

変数宣言のリストの中に共用体を入れて、入れ子構造にすることも可能です。

共用体内の最初のメンバの初期化が可能です。

union x {
  char a[4];
  int  b;
};

union x ux = { 'U', 'N', 'I', 'X' };

typedef

typdefは、既存の型に対して新しい名前を付けるときに使用します。

typedef 型名 新しい型名;

新しい型名を付けると、既存の型名のように使用することができます。

typedef unsigned int BYTE;
BYTE data;

この例は、次のように変数を宣言するのと同じです。

unsigned int data;

型名の代りに、構造体や共用体の宣言を指定することもできます。

typedef struct {
  変数宣言のリスト
} 新しい型名;
typedef union {
  変数宣言のリスト
} 新しい型名;

これらは次のように記述することもできる。

struct tag_name {
  変数宣言のリスト
};

typedef struct tag_name 新しい型名;
union tag_name {
  変数宣言のリスト
};

typedef union tag_name 新しい型名;

修飾子

修飾子はデータ型を修飾します。

const

const は定数を表す修飾子です。例として strcpy 関数のプロトタイプ宣言を見てみましょう。

char *strcpy( char *string1, const char *string2 );

strcpy 関数は文字列 string2string1 へコピーする関数です。 string2 はコピー元の領域であり、コピー後も内容は変化しません。 strcpy 関数にとっては入力パラメーターであり、定数であるため const 修飾子が付いています。 一方、 string1 はコピー先の領域であり、コピー後は内容が変化します。 strcpy 関数にとっては出力パラメーターであり、変数であるため const 修飾子が付いていません。

volatile

volatile修飾子は最適化を抑制するオブジェクトを宣言します。

volatile int comm = 1;
while (1) if (comm == 0) break;

もし volatile 修飾子が無ければ、Cコンパイラーにより最適化が行われます。 ソースプログラムではループの中で変数が0に等しいかどうか比較していますが、変数が0になることは無いのでCコンパイラーの最適化機能で無駄な比較が削除されることがあります。 このため無条件で無限ループとなります。 volatile 修飾子があればコンパイラーによる最適化が行われないため、(他のプロセスから値を変更するなどして)変数 comm の値が0になるとループを脱出します。 これは、タスク間の同期制御に利用できます。

標準Cライブラリ関数

gettimeofday, settimeofday - 日付と時刻の取得/設定

gethrtime, gettrvtime - 高精度時刻の取得

文字列

#include <string.h>
char *strtok(char *s1, const char *s2 );

引数s1で指定した文字列の中に引数s2で指定した区切り文字(文字列形式で複数種類の文字を指定可能)が存在すれば、全てNUL文字('\0')に置き換えます。

つまり、1つの文字列を区切り文字によって複数の文字列に分割します。この分割されたそれぞれの文字列をトークンといいます。

例えば、以下のような文字列 s1 があったとします。

'A' 'B' ',' 'C' 'D' ',' 'E' 'F' '\0'

strtok(s1, ",") を実行すると、文字列 s1 は次のようになります。

'A' 'B' '\0' 'C' 'D' '\0' 'E' 'F' '\0'

また、strtokは最初のトークン ("AB") の先頭の文字 ('A') のポインタを返します。

次に、strtok(NULL, ",") を実行すると、strtokは次のトークン ("CD") の先頭文字 ('C') のポインタを返します。

次にstrtok(NULL, ",") を実行すると、strtokは次のトークン ("EF") の先頭文字 ('E') のポインタを返します。

最後のトークンの先頭文字のポインタを返した後にstrtok(NULL, ",") を実行すると、strtokは戻り値として NULL を返します。

時刻

現在の時刻を取得するには、time() 関数を使用します。

#include <sys/types.h>
#include <time.h>

time_t time(time_t *tloc);

time_t は sys/types.h の中で typedef により unsigned int または unsigned long として定義されています。

引数 tlocには time_t 型の変数のポインタを指定します。time() 関数により、tloc で指定した time_t 型の変数の中に1970年1月1日0時0分0秒からの経過秒数が格納されます。

戻り値には 1970年1月1日0時0分0秒からの経過秒数が返されます。

tloc に格納される値と time() 関数の戻り値は同じです。引数に NULL を指定することもできます。

time_t t;
t = time(&t);
time_t t;
t = time(NULL); /* 引数にNULL指定 */
time_t t;
time(&t); /* 戻り値は無視 */

時刻を表すデータ型には、time_tstruct tm があります。これらを相互に変換したり、文字列に変換する時刻関数群が用意されています。これらの時刻関数の相関図を次に示します。

時刻関数相関図

たとえば、time() 関数で取得した現在の時刻(time_t型)を localtime() 関数を用いて struct tm 型に変換することができます。time_t 型の時刻は、ctime() 関数を用いて文字列に変換することができます。

#include <time.h>

char *ctime(const time_t *clock);

struct tm *localtime(const time_t *clock);

struct tm *gmtime(const time_t *clock);

char *asctime(const struct tm *tm);

乱数

C言語で乱数を生成するには、次の関数を使用します。

#include <stdlib.h>

int rand(void);

void srand(unsigned int seed );

rand() 関数は 0 から RAND_MAX までの乱数を返します(RAND_MAX はヘッダファイル stdlib.h の中で定義されています)。

ただし、あらかじめ決められた乱数表から順番に値を返しているので、完全にランダムな数を生成しているわけではありません。これは、rand() 関数の戻り値を printf() 関数で出力するプログラムを作成すれば確認できます。実行するたびに毎回同じ数値が出力されるはずです。

そこで乱数の初期化が必要です。乱数の初期化とは、乱数表を先頭からではなく別のところから読み出すようにすることでです。乱数の初期化は srand() 関数で行います。

引数 seed には初期値を指定します。通常は time() 関数の戻り値を指定します。プログラムを実行するたびにそのときの時刻は変わるので、プログラムを実行するたびに異なる乱数が発生するようになります。

#include <time.h>
#include <sys/types.h>
#include <stdlib.h>

int i;

srand(time(NULL));
i = rand() % 100;

rand 関数は 0 から RAND_MAX までの乱数を返します。これを 0 から 99 までの乱数にしたいときは、100 で割った余りを使います。

関数

関数の定義

type name([ptype parameter[, ptype parameter...]])
{
 // 処理
}

type
関数の戻り値の型
name
関数の名前
ptype
仮引数の型
parameter
仮引数の名前

関数の呼び出し

name([argument[, argument...]])

name
関数の名前
argument
実引数

関数で可変個数の引数を扱う

printf()scanf() では、引数の数や型が不定です。これらの関数のように、引数の個数が可変である関数の作成方法を説明します。

引数の個数が可変の関数を作成するには、ヘッダファイル stdarg.h をインクルードします。

関数の定義は次のようにします。

関数名( 変数名 , ... )

可変個数の引数は "..." で表します。引数を "..." で表すと、コンパイル時に引数の個数をチェックしなくなります。ただし、可変個数でない引数が最低ひとつは必要です。引数が全て可変個数の引数の関数は作成できません。したがって、次のような関数を定義することはできません。

関数名( ... )

最初の引数はどんなものでも構いませんが、通常は可変引数の個数や型を示すものが普通です。

関数内では、可変引数を扱うために va_list 型の変数を宣言します。

va_list 変数名 ;

可変引数の扱いを開始するには、va_start マクロを呼び出します。

void va_start( va_list 変数1 , void 変数2 );

変数1 には、va_list 型の変数を指定します。変数2 には、不定引数になる前の最後の固定引数を指定します。

可変の変数を取り出すには va_arg を使用します。

va_arg( va_list 変数名 , );

可変引数を扱う必要がなくなったら va_end マクロを呼び出します。

void va_end( va_list 変数名 );

可変個数の引数を扱う関数は何らかの方法で引数の数と型を特定しなければなりません。その方法として、次のようなものが考えられます。

型はあらかじめ決まっていて、引数の数を固定引数で渡す

可変個数の引数を扱う関数の例を次に示します。

#include <stdio.h>
#include <stdarg.h>

void print_varargs(int n, ...)
{
        va_list pvar;
        char *p;

        va_start(pvar, n);
        for (; n > 0; n--) {
                p = va_arg(pvar, char *);
                printf("%s\n", p);
        }
        va_end(pvar);
}

void main(void)
{
        print_varargs(1, "hello");
        print_varargs(2, "hello", "world");
}

上記の例では、型は char * の固定で、引数の個数を第1引数で与えています。

引数の数と型を固定引数で渡す

printf() でも最初の引数は "%d %s" のように引数の型や個数を表していますね。

型はあらかじめ決まっていて、特定の値が現れるまで引数を取り出す

UNIXのシステムコール execl(), execle(), execlp() のように、特定の値が現れるまでパラメータを取り出します。

可変引数の型が char * で、NULL が現れるまで引数を取り出す例を次に示します。

#include <stdio.h>
#include <stdarg.h>

void print_varargs(int dummy, ...)
{
        va_list pvar;
        char *p;

        va_start(pvar, dummy);
        while ((p = va_arg(pvar, char *)) != NULL) {
                printf("%s\n", p);
        }
        va_end(pvar);
}

void main(void)
{
        print_varargs(0, "hello", NULL);
        print_varargs(0, "hello", "world", NULL);
}

引数の数を渡すのではなく、最後の引数であるという目印として特定の値を決めておく方法です。ただし、最後の引数であることを表す値(上記の例では NULL)を通常値として渡すことはできません。

va_list型を直接扱う

va_list 型の変数を直接扱える関数もあります。

#include <stdio.h>
#include <stdarg.h>

int vprintf(const char *format, va_list ap);

int vfprintf(FILE *stream, const char *format, va_list ap);

int vsprintf(char *s, const char *format, va_list ap);

int vsnprintf(char *s, size_tn, const char *format,  va_list ap);

上記の関数を使えば、引数をひとつひとつ取り出すことなくストリームに出力することができます。

#include <stdio.h>
#include <stdarg.h>

void print_varargs(char *format, ...)
{
        va_list pvar;

        va_start(pvar, format);
        vprintf(format, pvar);
        va_end(pvar);
}

void main(void)
{
        print_varargs("%s\n", "hello");
        print_varargs("%s %s\n", "hello", "world");
}

X/Open XA

X/Open XAとは、分散トランザクション処理のための標準規格であり、X/Openが策定した。

X/Open XAでは、アプリケーションソフトと、トランザクションマネージャ、リソースマネージャの3者を規定しており、それぞれが果たす役割や、指示を発行するための通信手順(インタフェース)などを定めている。

X/Open XA 概念図

アプリケーションソフト

アプリケーションソフトは、トランザクションマネージャと通信し、トランザクションマネージャにトランザクション群の開始やコミット、ロールバックを指示する。

また、アプリケーションソフトは、個々のリソースマネージャと独自のAPIで通信し、リソースの更新などを行う。

トランザクションマネージャ(TPモニター)

トランザクションマネージャは、トランザクションの状態を管理して、複数のリソースマネージャ間の分散トランザクションの一貫性を保つ役割を果たす。トランザクションマネージャはTPモニターとも呼ばれる。

リソースマネージャ

リソースマネージャは、データ等の資源を管理する役割を果たす。代表的なリソースマネージャはリレーショナルデータベース管理システムである。

リソースマネージャは、リソースを提供するための独自のAPIを持つ。たとえば、リレーショナルデータベース管理システムではJDBC APIなどが使われる。

TXインタフェース

TXインタフェースとは、アプリケーションソフトとトランザクションマネージャの間の通信手順を定めたものである。

XAインタフェース

XAインタフェースとは、トランザクションマネージャとリソースマネージャの間でデータや指示のやり取りを行なう際の通信方法を定めたものである。

XAインタフェースに準拠した製品であれば、異なるメーカのトランザクションマネージャとリソースマネージャを連携させることも理論上は可能である。

スポンサーリンク