ログイン
kid's world

C言語の文法

#include指令

それでは,ここからはC言語の文法についてざっと整理していきたい。

ソースファイルをコンパイルしようとすると,まず「プリプロセッサ」で処理されて,その次に「コンパイラ」で処理される。プリプロセッサでは「#include」で始まる行が見つかると,その行を「指定されているファイルの内容」で置き換える。たとえば

#include <stdio.h>

と書くと,stdio.hという名前のファイルの内容がその行に追加されてからコンパイルされる。stdio.hは標準入出力に関する関数宣言などが書かれているファイルであり,これをインクルードすることによってさまざまな関数が使えるようになる。

トークン

トークンについては第6回で説明したが,ソースコードにおいて「これ以上分割できない」単位であり,たとえば変数名や関数名や予約語などの識別子,定数,演算子,カッコなどがトークンの例である。トークンの間は空白文字で区切られる。空白文字には,スペース,水平タブ,垂直タブ,改行,改ページなどがある。混乱の起こるおそれのない場合は,トークン間の空白文字を省略することができる。すなわち,それぞれのトークンはプログラマの都合で自由な区切り方をすることができ,ここでの書き方が「プログラムの見やすさ」に強い影響を与える。このように自由な書式で記述できるプログラミング言語をフリーフォーマットな言語という。

演算子

演算子はC言語における処理のもっとも小さな単位である。演算子についてはさまざまな場面で解説してきたように,数値計算をはじめとして,代入や比較などさまざまな処理を行うために使われる。すべての処理は細かく見ていけば演算子で記述されているといってもいいかもしれない。

ほとんどの演算子は,その記号の左右に式を必要とする。これを二項演算子という。それに対して右側(または左側)に1つだけしか式を必要としない演算子もあり,これを単項演算子という。演算結果は文法上「式」となるので,演算子を数珠つなぎにつなげていくことができる。このとき,「どの演算子から処理していくか」は次に説明する優先順位と結合規則によって決まる。

優先順位と結合規則

演算子の一覧をTable 1に示す。演算子には優先順位があり,それが高いものから先に評価される。また,結合規則により,優先順位が同じ演算子が続いているときに左右どちらを優先するのかが決められている。詳しくは第2回,第6回を参照していただきたい。

Table 1: 演算子の順序と結合規則

/c/string-table1.png

算術演算子は,足し算や掛け算などの計算を行うために使う演算子である。Table 2に,それぞれの演算子とその意味を示す。

Table 2: 算術演算子

記号意味
+加算10+3 → 13
-減算10-3 → 7
*乗算10*3 → 30
/除算10/3 → 3
%剰余10%3 → 1

算術演算子は,与えられた値の型で演算を行い,計算結果はその型で返すという決まりになっている。たとえば,Table 2に与えられている例の「10/3」の結果が「3」という整数値になっているのは,「10」も「3」もint型なので,答え(式の値)もint型となるためである。詳細は第3回を参照していただきたい。

代入演算子

変数に値を記憶させることを代入というが,これは代入演算子を使うことで行える。たとえばint型変数iに10を代入するには,

i=10;

と書く。また,

i=i+10;

というような書き方を多くするが,これを短縮して,

i+=10;

と書けるように,「+=」といった算術演算子も用意されている。よく使われるものをTable 3に示しておこう。

Table 3: 代入演算子

記号意味
=代入
+=加算して代入
-=減算して代入
*=乗算して代入
/=除算して代入
%=剰余を代入

なお,代入演算子の結果の式は,左辺値(代入演算子の左側にある変数の値)となる。また,結合規則は右から左なので,

i=j=k=100;

という書き方が可能となる。

インクリメント/デクリメント演算子

変数の値を1だけ増減する場合は,インクリメント演算子「++」やデクリメント演算子「--」を使う。たとえば,int型変数iに記憶されている数値を1だけ増やしたい場合は,

i++;

とする。

ポインタ変数にインクリメント演算子やデクリメント演算子を使った場合は,そのポインタ変数の型のサイズだけ増減する。

sizeof演算子

変数や配列の確保しているメモリ上のサイズは,sizeof演算子「sizeof」で得ることができる。たとえば,配列aのサイズは,

sizeof a

でわかる。「int」などの型名を直接指定して,そのサイズを知りたいときは,型名をカッコでくくる。たとえば,int型のサイズは,

sizeof(int)

でわかる。関数によく似た形式になるので,関数と間違わないように注意したい。

アドレス演算子・間接演算子

変数のアドレスは,知りたい変数の左にアドレス演算子「&」をつけることによって得ることができる。逆にポインタの指し示す先の値を得たり,書き換えたりするには,ポインタの左に間接演算子「*」をつける。詳細は後述する。

関係演算子・論理演算子

