INDEX
昔書いた「C言語 プログラミング入門」より
C言語概要
C言語は元々、UNIXとよばれるOSを開発する目的で作られました。UNIXは、1969年頃、AT&Tベル研究所というところで、Ken Tompsonnや、Dennis Ritchieらによって作られました。
初めの内、UNIXはアセンブラと呼ばれる低レベルのプログラム言語で作られていました。しかし、UNIXを高級言語で記述したいという要求から、Dennis Ritchieらによって、C言語が作られました。そして1973年頃には、UNIXはC言語によって記述されるようになりました。
その頃のCコンパイラは、PDP-11というマシンでしか使えませんでした。その後、Steve Johnsonという人の作った、Portable C Compilerが登場しました。このコンパイラは、名前の通り(Portableは、持ち運べるの意)で移植性に優れ、他の多くのマシンでCコンパイラが使われるようになりました。
その後、安価なパソコンでも使えるCコンパイラが、たくさん出回るようになり、1989年には前述のANSIによる標準化も行われ、今に至っています。
1983年には、同じくAT&Tベル研究所のBjarne Stroustrupによって、C++ (一般的にシープラプラと読む) という言語が開発されました。最近よく耳にするオブジェクト指向という概念を取り入れて、C言語を拡張したものです。C++はオブジェクト指向言語としては賛否両論がありますが、既存のCプログラムがそのまま全て使えることから、瞬く間に広がりました。VisualC++、BolandC++を初めとして、数々のC++コンパイラが世に出ています。
そしてC++も1996年現在、ANSIによって標準化作業が進められています(実は、C言語の標準化も再び行われています)。
さらに1995年5月、SunMicrosystemsのJames Goslingら(開発チームにはBill Joyというビッグネームもいました)によって、Java言語と呼ばれるものが開発されました。このC++によく似た言語仕様を持つJava言語は、インターネットのWWW上で動的なプログラムが作れるとあって瞬く間に広がっています。
Java言語は、C++からオブジェクト指向的な部分(つまりC言語でない部分)ばかりを取り出した言語と言えましょう。しかし、C言語に似た構文も少なからず残っており、C言語を理解していればJavaは容易に習得できるはずです。
C言語プログラム
プログラムの基本原則
C/C++言語のプログラムは、識別子・予約語・定数・演算子・区切り記号の5つの語句で一つの文や式が構成され、一つの文の終わりにはセミコロン(;)を記述する。
各語句の区切りとなる区切り記号は、ホワイトスペース、コメント(注釈)である。これらはすべて同じ扱いとなる。また、この区切り記号は、一つの語句を分断しない限りは、適当な位置に、適当な数だけ記入することが出来る。さらに、区切り記号として改行も許されているので、文や式を複数の行にまたがって書くことも出来る。
このように、C/C++は、一つの文をどこから書き始めても、どこで終わっても良く、「行」にとらわれない(プリプロセッサ、行コメント等の例外は存在するが)自由な記述が可能である。このことを、フリーフォーマットと言う。適当な字下げや空白等を利用し、読みやすく理解しやすいプログラムを作ることできる。
プログラムの処理を1つ1つ表しているものが文である。C言語の文法で言う文とは、以下の用のものをさす。
- 式
- 代入演算子や関数呼び出しなど、比較式も式の一つ。
- 単文
- 代入演算子や関数呼び出しなどの式の後ろにセミコロン(;)をつけることによって、それは単文となる。
- 複文
- 単文をいくつかまとめて、それらを中括弧({,})で囲みブロックにまとめたものを複文と言う。また、関数内の処理を表す部分も、中括弧で囲まれるブロックであるから、複文の一つになる。複文は、文法的には単文と同等に扱われる。ただし、複文の終わりを示す右中括弧(})には、セミコロン(;)はつけない。
識別子
識別子とはプログラムの中で付けられる名前、例えば、変数名、関数名、構造体等のタグ名のことを指す。
識別子は、次の規則に従います。(ただし、コンパイラ等によって異なるところもある)
- 使用できる文字は、英字(A〜Z, a〜z)、数字(0〜9)、下線( _ )のみ
- 先頭の1文字目は、数字以外
- 大文字と小文字は、別の文字として区別される
- 文字数は無制限 (コンパイラにより有効文字数がある)
- 予約語は、識別子として利用することは出来ない
IBMの汎用機 OS/390 では、頭8文字までが有効となる。
NECの汎用機 ACOS-4 では、先頭16文字までが有効になる。また、大文字・小文字の区別もない。
予約語
予約語(キーワード)は、C言語での用途があらかじめ定められているので、識別子として利用は出来ない。これらは以下に示すようにC言語のデータ型や制御構造として利用される。
| auto | break | case | char | const | continue | default | do |
| double | else | enum | extern | float | for | goto | if |
| int | long | register | return | short | signed | sizeof | static |
| struct | switch | typedef | union | unsigned | void | volatile | while |
C++では、以下の予約語も追加されている。
| new | delete | opeleter |
コンパイラによっては、それぞれに拡張された予約語が存在する。
定数
定数は、プログラム中に直接値を書き込み、プログラム実行中にその値が変ることのない数値や文字のことを指す。また、定義した定数は、必ずメモリ上のどこかに保存されます。
整数定数
- 8進数定数
- 数値の「0」で始まる数値の列
- 16進数定数
- 「0X」もしくは「0x」で始まる数値の列
- 10進数定数
- 先頭が「0」以外の数字、もしくは符号で始まる数値の列
これらの定数は、通常signed int型でメモリ上に確保される。明示的にlong型で確保する場合は、整数定数の最後に「L」または「l (小文字のL)」を付ける。また、unsignedにする場合も、最後に「U」または「u」を付ける。
浮動小数点定数
小数点を含む10進数を記述する。書式は、「 〔符号〕〔整数部〕〔.小数部〕〔E〔符号〕指数部〕 」となる。整数部と小数部のいずれかは省略可能。指数部を示す「E」は小文字も可能。
これらの定数は、通常doubl型でメモリ上に確保される。明示的にfloat型で確保する場合は、整数定数の最後に「F」または「f」を付ける。
列挙定数
データ型と宣言の列挙型を参照
文字定数
シングルクォーテーション(単一引用符)(’)で囲まれた1文字の文字。特殊な文字定数としてエスケープ文字も含まれる。内部的には数値(ASCIIコード等の文字コード)としてchar型でメモリに保持される。
リテラル文字列
ダブルクォーテーション(二重引用符)(”)で囲まれた文字の列。文字列の最後には、自動的に文字列の終端を示すNULL文字が付加される。
エスケープ文字
円記号(\)、もしくは、バックスラッシュ(\) と、それに続く文字や数字の組み合わせで、出力できないような特殊な文字を表したり、文字を文字コードで表したりする。
| \a | ベル | \b | ックスペース | \? | リテラルクォーテーション | ||
| \n | 復帰改行 | \r | 改行 | \' | シングルクォーテーション | ||
| \t | 水平タブ | \v | 垂直タブ | \" | ダブルクォーテーション | ||
| \f | 改ページ | \\ | 円記号 | \ddd | 8進数表記によるASCII文字 | ||
| \0 | NULL | \xdd | 16進数表記によるASCII文字 |
コメント
「/*」と「*/」で囲まれた部分は、コメント(注釈)として扱われ、その間の文字列(改行も含む)は、プログラムの実行時には、影響を与えない。但し、コメントを複数行にわたって書くことは出来るが、ネスト(入れ子)は、行うことは出来ない。
最近のコンパイラは、行コメント(「//」から行末までをコメントにする)をサポートしているものもある。
ホワイトスペース
スペース(空白)、タブ、改行(復帰改行)文字、改ページ文字のことをさし、プログラム中の識別子等を分離する区切り記号になる。
演算子
演算子の種類
豊富な演算子があると言うことが、C言語の一つの特徴である。一般的な算術演算子や関係演算子に加えて、ビット操作を行うビット演算子などがある。
算術演算子
| 演算子 | 意味 | 例 |
|---|---|---|
| + | 加算 | x = y + z; |
| - | 減算 | x = y - z; |
| * | 乗算 | x = y * z; |
| / | 除算 | x = y / z; |
| % | 剰余 | x = y % z; |
ビット演算子
| 演算子 | 意味 | 例 |
|---|---|---|
| & | ビットごとのAND | x = y & z; |
| | | ビットごとのOR | x = y | z; |
| ^ | ビットごとのXOR | x = y ^ z; |
| ~ | 1の補数 | x = y ~ z; |
| << | 左シフト | x = y << z; |
| >> | 右シフト | x = y >> z; |
代入演算子
| 演算子 | 意味 | 例 |
|---|---|---|
| = | 代入 | x = 10; |
| += | 加算 | x += y; /* as x = x + y; */ |
| -= | 減算 | x -= y; /* as x = x - y; */ |
| *= | 乗算 | x *= y; /* as x = x * y; */ |
| /= | 除算 | x /= y; /* as x = x / y; */ |
| %= | 剰余 | x %= y; /* as x = x % y; */ |
| <<= | 左シフト | x <<= y; /* as x = x << y; */ |
| >>= | 右シフト | x >>= y; /* as x = x >> y; */ |
| &= | ビットごとのAND | x &= y; /* as x = x & y; */ |
| |= | ビットごとのOR | x |= y; /* as x = x | y; */ |
| ^= | ビットごとのXOR | x ^= y; /* as x = x ^ y; */ |
関係演算子
| 演算子 | 意味 | 例 |
|---|---|---|
| == | 等しい | if( x == y ) |
| != | 等しくない | if( x != y ) |
| < | より小さい | if( x < y ) |
| <= | 以下 | if( x <= y ) |
| > | より大きい | if( x > y ) |
| >= | 以上 | if( x >= y ) |
論理演算子
| 演算子 | 意味 | 例 |
|---|---|---|
| && | 論理AND | if( x && y ) |
| || | 論理OR | if( x || y ) |
| ! | 論理NOT | if( !x ) |
条件演算子
| 演算子 | 意味 | 例 |
|---|---|---|
| ? : | 条件演算子 | x = ( y > z ) ? y : z ; |
順次演算子
| 演算子 | 意味 | 例 |
|---|---|---|
| , | 順次演算子 | x = 0, y = 0; |
アドレス/ポインタ演算子
| 演算子 | 意味 | 例 |
|---|---|---|
| & | アドレス | p = &x; |
| * | ポインタ | x = *p; |
単項演算子
| 演算子 | 意味 | 例 |
|---|---|---|
| ++ | 後置インクリメント | x++; |
| 前置インクリメント | ++y; | |
| -- | 後置デクリメント | x--; |
| 前置デクリメント | --y; | |
| + | プラス | +z |
| - | マイナス | -z |
| (type) | 型キャスト | x = (long)y; |
| sizeof | サイズ | z = sizeof(x); |
| . | メンバーの直接参照 | x = y.z; |
| -> | メンバーの間接参照 | x = y->z; |
| ( ) | 関数呼び出し | x = func(y,z) |
| [ ] | 配列要素 | x = y[1]; |
演算子の優先順位
C言語の演算子には、明確な優先順位がある。複数の演算子を使用する場合は、その優先順位に従って処理が行われる。また、同じ優先順位の場合は、各演算子の結合規則に従い評価される。
| 優先順位 | 演算子 | 結合規則 |
|---|---|---|
| 高 | (後置)++ (後置)-- ( ) [ ] -> . | → |
| ! ~ ++(前置) --(前置) + - (type) * & sizeof | ← | |
| * / % | → | |
| + - | → | |
| >> << | → | |
| < <= > >= | → | |
| == != | → | |
| & | → | |
| ^ | → | |
| | | → | |
| && | → | |
| || | → | |
| ? : | ← | |
| = += -= *= /= %= &= |= ^= >>= <<= | ← | |
| 低 | , | → |
データ型と宣言
基本データ型
識別子としてプログラム中で利用する変数は、すべて宣言子によって、その変数の型を定義し、必要とされるメモリ領域の大きさや表現方法を指定する必要がある。変数は、「初期化」または「代入」と言う手段で値が与えられる。
| 型名 | Byte(Bit) | 保証値 |
|---|---|---|
| void | * | * |
| char | 1 (8) | コンパイラ依存 (signed char もしくは unsigned char) |
| signed char | 1 (8) | -127 〜 +127 |
| unsigned char | 1 (8) | 0 〜 +255 |
| signed short int | 2 (16) | -32,767 〜 +32,767 |
| unsigned short int | 2 (16) | 0 〜 +65,535 |
| signed int | * | コンパイラ(CPU)に依存 (shortもしくはlong) |
| unsigned int | * | コンパイラ(CPU)に依存 (shortもしくはlong) |
| signed long int | 4 (32) | -2,147,483,647 〜 +2,147,483,647 |
| unsigned long int | 4 (32) | 0 〜 +4,294967,295 |
| signed long long int | 8 (64) | -9,223,372,036,854,775,807 〜 +9,223,372,036,854,775,807 |
| unsigned long long int | 8 (64) | 0 〜 +18,446,744,073,709,551,615 |
| float | 4 (32) | ±3.4E±38 (約 7桁) |
| double | 8 (64) | ±1.7E±308 (約15桁) |
| long double | 12 (96) [1] | ±1.1E±4932 (約19桁) |
それぞれの基本データ型の最大値,最小値は、LIMITS.H, FLOAT.Hに定義されている。この基本データ型の他に、列挙型、構造体、共用体、ポインタ、配列などがある。
MSVC拡張
_int64, _int32, _int16, _int8 : それぞれのビット数の整数型
long double の bitサイズ
12Byteが標準的だが処理系依存
gccでは、オプションで96bitか128bitが指定できる
データ型の変換
異なるデータ型での演算では、優先度の高いデータ型に合わせて変換し計算が行われる。ただし、代入演算子を使っている場合は、計算結果が左辺のデータ型に変換される。その際にサイズの小さいデータ型に変換されると、切捨てや丸め誤差が生じる。
また、強制的(明示的)に型変換を行う場合は、キャスト演算子を用いる。
型変換の優先順位は、「 char < short < ( int ) < long < float < double 」となる。
型修飾子
型修飾子には 2種類あり、その変数の性質を指定する。
- const
- その値を変更できない変数
- volatile
- 未知の方法でどの時点においてもアクセス可能な変数
記憶クラス
記憶クラスはその変数がメモリのどの場所に確保されるかを指定する。
- auto (自動変数)
- 宣言された関数のみで有効であり、関数を抜けると消滅する。記憶場所は、スタック もしくは、メモリ となる。
- register (レジスタ変数)
- 状況が許す限り、変数をレジスタに格納するように指定する。しかし、使用には制限も多く、レジスタに確保されない場合もある。通常、コンパイラの最適化により、auto 変数も含め配置されるので指定の必要はない。記憶場所は、レジスタ となる。
- static (静的変数)
- プログラムを実行中はその値を保持する。記憶場所は、メモリ となる。
- extern (外部変数)
- 他で宣言された同名の変数の使用を宣言記憶場所は、参照する変数に依存する。
変数スコープ(変数の通用範囲)
変数は宣言される位置や記憶クラスによって、その変数が使用できる有効範囲が異なる。
ローカル - 関数の中 or ブロック内
- auto / (指定無し)
- 宣言された関数(ブロック)内のみで使用可能。関数が呼ばれたとき(制御が移ったとき)に記憶領域が確保され、関数から抜けるときに消滅する。
- register
- 自動変数と同様で、宣言された関数内のみで使用可能。
- static
- 自動変数と同様で、宣言された関数内のみで使用可能。ただし、プログラム実行時に記憶領域が確保され、終了まで永久に存在する。明示的に初期化がされていない場合は、暗黙的に 0 (NULL) で初期化される。
- extern
- 自動変数と同様で、宣言された関数内のみで使用可能。ただし、実際に記憶領域は確保されず、同名(同型)のグローバル変数を参照するにすぎない。
グローバル - 関数の外
- static
- 宣言されたソースファイル内のみで、宣言後以降で使用可能。静的ローカル変数同様に、永久に存在し、暗黙の初期化も行われる。
- (指定無し)
- 宣言されたプログラム内(ソースファイルが違う場合も可)すべてから使用可能。静的ローカル変数同様に、永久に存在し、暗黙の初期化も行われる。
- extern
- 宣言されたプログラム内(ソースファイルが違う場合も可)すべてから使用可能。ただし、同名のグローバル変数を参照するにすぎない。
typedef宣言
typedef宣言は、既存のデータ型の同義語を定義する。
プリプロセッサの「#define」とよく似た機能であるが、#defineはプリプロセッサにより解釈され、typedefはコンパイラにより解釈される。また、単に同義語を作る#defineに比べ、typedefは「型」の同義語を作るという目的が明確である。
typedef 《既存のデータ型》 《新しいデータ型》 ;
列挙型
列挙型は、列挙定数(整数定数)のリストを定義し、int型整数値の並びに、特定の名前を与えるものである。基本的に列挙型の変数には、その列挙型のメンバーしか値をセットすることが出来ない。
プリプロセッサの「#define」に定数を定義して利用するのとよく似た機能である。
enum 《タグ名》 {
《列挙定数》〔 ,
《列挙定数》〔 ,
… 〕 〕
} 〔 《変数名》 〔 = 《列挙定数》 〕 〕 ;
enum 《タグ名》 《変数名》 〔 = 《列挙定数》 〕;
変数の初期化
変数の宣言に続けて、初期値を明示することで、使用する変数をあらかじめ初期化しておくことが出来る。ただし、その変数の記憶クラスによって、初期化のタイミング等が異なる。
- auto / register
- 宣言された関数(複文)に制御が移るたびに毎回初期化される。省略時の値は不定となる。
- static / グローバル変数
- プログラム実行時の最初に初期化される。省略時は、0 で初期化される。
- extern
- 初期化不可能
また、配列や、構造体等を初期化する際に、初期値の数が変数の要素(メンバー)数に満たない場合は、残りは、0 で初期化される。また、共用体は、最初のメンバーに対して初期化が行われる。
制御文
制御文(ステートメント)は、プログラムの実行の流れを規定する。制御文には以下の種類がある。また、制御文での式の評価は、「0の時が偽、0以外の時が真」となる。
if文
if文は、2分岐条件文である。まず、《式》を評価し、その結果が真ならば、《文1》を実行する。また、偽の場合で、elseが存在すれば、《文2》を実行する。《文1》、もしくは、《文2》を実行した後、制御は次の文へ移る。
if ( 《式》 ) 《文1》 〔 else 《文2》 〕
switch文
switch文は、多岐選択条件文である。まず、《式》を評価し、その値を《定数式》と比較する。一致する定数式があれば、そのcaseキーワードに制御を移す。すべて一致するものがなく、defaultキーワードがあれば、そこへ制御を移す。ちなみにC言語のswitch文は、実行すべき文を選択するのではなく、制御を移すべき場所を選択するものである。
switch ( 《式》 ) {
〔 case 《定数式》 :
《文》
… 〕
…
〔 default :
《文》
… 〕
}
while文
while文は、文の反復実行を行う、繰り返し(ループ)文である。まず、《式》を評価し、その結果が偽になるまで《文》を繰り返し実行する。
while ( 《式》 ) 《文》
do-while文
do-while文も、while文同様に繰り返し文である。ただ、while文と異なり、まず、《文》を実行し、その後、《式》を評価し、その結果が偽になるまで繰り返される。
do 《文》 while ( 《式》 ) ;
for文
for文も繰り返し文である。まず、《初期設定式》を実行する。次に、《実行判定式》を評価し、その結果が真ならば、《文》の実行を行う。そして、《再設定式》を行い、《実行判定式》を評価し、その結果が偽になるまで繰り返す。
for ( 〔 《初期設定式》 〕 ; 〔 《実行判定式》 〕 ; 〔 《再設定式》 〕 ) 《文》
break文
break文は、breakを囲んでいる最も内側の do-while, while, for, switch 文を終了し、その制御から抜ける場合に使用する。
break;
continue文
continue文は、continueを囲んでいる最も内側の do-while, while, for の残りの文を実行せず、次の繰り返しを実行する場合に使用する。break文のように繰り返し文自体を終了させるわけではない。
continue;
return文
return文は、関数の実行を終了し、呼び出した関数に制御を移す。《式》があればその式の値を関数の値として戻し、式を省略した場合は、その関数は値が返されないことになる。また、関数内にreturn文がない場合は、関数定義を表す右中括弧(})に達して、関数の終わりとなる。この場合も、戻り値なしで呼び出し元に制御が戻る。
return 〔 ( 《式》 ) 〕 ;
goto文
goto文は、無条件分岐文である。無条件でラベルのついた文へ制御を移す。goto文は、構造化プログラミングには、不要な文である。また、C言語で書く場合もほとんど必要のない文である。
goto 《ラベル名》 ;
関数
C言語のプログラムは、関数の集合である。数学で使われる関数と同様に、「ある一定の入力に対して、一意の結果を出力するもの」となる。
関数は、入力の値とその出力の結果がわかれば、その内部でどのような処理が行われているか知らなくても利用することが出来る。また、関数自身は入力の値とその出力の結果に影響を及ばなさければ、変更することが可能である。
このような関数をモジュールと呼ぶ。C言語では、お互いに影響の与えない独立したモジュールを組み合われることでプログラムを作成する。
関数定義
C言語の関数は、以下のように宣言する。また、プログラム中で関数を呼ぶ場合には、それ以前に呼ばれる関数がどのような関数であるかの宣言(プロトタイプ宣言)をする必要がある。
《記憶クラス》 《データ型》 《関数識別子》 ( 《引数の型と変数名》 );
- 記憶クラス
- その関数が、他のソースファイルから参照可能か否かを指定する。参照不可は、static を指定し、参照可能は、extern を指定する。省略時は、extern となる。
- データ型
- その関数が返す値(戻り値)のデータ型を指定する。省略時は、int型となる。また、戻り値がない場合は、void を指定し、型指定のないポインタ型の場合は、void* を指定する。
- 関数識別子
- 関数を識別するための名前、つまり、関数名を記述する。
- 引数の型と変数名
- その関数が受け取る引数のデータ型と、関数の中で使用する変数名を定義する。また、引数をとらない場合は、void を指定する。
引数
関数に値を渡すに引数を用います。この引数の渡し方には、以下の2つの方法がある。
- 値渡し ( call by value )
- 関数を呼び出す際に、実引数になっている変数の値を仮引数にコピーし関数を呼び出す。このため、呼び出された関数で、引数の値を変更しても、呼び出した側の変数の値は変更されない。C言語のデフォルトの関数呼び出しは「値渡し」である。
- 参照渡し ( call by reference )
- 関数を呼び出す際に、実引数となっている変数のアドレス(メモリ上の場所)を、仮引数に渡して関数を呼び出す。呼び出された関数では、渡されたアドレスを参照して、間接的に値を変更することになる。このため、呼び出された関数の引数のポインタ変数(参照アドレスが入る変数)が示す値を変更すると、呼び出した側の変数も変更される。
再帰
C言語の関数は、再帰呼び出しを行うことが出来る。つまり、ある関数の中で自分自身を呼び出すことが出来る。通常は使う場面は多くないが、アルゴリズムによっては、この手法を利用することで、簡潔にその内容を書くことが出来る。
関数が再帰呼び出しを行うと、その中で使われている変数がスタック領域に積まれていく。そして、関数からも戻るとスタック領域から変数を復帰させる。このように何度も再帰呼び出しを行うと多くのスタック領域が必要となる。また、無限ループなどで非常に多くの再起呼び出しを行うと、スタック領域を使い果たし、スタックオーバーフローとなるので注意が必要となる。
main関数
プログラムを実行したときに最初に呼ばれる関数がmain関数である。つまり、プログラムはmain関数から始まることになる。(WindowsのGUIプログラムの場合は、WinMain、WindowsDLLはDllMainとなる)
main関数のプロトタイプ宣言は、以下のようになる。
〔 void | int 〕 main( 〔int argc 〔, char *argv[] 〔, char *envp[] 〕〕〕 )
引 数
- argc
- コマンドラインからプログラムに渡される引数の数プログラム名も引数と見なされるので、argcの最小値は1
- *argv[]
- コマンドラインからプログラムに渡される引数のNULL終端文字列へのポインタ配列最初(argv[0])は、プログラム名。以降は、プログラムへのコマンドライン引数。最後(argv[argc])は、NULLポインタ。
- *envp[]
- 環境変数文字列へのポインタ配列最後は、NULLポインタ。
戻り値
int を指定すると、return の式の値を、プログラムの終了コード(戻り値)として、OSへ渡す。void を指定すると、OSへは終了コードを渡せない。
C++での関数宣言の拡張
C++での関数宣言では、以下のようなことが出来るようになっている。
- 引数が省略された場合のデフォルト値を設定できる。
- 関数のオーバーロード(多重定義)が可能である。
- インライン関数の定義が可能である。
配列とポインタ
配列
配列とは、指定したデータ型を持った、複数の要素により構成されるデータ構造である。大括弧( [,] )で囲まれる要素数は、配列を構成するデータの個数、つまり、配列要素の個数を表す。
配列内の個々の要素は、配列名とそれに続く配列要素演算子( [,] )で囲まれた添え字で指定する。添え字は、0から始まる。従って、要素n個の配列で個々の要素を指定するときの添え字は、0からn-1となる。
《記憶クラス》 《データ型》 《配列名》[《要素数》] ;
配列の配列
多次元配列を使う場合は、配列の配列として宣言する。各次元の要素数をそれぞれ大括弧で囲み並べる。プログラム中で個々の配列要素を指定するときも同じ形式で指定する。
《記憶クラス》 《データ型》 《配列名》[《要素数》][《要素数》]…;
ポインタ
ポインタは、任意のデータ型の変数を参照するための値(アドレス)をデータとして持つための変数である。ポインタ変数を宣言するには、変数名の前にアスタリスク(*)をつける。
《記憶クラス》 《データ型》 *《ポインタ変数名》;
ポインタ変数が、指し示す先のデータの値を参照、代入するには、ポインタ演算子をポインタ変数名の前につけて参照する。また、変数のアドレスを取得する場合は、変数の前にアドレス演算子をつける。
ポインタと配列
配列内の各要素を参照する場合には、配列の添え字を用いるが、ポインタを使っても、同等のことを行うことが出来る。これは、配列名がその配列の0番目の要素のアドレスと同義であるためである。ただし、ポインタはアドレスを指す変数であるのに対し、配列名はそのアドレスを指す定数である。
また、あるポインタが特定の配列要素を指しているときに、そのポインタに1加算することは、結果的に次の要素を指し、そのポインタに1減算することは、結果的に前の要素を指すことになる。このポインタに対しての加減算は、どのデータ型の配列であろうと当てはまる。
構造体と共用体
構造体
配列は同じ型のデータの集まりで構成されるデータ構造であるのに対して、構造体は、異なる型のデータの集まりで構成することの出来るデータ構造である。関連したデータをデータを集めて、一つの名前でまとめることによって、関連した一群のデータを別々のデータとしてでなく、一つのデータの単位として扱うことが出来る。
struct 〔《構造体タグ名》〕 {
〔《データ型》 《メンバー名》;〕
:
} 〔《変数名》 〔,…〕 〕;
structは、構造体のデータ定義をする宣言子である。構造体は、そのメンバー(構成要素)を指定することによって定義される。
構造体では、タグと言う名前を指定することが出来る。タグは、そのときに定義された構造体の構造を表す名前となる。この宣言以降で、同じ構成の構造体を使用するときに、この構造体タグ名を指定することで、再度、構成定義を書かなくても、同じ構成の構造体を表すことが出来る。あくまでも、構造体タグ名は、その構成をあわす名前であり、実際のデータ型や変数になるわけではない。
共用体
共用体は、同じメモリ領域を異なるデータ型の変数で操作できるようにするための宣言である。宣言の方法や利用の仕方は、以下に示すように、構造体とほぼ同じである。
union 〔《共用体タグ名》〕 {
〔《データ型》 《メンバー名》;〕
:
} 〔《変数名》 〔,…〕 〕;
unionは、共用体のデータ定義をする宣言子である。
各メンバーには、同じアドレスが割り当てられる。また、データ領域は、そのメンバーのうち、最大のものを保持できる大きさが確保される。
共用体は、メンバーのいずれのデータ型であってもかまわないが、今どのデータ型で使用しているかは、プログラマが意識して使用する必要がある。あるデータ型を保持している共用体に対して、他のデータ型として扱った場合には,それぞれのマシンに依存した結果となる。
構造体/共用体のメンバー参照
プログラム内で構造体/共用体の各メンバーの値に代入や参照するには、「変数名.メンバー名」として参照する。ここでのピリオド(.) は、構造体/共用体の変数名とメンバー名を結びつける、メンバーの直接参照演算子である。
また、構造体/共用体のポインタ変数名から各メンバーの値に代入や参照するには、「変数名−>メンバー名」として参照する。ここでのアロー(−>) は、構造体/共用体のポインタ変数名とメンバー名を結びつける、メンバーの間接参照演算子である。
ちなみに、ポインタ演算子(*)と直接参照演算子(.) で、「(*(data+1)).name」のように参照することも可能だが複雑になる。
構造体/共用体の利用
構造体/共用体と言っても、変数宣言の仕方は、基本データ型とほぼ同じですし、記憶クラス、配列、ポインタ変数も扱うことが出来る。また、構造体の中に別の構造体や共用体を入れることも出来る。
また、構造体/共用体も基本データ型と同じように、関数の引数や戻り値にすることも出来る。引数には、値渡し、参照渡しともに指定することが出来る。
自己参照構造体
自己参照構造体は、構造体メンバに自分自身と同じタグの構造体をポインタで宣言する。これにより、鎖のようにつながったデータ構造(リスト構造)を作成することができる。
ビットフィールド
C/C++には1ビット単位でデータへアクセスする機能があり、それはビットフィールド(bit field)と呼ばれる。構造体メンバ名の後ろに「:ビット幅」を付けることでアクセスするビット幅が決まる。ビットフィールドは構造体の一種である。
ビットフィールドのメンバは、signed int / unsigned intとして宣言する。幅1bitのデータについては符号を持てないのでunsignedにしなければいけない。
プリプロセッサと分割コンパイル
プリプロセッサ
プリプロセッサは、C/C++にない機能を補うものである。他のファイルを挿入したり(#include)、定数や式を定義したり(#define)、また、条件によりコンパイル方法を変更させたり(#if,#else等)するのに利用する。これによって同じプログラムを異なるコンパイラやOSで処理させることや、各プログラムでの共通事項を一々書く手間を省くことが出来る。
プリプロセッサに対する命令は、1命令1行で書く必要がある。
マクロ定義
プログラム中の定数または文に対して、意味のある定義をする。定義後、その定義名が現れるたびに、定義した文字列に置換する。文字列は、1つ以上の定数、キーワード、もしくは、文で構成される。
#define 《定義名》 《文字列》
また、パラメータをもつ式にし、マクロ関数を定義することも出来る。
#define 《定義名》( 《パラメータ》 ) 《文字列》
そして、指定した定義名の定義を削除することも出来る。
#undef 《定義名》
ファイルの組み込み
指定したファイル名の内容を、この命令が記述された場所に展開する。
#include <ファイル名> #include "ファイル名"
ダブルクォーテーション(”)で囲んだ場合は、そのソースのディレクトリを探し、見つからない場合は、標準のインクルードディレクトリを探しに行く。また、山括弧(<>)で囲んだ場合は、最初から、標準のインクルードディレクトリを探しに行く。
条件コンパイル
定数式を評価して、その結果で実行する文を決める。
#if 《定数式》 《文》 〔#elif 《定数式》 《文》〕 〔#else 《文》〕 #endif
または、マクロ定義があるかで実行する文を決める。
#ifdef 《マクロ定義名》 《文》 〔#else 《文》〕 #endif
または、マクロ定義がないかで実行する文を決める。
#ifndef 《マクロ定義名》 《文》 〔#else 《文》〕 #endif
また、次のようにしても、マクロ定義の有無で条件コンパイルが行われる。
#if define 《マクロ定義名》 #if !define 《マクロ定義名》
行番号制御
コンパイラが出すエラーメッセージを、指定したファイル名と行番号で出力する。
#line 《行番号》 〔 《ファイル名》 〕
分割コンパイル
数百行のプログラムや一人での開発時には、1つのファイルに全てのプログラムを記述しても良いのですが、数千行にわたる大規模なプログラムでは、プログラムの中から特定の関数を探し出すだけでも大変ですし、バグを発見するのはもっと困難になる。
また、多人数で開発する時には、機能ごとに分けて開発するので1つのプログラムを複数に分割して管理し開発する事になります。
分割コンパイルを行う事は、開発効率を高くするのに非常に有効である。
標準関数
プログラムを作るとき全ての機能を1から作るのは、非現実的な労力を必要とする。標準的に用意されている関数を標準関数と呼ぶ。printfやscanfなども標準関数である。標準関数は、ライブラリと呼ばれるファイルに全部まとめられているので、これを標準ライブラリと呼ぶ。
標準入出力関数
printf
標準出力ストリームに書式付きで出力する。
#include <stdio.h> int printf( const char* format 〔, argument 〕 )
- 引 数
format : 書式文字列 argument : オプション引数
- 戻り値
出力した文字数を返す。
- 書式文字列
書式文字列は、"\"で始まるエスケープシーケンスは、その記号の意味で処理される。"%"で始まる書式指定は、下記のように処理される。その他の一般文字は、そのまま出力される。書式指定は、次の形式で行う。各書式指定フィールドでは、特定の書式を表す1個の文字または数字を指定する。
%〔flags〕 〔width〕 〔.precision〕 〔{ h | l }〕type
flags は、出力位置の調整をする。
- 指定された出力幅内で結果を左詰にする。(未指定時は、右詰) + 出力が符号付の場合は、値の前に必ず符号をつける。(未指定時は、負の場合のみ) 0 左側の空白に0が埋められる。
widthは、出力する文字数の最小幅を指定する。
precisionは、全体または一部に表示する最大文字数、または、整数値として表示する最小桁数を指定する。
d,u,o,I,x,X 出力される最小限の桁数を指定。 e,E,f 出力される小数点以下の桁数を指定。 g,G 出力される最大の有効桁数を指定。 s 出力される最大の文字数の指定。
h | l は、引数のサイズを規定する。shortはhを、long及びdoubleはlを指定する。
typeは、必須項目で、対応する引数を文字、文字列、数値のいずれとして解釈するかを指定する。
d 符号付き10進整数 f +ddd.ddd形式の実数 c 一文字 u 符号なし10進整数 e,E +d.dddeddd形式の実数 s 文字列 o 符号付き8進整数 g,G f,gの短い方の書式 i 符号なし8進整数 x,X 符号なし16進整数
width及びprecisionの指定を*にすると、引数リストの int 引数から値が提供される。
文字列操作関数
strcmp
文字列を比較する。
#include <string.h> int strcpy( const char* string1, const char* string2 )
- 引 数
string1 : 比較するNULL終端文字列 string2 : 比較するNULL終端文字列
- 戻り値
負 : string1がstring2より小さい。 0 : string1とstring2同じ。 正 : string1がstring2より大きい。
strcpy
文字列をコピーする。
#include <string.h> char* strcpy( char* string1, const char* string2 )
- 引 数
string1 : コピー先の文字列 string2 : コピー元のNULL終端文字列
- 戻り値
コピー先の文字列を返す。
strcat
文字列を追加する。
#include <string.h> char* strcat( char* string1, const char* string2 )
- 引 数
string1 : 追加先のNULL終端文字列 string2 : 追加するNULL終端文字列
- 戻り値
追加先の文字列( string1 )を返す。
strlen
文字列の長さを取得する。
#include <string.h> size_t strlen( const char *string )
- 引 数
string : 長さを調べるNULL終端文字列
- 戻り値
stringの文字数を返す。この長さには、終端のNULLは含まれない。
strchr
文字列から検索して文字を見つける。
#include <string.h> char* strchr( const char *string, int c )
- 引 数
string : 検索されるNULL終端文字列 c : 検索する文字
- 戻り値
string中でcが最初に現れた位置へのポインタを返す。 文字が見つからないとNULLを返す。
sprintf
書式付きデータを文字列に書き込む。出力したバイト数を返す。
#include <stdio.h> int sprintf( char* buffer, const char *format 〔, argument ...〕 )
- 引 数
buffer : 出力先 format : 書式文字列 argument : オプション引数
- 戻り値
出力した文字数を返す。
書式文字列の詳細は、printfを参照。
バッファ操作関数
memcmp
2つのバッファ内の文字を比較する。
#include <memory.h> int memcmp( const void* buf1, const void* buf2, size_t count )
- 引 数
buf1 : 比較するバッファのポインタ buf2 : 比較するバッファのポインタ count : 比較するサイズ
- 戻り値
負 : buf1がbuf2より小さい。 0 : buf1とbuf2同じ。 正 : buf1がbuf2より大きい。
memcpy
バッファの内容を、先頭から指定されたバイト数分、他のバッファへコピーする。
#include <memory.h> void* memcpy( void* dest, const void* src, size_t count )
- 引 数
dest : コピー先のバッファのポインタ src : コピー元のバッファのポインタ count : コピーするサイズ
- 戻り値
コピー先のバッファのポインタを返す。
memset
バッファの内容を、指定された文字で、バイト数分初期化する。
#include <memory.h> void* memset( void* buf, int c, size_t count )
- 引 数
buf : 対象バッファへのポインタ c : 初期化する文字 size : 初期化するサイズ
- 戻り値
バッファのポインタを返す。
memchr
バッファから検索して文字を見つける。
#include <memory.h> void* memchr( const void* buf, int c, size_t count );
- 引 数
buf : 対象バッファへのポインタ c : 検索する文字 size : 調べるサイズ
- 戻り値
buf中でcが最初に現れた位置へのポインタを返す。 文字が見つからないとNULLを返す。
ファイル操作関数
fopen
指定したストリームを開く。
#include <stdio.h> FILE* fopen( const char* filename, cont char* mode )
- 引 数
filename : 開くファイル名 mode : 許可されるアクセスモード
- 戻り値
成功するとファイルポインタを、失敗するとNULLポインタを返す。
fclose
指定したストリームを閉じる。
#include <stdio.h> int fclose( FILE* stream )
- 引 数
stream : 開いたストリームのファイルポインタ
- 戻り値
ストリームのクローズに成功すると0を返す。
feof
ストリーム上でファイル終端の有無を調べる。終端でないと0を、終端の場合は0以外を返す。
#include <stdio.h> int feof( FILE* stream )
- 引 数
stream : 開いたストリームのファイルポインタ
- 戻り値
ファイルの終端より後で最初の読み出し操作が行われると、0以外の値を返す。 現在の位置がファイルの終端でない場合は、0を返す。
ferror
ストリーム上でエラーの有無を調べる。
#include <stdio.h> int ferror( FILE* stream )
- 引 数
stream : 開いたストリームのファイルポインタ
- 戻り値
stream上でエラーが発生していないと、0を返す。 それ以外の場合には、0以外の値を返す。
fseek
指定された位置にファイル ポインタを移動する。
#include <stdio.h> int fseek( FILE *stream, long offset, int origin )
- 引 数
stream : 開いたストリームのファイルポインタ offset : 基点からの移動させるバイト数 origin : 基点の指定 ( SEEK_SET:先頭 , SEEK_CUR:現在位置 , SEEK_END:終端 )
- 戻り値
成功すると0を返す。それ以外の場合は、0以外の値を返す。 シークできないデバイスでは、戻り値は未定義。
fread
ストリームからデータを読み出す。
#include <stdio.h> size_t fread( void* buffer, size_t size, size_t count, FILE* stream )
- 引 数
buffer : データの格納するバッファのポインタ size : 項目サイズ(バイト単位) count : 読み出す最大項目数 stream : 開いたストリームのファイルポインタ
- 戻り値
実際に読み出した全項目の数を返す。 この数がcountより小さい場合は、エラーが発生したか、終端に達している。
fwrite
ストリームにデータを書き出す。
#include <stdio.h> size_t fwrite( const void* buffer, size_t size, size_t count, FILE *stream )
- 引 数
buffer : 書き込むバッファのポインタ size : 項目サイズ(バイト単位) count : 書き出す最大項目数 stream : 開いたストリームのファイルポインタ
- 戻り値
実際に書き出した全項目の数を返す。 この数がcountより小さい場合は、エラーが発生している。
fgets
ストリームから文字列を読み出す。
#include <stdio.h> char* fgets( char* string, int n, FILE* stream )
- 引 数
string : データの格納場所のポインタ n : 読み出す最大バイト数 stream : 開いたストリームのファイルポインタ
- 戻り値
成功すると、stringを返す。 エラーが発生するか、ファイルの終端に達するとNULLを返す。
fpus
ストリームに文字列を書き出す。
#include <stdio.h> int fputs( const char* string, FILE* stream )
- 引 数
string : 書き込むバッファのポインタ stream : 開いたストリームのファイルポインタ
- 戻り値
成功すると、負でない値を返す。 エラーが発生すると、EOFを返す。
fprintf
指定したストリームに書式化して出力する。出力したバイト数を返す。
#include <stdio.h> int fprintf( FILE* stream , const char* format 〔, argument list〕 )
- 引 数
stream : 開いたストリームのファイルポインタ format : 書式文字列 argument : オプション引数
- 戻り値
出力した文字数を返す。
書式文字列の詳細は、printfを参照。
文字列から数値変換関数
atoi
文字列をint型整数値へ変換する。
#include <stdlib.h> int atoi( const char *str )
- 引 数
str : 変換するNULL終端文字列
- 戻り値
int型整数値を返す。
atol
文字列をlong型整数値へ変換する。
#include <stdlib.h> long atol( const char *str )
- 引 数
str : 変換するNULL終端文字列
- 戻り値
long型整数値を返す。
atof
文字列を倍精度浮動小数点数値へ変換する。
#include <stdlib.h> double atof( const char *str )
- 引 数
str : 変換するNULL終端文字列
- 戻り値
倍精度浮動小数点数値を返す。
メモリ操作関数
malloc
メモリ領域を割り当てる。
#include <malloc.h> void* malloc( size_t size )
- 引 数
size : 割り当てるサイズ
- 戻り値
割り当てに成功すると、割り当てた領域の先頭ポインタを返す。 失敗した場合は、NULLポインタを返す。
calloc
要素を0x00で初期化したメモリ領域を割り当てる。
#include <malloc.h> void* calloc( size_t num , size_t size )
- 引 数
num : 要素数 size : 各要素のサイズ
- 戻り値
割り当てに成功すると、割り当てた領域の先頭ポインタを返す。 失敗した場合は、NULLポインタを返す。
realloc
すでに割り当てられているメモリ領域の大きさを変更する。すでに、割り当てられているメモリ領域の内容は、再度割り当てる前後の小さい方のサイズ分は保証される。
#include <malloc.h> void* realloc( void* old , size_t size )
- 引 数
old : 割り当てられたメモリ領域のポインタ size : 割り当てるサイズ
- 戻り値
割り当てに成功すると、再度割り当てた領域の先頭ポインタを返す。 失敗した場合は、NULLポインタを返す。
free
割り当てられたメモリ領域を開放する。
#include <malloc.h> void free(void* old )
- 引 数
old : 割り当てられたメモリ領域のポインタ
埋め込みSQL
埋め込みSQLとは
埋め込みSQL (Embedded SQL や ESQL とも言う)とは、C/C++のソース内にSQL文を記述することで、プログラムからデータベースの操作やデータの取得等を直接行えるようにするものである。
Cのコンパイラは、SQL文を理解できないので、プリコンパイラを使いSQL文を適切なC関数等に変更する必要がある。プリコンパイラは、「EXEC SQL …」で始まるSQL文を処理し、適切なCのソースへと変更する。各RDBにそれぞれのプリコンパイラ(プリプロセッサ)が用意されているのでそれを利用する。
※以降の説明は、Oracle の Pro*C/C++ を中心に説明している。
ホスト変数
プログラムからデータベースにデータを渡す場合(たとえば、問い合わせ内のパラメータとして)、あるいは、データベースからプログラムにデータを戻す場合には、そのデータの格納先となる Cの変数を、プリコンパイラがこれらを認識できるようにする必要がある。 この埋め込みSQLで使用するCの変数をホスト変数と言う。
ホスト変数の宣言は、以下のように宣言セッションの中に記述する。
EXEC SQL BEGIN DECLARE SECTION; ホスト変数の宣言; EXEC SQL END DECLARE SECTION;
このセッション内で宣言した変数が、ホスト変数として埋め込みSQLの中で使うことが出来る。宣言したホスト変数をSQL文内で使用するときは、宣言した変数の前にコロン(:)を付けて、SQL文を記述する。また、ホスト変数は、通常のCのソースからも変数として利用することが出来る。
ホスト変数では、VARCHAR型の変数も利用することが出来る。この変数は、Cのソースでは、データ長が入るlenと、実際のデータが入るchar型配列のarrの構造体としてそれぞれに宣言される。
構造体をホスト変数として利用することも出来る。ただし、この場合、構造体の定義もこのセッション内で行う必要がある。また、select文の結果を格納するときや、insert文で値を渡すときに、構造体のホスト変数を使う場合は、構造体の定義の順番にSQL文の項目と合わせられる。
埋め込みSQLの記述
SQL文を、「EXEC SQL … ;」の中で記述することで、プログラム中からSQL文を実行することが出来る。SQL文内でホスト変数を利用する場合は、変数名の前にコロン(:)を付け変数名を書く。ただし、一部のSQL文(DDL文等)については、SQL文内にホスト変数を利用することは出来ない。
カーソル
select文で複数レコードのデータを取得する場合は、カーソルを利用する。手順としては、以下のようになる。
- まず、DECLARE CURSOR文を用いてカーソルの宣言を行う。この時に実行するSQL文を定義する。
- 次に、OPEN CURSOR文を使用してカーソルを開く。この時に定義したSQL文が実行される。
- 1行づつデータを取り出すには、FETCH文を利用する。FETCH文を実行すると、その行の各データをホスト変数へ格納する。
- そして、処理が終わったらCLOSE文を使いカーソルを閉じる。これにより、カーソルに蓄えられていたクエリの結果集合が破棄される。
動的SQL
プログラム内で決まったSQL文を実行する場合は、直接SQL文をソース内に記述できる。しかし、そのSQL文が実行時に、組み立てられる場合や外部から与えられる場合は、その都度、SQL文を変更する必要がある。このような文を動的SQLと言う。
Oracle動的SQLの種類としては、以下の4種類がある。
- 入力ホスト変数のない、非問合せ文
- 入力ホスト変数の数がわかっている、非問合せ文
- 選択リストの列数と、入力ホスト変数の数がわかっている、問合せ文
- 選択リストの列数、または、入力ホスト変数の数がわからない、問合せ文
方法1は、実行するSQL文をその都度作成し、実行するため、実行するたびにSQLの解析が行われる。また、方法4は、最も柔軟性に富んでいるが、複雑なコーディングが必要となる。
ANSI動的SQLの場合、動的SQLを指すのは、Oracle動的SQL方法4のみ。しかし、埋め込みSQLとしては、すべての方法をサポートしている。
最終更新時間:2008年11月14日 00時37分00秒 指摘や意見などあればSandBoxのBBSへ。