C言語-文字列処理 |
1.4 ポインタ
配列に格納された文字列を1文字ずつ参照するとき、これまでは配列の添字を変えることで参照してきた。例えば、配列buffに文字列が格納されているとき、先頭の文字はbuff[0]で参照でき、先頭から8文字目はbuff[7]で参照できる。
しかし、一般的に、文字列の各文字を参照するとき、ポインタ変数を使用する。ポインタ変数とは、アドレスを値として持つ変数である。
ポインタ変数の宣言は、通常の変数と同じように宣言するが、変数名の直前に*(アスタリスク)を付ける。
配列名は先頭アドレスを値として持つ定数であり、値を変更することはできない。もし、値を変更すると下図の10バイト分の場所全体が移動することになり不都合が生じる。
それに対してポインタ変数は変数であるから値を変更するような演算も可能である。
char buff[10]; ← 配列名buffは、先頭アドレスの14を値として持つ
char *pt; ← ポインタ変数の宣言
pt = buff; ← ポインタ変数の初期化(ptに14が格納される)
次に、入力文字列を空白で1文字ずつ間隔をあけて表示するプログラムで、配列版とポインタ版を示す。
入力された文字列を1文字間隔で表示する。(配列版)[pro1-11.c]
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: |
#include <stdio.h> #include <string.h> int main(void) { char buff[256]; /* 入力文字列用 */ int i; /* for文用 */ printf("文字列 : "); /* プロンプト表示 */ gets(buff); /* 文字列入力 */ for(i = 0; i < strlen(buff); ++i) /* 文字数分の回数繰り返す */ printf("%c ", buff[i]); /* 1文字ずつ表示 */ printf("\n"); /* 改行 */ return(0); } |
文字列 : abcde |
12行目で文字数の回数だけ表示(13行目)を繰り返す。
13行目の"%c "で1文字表示(%c)したあと空白を1つ表示し、1文字間隔としている。
文字列の最後にはナル文字(\0,終端記号)があるので、12行目は次のようにも記述できる。
for(i = 0; buff[i] != '\0'; ++i)
さらに、ナル文字は文字コードが0であるので、 if, forやwhileで使用する条件では、条件の式が0以外のとき真、0のとき偽になる。したがって、!= '\0' を省略することができる。
for(i = 0; buff[i]; ++i)
入力された文字列を1文字間隔で表示する。(ポインタ版)[pro1-12.c]
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: |
#include <stdio.h> int main(void) { char buff[256], *pb; /* 入力文字列用, ポインタ */ pb = buff; /* ポインタ変数初期化 */ printf("文字列 : "); /* プロンプト表示 */ gets(pb); /* 文字列入力 */ while(*pb != '\0'){ /* ナル文字まで繰り返す */ printf("%c ", *pb); /* 1文字ずつ表示 */ ++pb; } printf("\n"); /* 改行 */ return(0); } |
文字列 : abcde |
7行目でポインタ変数pbに配列buffの先頭アドレスを格納する。ポインタ変数の初期化という。
文字列の終端(最後)には、ナル文字(\0)があるので、12行目でナル文字と等しくない間、繰り返す。ポインタ変数pbの*は、ポインタ演算子といい、pb番地の内容を表す。12行目は次のようにも記述することができる。
while(*pb){
14行目ではポインタ変数pbに1加算して、pbが次の文字のアドレスの値を持つようにする。配列名buffも先頭のアドレスを値として持っているが、配列名buffは定数であるので、値を変更できない。
ポインタ演算子とは逆に、変数の値が格納されているアドレスを取得するアドレス演算子 &がある。関数scanfでデータ入力するとき、scanf("%d", &a); のように記述した。この &a がアドレス演算子を使用した部分で、関数scanfに変数aの値が格納されるアドレスを渡している。関数scanfは入力されたデータを渡されたアドレスに格納している。
配列の内容とその配列用のポインタの示している位置を表示する。[test_pnt.c]
ポインタ変数を使用する上で、ポインタ変数に格納する値(アドレス値)は重要ではない。使用機種やコンパイラによって確保される領域のアドレスが違うからである。
ポインタ変数が、現在、配列のどの位置を示しているかが重要である。このプログラムの関数test_pointerは、呼び出されたときの配列の内容とその配列用のポインタの示している位置を表示するものである。
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: |
#include <stdio.h> /* void test_pointer(char *s, char *p); char *s; 文字列配列(データバッファ) char *p; 文字列配列用のポインタ データバッファの内容を出力し、その時のポインタの位置を $ で示す。 配列操作用のポインタを使用していない場合は、NULLを指定する。 改行文字は ? 、ヌル文字は @ で表示する。 */ void test_pointer(char *s, char *p) { char *w = s; /* ポインタの宣言と初期化 */ printf("BUFFER DATA = "); while(*w){ if(*w == '\n') /* 改行コードとして */ printf("?"); /* ? を表示 */ else printf("%c", *w); ++w; } printf("@\n"); /* NULL = @ */ if(p != NULL){ w = s; /* ポインタの初期化 */ printf("POINTER ($) = "); while(*w){ if(w == p) printf("$"); else printf(" "); ++w; } if(w == p) printf("$\n"); else printf("\n"); } } #ifdef TESTP int main(void) { char buff[256], *pp; /* 入力文字列 */ printf("文字列 : "); /* プロンプト表示 */ gets(buff); /* 文字列入力 */ printf("文字列は%sです\n", buff); /* 入力文字列表示 */ pp = buff; while(*pp){ test_pointer(buff, pp); ++pp; } test_pointer(buff, pp); return(0); } #endif |
文字列 : abcde |
コンパイルは次のように行う。
--- MS-DOS ---
> cc -DTESTP test_pnt.c
--- UNIX ---
% gcc -DTESTP test_pnt.c
関数mainが#ifdef〜#endifではさまれている。これは、コンパイル時に-DオプションでTESTPを指定したとき、または、#define
TESTPの1行を#ifdefより前に入れたときだけコンパイルされる。
この関数test_pointerを他のプログラムでも利用できるように、cc -c test_pnt.c として、オブジェクトファイルを作成しておく。
作成されたオブジェクトファイルの拡張子は、".o"または".obj"である。これ以降、この関数を使用するときは、プログラムの#include文の後に
extern void test_pointer(char *, char *);
を記述する。コンパイルは cc mainfile.c test_pnt.o (または test_pnr.obj)
とする。
このように、2つのファイル別れたプログラムのコンパイル等については、「5.1 ファイルの分割」で説明する。
Copyright © 2001 Hiroshi Masuda |