関係演算子と論理演算子が作る式の値は,条件が満たされたとき(真という)は「0以外の値」,満たされなかったとき(偽という)は「0」となる。演算子の一覧とその条件を,Table 4とTable 5に示す。これらの演算子はif文などの制御文で利用される。詳細については第7回を参照していただきたい。

Table 4: 関係演算子

記号条件
< a < b aはbよりも小さい
> a > b aはbよりも大きい
<=a <= baはb以下
>=a >= baはb以上
==a == baはbと等しい
!=a != baはbと等しくない

Table 5:論理演算子

記号条件
&&a && baかつb
||a || baまたはb
! !aaではない

コンマ演算子

コンマ演算子「,」は何もしない演算子であり,式の値は,コンマ演算子の左側の式の値である。何もしない演算子が必要となるのは,式が1つしか書けないところで複数の式を書きたい場合である。コンマ演算子の優先順位はいちばん低いので,式をコンマ演算子でつなげていくと全体は1つの式となり,それぞれの式はべつべつに評価されることになる。for文の初期化式や反復式に使うことが多い。

式と文

C言語において「式」と「文」の概念は非常に重要である。演算子や関数との関係や,これらがいったいどのようにC言語の文法を構築しているのかを考えながら,復習してみよう。

一次式

式にはさまざまな種類があり複雑な階層構造になっているが,もっとも基本的なのは一次式である。これは,識別子,定数,文字列,またはカッコで囲まれた式のことである。識別子の例としては変数がある。つまり,先ほどの「i」(int型変数)や「10」などは一次式である。

代入式

代入演算子と,その左側の左辺値と,右側の式の3つの組を代入式という。たとえば「i=10」は代入式である。代入式の値は左辺値の値,すなわち「i=10」ならば「10」である。

文は必ず「;(セミコロン)」で終わる。文の多くは関数の内部に存在し,基本的には上から下に向かって逐次実行されていく。その実行順序は,制御文によって変えることができる。

式文

式の後ろにセミコロンをつけた,

式;

という形の文を式文という。ほとんどの文は,この式文である。C言語では式や演算子は単独で存在することはできないので,この式文を書くことになるからだ。

複文(ブロック)

制御文の内部など,場合によっては1つの文しか書けないところに複数の文を書かなければならないことがある。そのときは複数の文を複文(ブロック)という1つの文にする。

複文は「{」で始まり,「}」で終わる。たとえば,

{
  文1
  文2
}

は1つの複文であり,文法上1つの文としてみなされる。

制御文

制御文,すなわちif文,switch文,for文,while文などもすべて文である。これらについては次で詳しく説明しよう。

制御文

本来,文は逐次実行されるが,制御文を使うと,その流れを条件によって制御することができる。条件を指定するのには,主に関係演算子と論理演算子が利用される。

break文

break文は,while文,for文,switch文の3つの制御文の中で使うことができ,呼び出されるとその制御文を強制的に終了し,次の文へ実行を移す。つまり,強制的にその制御文から抜け出すことができる。

continue文

continue文は,while文とfor文の中で使うことができ,呼び出すと「制御文中の残りの部分の実行」を飛ばし,次の繰り返しを実行する。

return文

return文を呼び出すと,実行中の関数を終了して,その関数の呼び出し元へ制御を戻す。return文の後ろには式を書くこともでき,その値は関数の呼び出し元へ返される。

if文

if文は条件によって実行する文を選択する制御文である。

if(式) 文1 else 文2

上記の場合,式が「0」でなければ文1が実行され,そうでなければ文2が実行される。

switch文

switch文は,与えられた式の値に対応する文を実行する制御文である。if文は2つの文の中から選択することしかできないが,switch文は複数の文の中から実行する文を選択することができる。

switch(式){
  case 定数式1: 文1a 文1b...
  case 定数式2: 文2a 文2b...
  ...
  case 定数式n: 文na 文nb...
  default: 文da 文db...
}

式に指定した値と同じ定数式があった場合は,そのcase以降が実行される。そうでなければ,default以降が実行される。break文を使うと,break文以降は実行されないでswitch文から抜け出すことができる。

while文

while文は,条件が満たされている間,文を繰り返し実行する制御文である。

while(式) 文

式の値が0でないかぎり,文が繰り返し実行される。式の値が0になると次の文へ実行が移される。

do while文

do while文はwhile文と同様に条件が満たされている間,文を繰り返し実行する制御文であるが,条件にかかわらず最初の1回は必ず文が実行される点が異なる。

do 文 while(式);

for文

for文は,繰り返しを行うことについてはwhile文に似ているが,繰り返しを行う前に初期化が行える点と,各繰り返しのあとに式を評価することができる(実行ができる)点が異なる。

for(初期化式; 条件式; 反復式) 文

