この記事を書いている2020年の5月に、C言語がプログラミング言語の検索回数世界NO1の座に輝いている((PCI:TIOBE Programming Community Index)こともあり、何回かにわけてC言語を解説します。
実業務で長年つきあってきたC言語について、実践的な使い方を説明しようと考えています。
プログラミング言語を習得するときの壁
プログラミング言語習得で最初につまずく箇所
以前、某大手メーカーでプログラミングの講師を数年勤めていた方から聞いたことがあります。
C言語も含めたプログラミング言語を教えていると、最初につまずくのは、配列、その次が構造体などのデータの扱い、関数、そしてポインタという順番なのだそうです。
なる程そうかとも思います。
実際、全くのはじめてのときに、配列という概念がでてきた瞬間に、頭にクエスチョンマークがたくさん並びそうです。
さらに構造体はやはり本当の初心者なら、最初はできないものだし、関数は悩むし、ポインタとなると数カ月C言語をやっているという人なら、理解してない人も結構いるかもしれません。
プログラミング C言語の配列
さて、その配列。
int A[10];
A[0] , A[1], .... , A[9]
という10個の整数型(int)を束ねて扱うものなら、整数型の配列になります。
文字の添え字は、0から始まります。
これが、文字(char)型だと、文字列になりますね。
char strHello[5]; strHello[] = 'H' 'E' 'L' 'L' 'O' '\0' strHello[0] = 'H' strHello[0] = 'E' strHello[0] = 'L' strHello[0] = 'L' strHello[0] = '\0'
文字列の最後の文字に'\0'(ヌル文字)をいれているのが、C言語で文字列を取り扱うときの暗黙の了解となっています。
C言語で提供されている標準ライブラリの処理は、このヌル文字をみて、文字列の最後かどうかを判断しています。
例えば、文字列の長さを調べる関数にstrlenというものがあります。
その関数では、文字列を一文字ずつみていって、ヌル文字'\0'を見つけて、そこまでの文字が文字列の長さだと認識するわけです。
int i; i = 0; for (;;) { if (strHello[i] == '\0') { /* ヌル文字の場合 */ printf("文字の長さは %d です\n", i); break; /* ループを抜ける */ } else { ++i; /* iを1増やす */ } }
言語による違いといえば、添字を囲むのに[] を使うか() を使うか、配列の添字が0からはじまるか1からはじまるかの違いくらいですが、C言語以降にでてきたプログラミング言語は、ほとんどがゼロを起点としています。
FORTRAN、COBOL、BASIC 等の言語は1起点です。EXCEL操作などにつかうマクロ言語であるVBAは、BASICが起源になっているので、1はじまりです。
Java、PHP、Pythonなどは、0起点になっています。
以下が、配列の書き方の例です。
#define MAX_ARY 10 int i; int iAry[MAX_ARY]; iAry[0] = 1; iAry[1] = 2; iAry[2] = 3; ..... for (i = 0; i < MAX_ARY; i++) { printf("iAry[%d] = %d\n", i, iAry[i]); }
配列は、添え字をつけて、for文と同居させるのが,よくある使い方になります。
C言語の第2の壁、構造体
さて、配列の次の関門は、構造体です。
構造体
いくつかのデータをプログラマの都合のいいようにまとめて、使いまわせるという点で極めて便利なものです。
プログラムを書く人が定義できるデータの型です。
名前と年齢の対を扱うパターンを示してみます。
char szName[128]; int iAge; で、それが10人の場合、前回の配列を使用して、 char szName[10][128]; int iAge[10]; で、1人目は、 szName[0] と iAge[0] で示される。 すなわち、 strcpy(szName[0], "taro yamada"); iAge[0] = 30; strcpy(szName[1], "hanako sato"); iAge[1] = 27; strcpy(szName[2], "ichiro tanaka"); iAge[2] = 38; ... for (i = 0; i < 10; i++) { printf("Name=[%s] Age=[%d]\n2, szName[i], iAge[i]); } これで、10人分の名前と年齢が表示されます。
同じ処理を構造体で書いてみます。
struct Human { char szName[128]; /* 名前 */ int iAge; /* 年齢 */ }; struct Human human[10]; strcpy(human[0].szName, "taro yamada"); human[0].iAge = 30; strcpy(human[1].szName, "hanako sato"); human[1].iAge = 27; strcpy(human[2].szName, "ichiro tanaka"); human[2].iAge = 38; ... for (i = 0; i < 10; i++) { printf("Name=[%s] Age=[%d]\n2, human[i].szName, human[i].iAge); }
とやる方が、利用するデータがはっきりするし、人間にとってはわかりやすくなります。
ところで、構造体の場合、typedef 文がよく使われるので、合わせて記憶しておくといいです。
typedef struct Human THUMAN;
と書けば、struct Human という型が THUMAN という型であらわせるので、
THUMAN human[10];
で、先の10人の人間の集まりを示せることになります。
また、よく使われるのに、typedef と struct の定義をまとめて書いてしまう次の形式があります。
typedef struct Human { char szName[128]; /* 名前 */ int iAge; /* 年齢 */ } THUMAN;
これは、あくまでも構造体の定義と型の定義という2つを同時にやっているだけです。
さらに、構造体のタグ名(この例ではHumanというやつ)を省略して、
typedef struct { char szName[128]; /* 名前 */ int iAge; /* 年齢 */ } THUMAN;
というタイプもよく見かけますが、個人的には関連するエラーが発生した時に、コンパイラによっては、unkown ...等というメッセージがでたりしますので、タグ名は省略できますが、しない方がよさそうです。
今回はここまでにします。
C言語の話は、この後も何回かに分けて投稿しますので、興味のある方は引き続きよろしくお願いします。