C言語講座4回目。
今回は、C言語の大きな壁でもあり、C言語の威力を発揮するところでもあるポインタについて、書いてます。
ここを理解することで、C言語の世界が一つ広がります。
C言語でプログラミング:ポインタ
ポインタの概念
C言語の学習には、大きな山があります。ポインタです。
わかってしまえば、便利なことこの上ないものですが、C言語以外ではあまり扱われなくなりました。
まずは、基本となる文字ポインタから説明します。
char *p;
アスタリスクをつけて宣言すると、char つまり文字そのものではなく、charを指し示すアドレスになる、というのがポインタの意味です。
つまり、ポインタ自身は、この場合、char 型を指し示せるもので、それ自身は、中身の無い空っぽであるということ。
あくまでも、指し示すことができるだけのものです。
これを忘れると、ついつい、
char *p;
strcpy(p, "hello");
とかやってしまいがちです。
この場合、p は、何もまだ指し示していないので、プログラムはエラーになってしまいます。
エラーになれば、いいのですが、場合によっては、プログラムの他の場所で使っているメモリ領域に、hello, world! という文字列が突然コピーされてしまいますので、プログラムの動作が保証できなくなってしまいます。
実際に、この2行だけをメイン関数に書いて、実行してみると、
セグメンテーションフォルトの例外を発生して停止します。
例外を発生させないためには、下記のように、ポインタが指し示す領域を定義しておく必要があります。
char* p; char buf[128]; p = buf; /* p でbuf を指し示した */ strcpy(p, "hello"); printf("%s\n", p); printf("%s\n", buf); printf("%c\n", *p);
これなら、pは、きちんと buf という128個の連続するchar の領域を指し示しているので、問題なし。
char buf[128] の buf 単体で扱えば、128個の連続する領域の先頭を指し示すというのが、C言語のルールですので、上記プログラムを実行すると、下記のようになります。
hello
hello
h
このようにpは、bufを指し示しているので、pとbufの2行は同じ出力になります。
p に代入したのは、bufが指し示す場所(アドレス) なので、実際には buf の方に hello と格納されているわけです。
また、pが指し示しているまさにその場所の中身を取り出すには、アスタリスクをつければ可能です。
この場合、hello の先頭のhそれ自身が文字として取り出されるので、3行目は、helloの一文字目にあたる、hと表示されています。
ポインタで文字列操作
文字列操作はポインタで、というのは、
char* p; char buf[512]; strcpy(buf, "AAA,BCC"); さて、この文字のなかでカンマを探すのは、 p = buf; while (*p != ',') { ++p; } ++p;/*カンマをスキップ */ printf("%s\n", p); /* BCC と表示される */
つまり、ポインタをインクリメントしていけば、次の文字を指し示してくれます。
配列で同じことをするのであれば、インデックスでやります。
int i; char buf[512]; strcpy(buf, "AAA,BCC"); for (i = 0; buf[i] != ','; i++) ; ++i; printf("%s\n", buf + i);
ポインタは、文字列操作だけのものではなく、メモリの領域を指し示すモノになります。
文字列だけではなく、他の型に対してももちろん可能です。
int iary[8]; int* ip; /* 整数型へのポインタ */ ip = iary;
動的なメモリ確保でポインタを活用
ポインタが活躍するのは、動的(実行時)に領域を確保する場合です。
たとえば、下記は、人が入力したものを覚えておくプログラムですが、何文字入力されるかわからないものをずっと覚えておくにはメモリがたくさん必要になります。
入力された分だけメモリ領域を確保(malloc)しておけば、必要な分だけのメモリ利用になります。
#include < stdio.h > #include < stdlib.h > #include < string.h > #define MAX_RIREKI 256 int main() { char inbuf[512]; char *pInp[MAX_RIREKI]; int i, nlen, maxInp; maxInp = 0; for (;;) { printf("入力?:"); gets(inbuf); /* 読み込み */ nlen = strlen(inbuf); if (nlen <= 0) { printf("入力がなかったため、終了します!\n"); break; } pInp[maxInp] = malloc(nlen + 1); /* 動的に必要分だけメモリ確保 + 1 は、ヌル文字分です これをしなければ、512 * MAX_RIREKI の分だけメモリが必要 */ strcpy(pInp[maxInp], inbuf); maxInp++; if (maxInp >= MAX_RIREKI) { printf("**履歴が最大になったため終了します**\n"); break; } } /* end of for */ /* 入力履歴の表示 */ for (i = 0; i < maxInp; i++) { printf("%d:[%s]\n", i+1, pInp[i]); } return 0; }
実行結果
入力?:hello 入力?:bye 入力?: 入力がなかったため、終了します! 1:[hello] 2:[bye]
ポインタは、ここまでに示したような、文字列操作やメモリ確保以外にもいろいろな用途で利用ができます。
C言語がシステム的なプログラミングもできるのは、ポインタで領域にアクセスしてその領域を書き換えるなどの処理でも使えるからですが、基礎講座という説明範囲を超えてしまうので、今回はここまでにします。
C言語の話は、この後も何回かに分けて投稿しますので、興味のある方は引き続きよろしくお願いします。