参考までに,for文をwhile文で書き換えたものを次に示す。

初期化式;
while(条件式){
  文
  反復式;
}

関数

複数の文をひとまとめにして何度も再利用したい場合は,関数を使う。関数は内部に文を複数書くことができる。関数は順番に実行されるものではなく,「呼び出されることによって内部の文が実行される」という仕組みになっている。

関数呼び出し

関数は,関数名とそれに続くカッコを書くことで呼び出すことができ,カッコの中には引数を指定する。これを関数呼び出しといい,文法上は式の1つとなる。単純に呼び出したいときは,式文として,

関数名(引数リスト);

と書いて呼び出す。関数は値を返すことができ,その値は「その関数呼び出しの値」となる。たとえばint型を返す関数fooとint型変数i,jがあったとき,

i=foo()+j;

と書くことができる。

main関数

関数は呼び出されるものなので,関数が存在しているだけでは意味がないのだが,C言語では「main」という名前のついた関数は特別な意味を持つ。main関数はそのプログラムが実行されるときに初めに自動的に呼び出される。したがって,main関数を1つ用意し,中にいくつかの文を書けば,それだけでも立派なプログラムとなる。

プロトタイプ宣言

関数を作るときはプロトタイプ宣言を行い,コンパイラにその関数の型や引数についての宣言を行う。実際の関数の定義は,プロトタイプ宣言よりも後ろに書く。そうすることで,すべての関数から自由に呼び出すことができるようになる。プロトタイプ宣言は,次のような書式である。

戻り値の型 関数名(引数リスト);

関数定義

もちろんプロトタイプ宣言をしただけでは関数は使えない。関数の中身を記述する必要がある。それを関数定義という。関数定義は次のような書式である。

戻り値の型 関数名(引数リスト)
{
  文のリスト
}

値の返し方

関数は,次のようにreturn文によって値を呼び出し元に返すことができる。

return 式;

ポインタによる値の返し方

複数の値を同時に返す

複数の値を同時に返したいときは,引数としてポインタ変数を受け取るようにする。そうすれば,関数内部でそのポインタを介してメモリ上に直接値を書き込むことで複数の値を返すことができるからだ。

配列全体を返したいときも同様である。

配列の扱い方

配列名は先頭要素へのポインタであることを思い出せば,関数を,

foo(int *array);

と宣言して,foo関数内部では関数のように扱うことができることがわかるだろう。

また,

foo(int array[]);

と宣言しても同じである。

文字列を返す

文字列は基本的にchar型の配列と考え,配列と同様の扱い方によって値を返す。関数側では「メモリの確保については気にせずに文字列を書き込む」というスタイルが一般的であり,関数の呼び出し元で十分なメモリ領域を用意させるようにする。

変数

変数を使うとデータを一時的に記憶することができる。数値や文字などさまざまなデータを記憶するために使うことができる。記憶できる期間は,長くてもプログラムが実行されている間だけである。プログラムが終了してもデータを保持しておくためにはファイルを使う。

変数の型と宣言

変数は宣言をしなければ使うことができない。宣言は以下のように変数名ともに型を指定して行う。

型 変数名;

変数名はコンマで区切って複数指定でき,そのときはすべて同じ型となる。

連載で紹介した型の一覧をTable 6に示す。

Table 6:連載で紹介した型の一覧

型名意味
char文字型(1バイト)
int整数型
short 整数型
long整数型
float実数型
double実数型
long double実数型

変数はメモリの一部分を確保することによってデータを保存している。確保されるサイズはこれらの型によって異なる。実際にどれだけのサイズが確保されるのかは,以下のようにsizeof演算子の後ろに型名をカッコで囲んで書くことによって知ることができる。

sizeof(型名)

変数名はプログラマが自由に決めることができるが,Fig. 1の予約語は使うことができない。1文字目は必ず英字(大文字,小文字のアルファベットと,アンダーバー「_」)である必要がある。2文字目以降は英数字が使える。

Fig. 1: 予約語

auto     double  int      struct
break    else    long     switch
case     enum    register typedef
char     extern  return   union
const    float   short    unsigned
continue	 for     signed   void
default  goto    sizeof   volatile
do       if      static   while

変数の初期化

変数(自動変数)は,宣言した直後は値が定まっていない。これはメモリを確保したときにそのメモリを初期化しないためである。もし初期化をしたい場合は,宣言時に以下のように「=」を使って変数の初期化をすることができる。これは代入演算子ではないところに注意しよう。

型 変数名=初期値;

変数のスコープ

変数は,宣言する位置に応じて有効な範囲が決まる。その変数が利用できる範囲のことを変数のスコープと呼んでいる。たとえば,関数の中で宣言した変数はその関数内でのみ利用可能である。ほかの関数からは使うことができない。このような変数をローカル変数と呼んでいる。それに対して関数定義の外で宣言した変数をグローバル変数といい,すべての関数から利用可能となる。

ポインタ変数

変数が確保しているメモリの先頭アドレスは,ポインタ変数に記憶することができる。

ポインタ変数は,「そのポインタ変数が指し示す領域を確保している変数」の型名を指定して,以下のように宣言する。

型 *変数名;

アドレス演算子

変数が確保しているメモリの先頭アドレスは,アドレス演算子「&」で知ることができる。たとえば,int型変数iのアドレスを「int型を指し示すポインタ変数に代入する」には,以下のようにする。

int *p, i;
p =&i;

間接演算子

ポインタ変数が指し示す先のメモリ内容は,単項演算子である間接演算子「*」で読み取ったり,書き換えたりすることができる。二項演算子である乗算演算子ではないことに注意したい。たとえば,

*p=100;

とすることで,ポインタ変数pの指し示す先のメモリに「100」を書き込むことができ,

j=*p;

とすることで,そのメモリの内容を変数jに代入することができる。

配列

同じ型のデータを大量に扱うときは配列を使う。

配列は,次のようにデータの個数とともに宣言する。

型 配列名[個数];

添え字を使うことによって,配列のそれぞれの要素にアクセスすることができる。たとえば,以下のように100個の要素を持つ配列を宣言したとすると,0〜99の添え字が使えるようになる。

int i[100];
i[99]=123;

配列名はポインタを表す。ただしポインタ変数のように代入はできないので,

int i[100], j;
i=&j;

は誤りである。

構造体

構造体を使うと,複数の型のデータをひとまとめに扱うことができる。構造体には複数のメンバを内部に含むことができ,独自に作成できる型と考えてもよい。ただし,構造体に対して利用できる演算子は,代入演算子「=」,アドレス演算子「&」,構造体メンバ演算子「.」だけである。

構造体の定義

構造体はstructというキーワードを使って,次のように定義する。基本的にこの定義は関数外に書く。

struct 構造体名{
  メンバ1
  メンバ2
  ...
};

構造体の変数宣言

実際に構造体を使うには,変数を宣言しなければならない。「int型」というような型名だけあっても意味がなく,int型の変数を宣言しなければならないのと同じである。その変数の型名としては「struct 構造体名」を使う。

struct 構造体名 変数名;

構造体メンバ演算子

構造体の変数に対して,構造体メンバ演算子「.」を使うことによって,構造体のそれぞれのメンバにアクセスすることができる。単純にドット演算子と呼ばれることもある。以上を実際の例で整理すると次のようになる。

struct position{
  int x;
  int y;
};

というposition構造体を定義したとすると,変数posの宣言は,

struct position pos;

と行うことができる。そして,構造体メンバ演算子を使って,

pos.x=100;
pos.y=200;

というように,それぞれのメンバに値を書き込むことができる。

定数

定数はプログラム中において値が変化しない要素のことであり,文法上「式」となる。

小数点をつけずに数値を書くと,int型やlong型などの整数を表す型の定数となる。可能な限り小さな型で表現される。たとえば「100」はint型となる。最後に「l」や「L」をつけるとlong型になる。たとえば「100L」はlong型の100となる。「0」(ゼロ)で始まる数値は8進数として扱われ,「0x」か「0X」で始まる場合は16進数として扱われる。

小数点「.」のついた数値はdouble型となる。たとえば「100.」はdouble型である。最後に「f」や「F」がついたものはfloat型となり,「l」か「L」のついたものはlong double型になる。たとえば「200.0L」はlong double型だ。

文字定数

「'」(単一引用符)で文字を囲んだものは文字定数といい,char型となる。また「\」で始まる文字列をエスケープ文字という。エスケープ文字の例をTable 7に示す。

Table 7: エスケープ文字

文字意味
\n改行
\r復帰
\f改頁
\t水平タブ
\v垂直タブ
\b後退
\aベル
\\バックスラッシュ(\記号)
\?疑問符
\'単一引用符
\"二重引用符
\ooo8進数
\xhh16進数

なお,char型は「文字」を表すわけではなく,1バイトの「数値」を表す。つまり文字は数値として保存されていることに注意しよう。

文字列定数

「"」(二重引用符)で囲まれた0個以上の文字の列を,文字列定数という。文字列定数はchar型の配列であり,最後の要素は'\0'である。'\0'のことをヌル文字という。よって,たとえば「"ABC"」という文字列を表現するためには,ヌル文字を含めて4バイトが必要となる。


ライブラリ関数」へ進む

広告


©Toshio Koide 1996-2007.

目次

リンクについて

リンクは御自由にどうぞ。

メール

mail.gif

広告