.net column
.NET開発者のためのブログメディア

C言語のポインタのメリット5選|使う際のポイント7選や注意点も解説

 
C言語のポインタのメリット5選|使う際のポイント7選や注意点も解説
SE
C言語のポインタとはなんですか?
PM
変数のアドレスを記憶する変数のことを指します。

C言語のポインタとは?


ポインタとは、変数のアドレスを示すためのものです。ポインタを使用することで、アドレス先や配列の要素の値の取得や変更ができるようになります。本記事ではC言語のポインタについて詳しくご紹介していきます。

ポインタは初心者の最初の壁

C言語の学習にあたって、初心者が最初につまずきやすい壁と言われるのがポインタという文法です。一度分かってしまえばなんてことはないのですが、人によっては理解が難しいかもしれません。ポインタについてわかりやすく解説しましょう。C言語のポインタを理解する前にまず、コンピュータのメモリの仕組みについて簡単に知っておく必要があります。メモリには数値のデータが保存されていますが、その場所は「アドレス」という値で表されます。アドレス(address)とは日本語で「住所」という意味なので、データのある場所を示すのがアドレス、と考えれば理解できるでしょう。

ポインタの記述の仕方

ポインタを記述する場合、変数の先頭部分に「*」を付けることで、その変数をポインタとして扱えるようになります。たとえば、下記のように宣言すれば、変数p_numberはポインタとなります。

int *p_number;

C言語でポインタを使うメリット5選


ポインタを利用することで、目的の場所のアドレスを簡単に指定できるようになります。それでは、C言語でポインタを利用することには具体的にどのようなメリットがあるのでしょうか。ここではポインタを使うメリットについてご紹介します。

1:メモリを節約できる

ポインタを使うと、構造体をコピーする必要がなくなります。特に変数の数が多いオブジェクトはコピーのコストも大きくなりますが、ポインタを利用すればポインタ変数にアドレスを代入することでコピーが行われなくなるため、メモリを大きく節約できます。

2:プログラムの処理速度を向上できる

プログラムの処理の中でデータを何度も繰り返しコピーしていると、プログラムの処理速度は遅くなります。しかしポインタを利用すれば不要なコピーを行わなくなるため、処理速度を向上することが可能になります。

3:処理内容を後からでも変更できる

ポインタは1つのポインタで複数の関数を利用できます。特にポインタを使用して関数の引数に参照渡しを行えば、後からでも処理内容を簡単に変更することが可能になります。

4:大規模なプロジェクトに向いている

多くの大きなオブジェクトが存在するようなプログラムでは、ポインタを利用しなければプログラムとして使えないくらいに処理が遅くなります。そのため、大規模なプロジェクトではポインタの利用は必須だと言えるでしょう。

5:複数の変数を1つの関数で処理変更できる

ポインタは、1つのポインタ変数によって複数の関数の中から利用する関数を選択することが可能です。また、1つのポインタ変数でオブジェクトなどの構造体すべてのメンバを取得、変更することもできます。

C言語のポインタを使う際のポイント7選


C言語でポインタを利用する場合、ポインタについてよく理解する必要があります。ここではC言語のポインタを使う際のポイント7選をご紹介しますので、ぜひ参考にしてください。

1:アドレスを示すのがポインタ

頭の中で1,2,3…とそれぞれに番号が振られているロッカーを思い浮かべてください。アドレスはその番号のことなのです。そしてポインタとは、その番号を示すための印(しるし)だとご理解ください。ロッカーの中には数値のデータが入っていて、ポインタという印を使い、割り振られた番号を指定すれば、中身を取り出せるということです。C言語のプログラムで説明しましょう。以下のコードでは、valueは普通の変数です。そしてptr_valがポインタ変数です。ポインタはこのように宣言する時に*をつけます。

int value = 100;
int *ptr_val;

2:普通の変数に&をつければアドレスを取得できる

そして以下のように普通の変数の先頭に&を付けてポインタ変数に代入すると、普通の変数が入っているメモリのアドレスをポインタに渡せます。

ptr_val = &value;

両方の変数の中身を画面に表示してみましょう。

printf(“valeの中身は%d\n”,value);
printf(“ptr_valの中身は%d\n”,*ptr_val);

ポインタの中身を取り出す時は、先頭に*をつけます。結果は以下のようになります。

valueの中身は100
ptr_valの中身は100

同じ値になりましたね。ptr_valがvalueと同じ場所のアドレスを指しているからです。

3:ポインタ変数の中身はアドレス

理解して欲しいのは、valの中身の100をptr_valに代入したのではなく、valのアドレスをptr_valに渡したということです。試しにアドレスも表示してみましょう。

printf(“valeのアドレスは%x\n”,&value);
printf(“ptr_valのアドレスは%x\n”,ptr_val);

結果は以下のようになります。なおアドレスの値は処理系やメモリの状況によって変わりますが、どちらも同じ値の16進数が表示されるのは変わりません。

valueのアドレスは0x5ff4a3d8
ptr_valの中身は0x5ff4a3d8

4:配列変数とポインタの関係

次に配列変数のポインタとしての使い方を説明します。

int ary[] = { 100, 200, 300, 400, 500 };

実はこの時点ですでにaryはポインタ変数なのです。以下のようにすればそれがわかります。

printf(“ary[0]の中身は%d\n”,ary[0]);
printf(“*aryの中身は%d\n”,*ary);

結果は以下のようになります。

ary[0]の中身は100
*aryの中身は100

配列宣言をした時点で、ポインタを宣言した事と同じ意味を持つのです。

5:ポインタによる配列変数へのアクセス

ポインタ変数は値を加減して操作できます。ただし配列変数に対し直接それを行うのは危険なのでやってはいけません。以下のように配列とは別のポインタを用意します。

int ary[] = { 100, 200, 300, 400, 500 };
int *ary_ptr;

ary_ptr = ary; // またはary_ptr = &ary[0];でも可
ary_ptr++;

printf(“*ary_ptrの中身は%d\n”,*ary_ptr);

こうすると、結果は以下になります。ポインタを加算してずらすことができたということですね。

*aryの中身は200

6:ポインタによる文字列へのアクセス

C言語では文字列はchar型の配列という扱いなので、以下のようにポインタでも扱うことができます。

char str1[] = “”文字列No.1″”;
char *str2 = “”文字列No.2″”;

printf(“”%s\n%s\n””,str1,str2);

結果は以下のように表示されます。

文字列No.1
文字列No.2

数値の配列変数の応用で文字列も同じように扱うことができます。

7:ポインタによる構造体へのアクセス

ポインタは構造体にも以下のようにしてアクセスできます。

typedef struct {
int age;
char *name;
} PERSON;

PERSON st_per;
PERSON *ptr_st_per;

ptr_st_per = &st_per;
ptr_st_per->age = 18;
ptr_st_per->name = “太郎”;

printf(“年齢 %d 名前 %s\n”,ptr_st_per->age,ptr_st_per->name);

「->」という新しい文法が登場しましたが、これはアロー演算子と言います。構造体にはこのようにアロー演算子でアクセスするのが一般的です。

C言語のポインタの応用と注意点3選


ポインタにはさまざまなメリットがありますが、一方で使用する際には注意点も存在します。ここではC言語のポインタの応用やポインタの注意点についてご紹介しますので、参考にしてみてください。

1:ポインタの実践的な使い方

ここまで読んできて「ポインタって何の役に立つの?」と思う人もいるでしょう。ところがポインタはC言語のプログラムには欠かせないのです。例えば以下のような場合に必要になります。

void func(void) {
int a = 100,b = 200, *c;
add(a, b, c);
printf(“結果 %d\n”,*c);
}

void add(int val1, int val2, int *val3) {
*val3 = val1 + val2;
}

結果 300

このようにポインタで他の関数に変数のアドレスを渡して、別の関数の中で渡した変数の値を操作することができるのです。ポインタがないとプログラムの自由度が大幅に下がりますね。

2:バッファオーバーラン

便利なポインタですが、アドレスを操作するために危険性もあります。ポインタのよくあるバグの一つに以下のような「バッファオーバーラン」があります。

char buffer[] = { 0, 1, 2, 3, 4, 5 }; // 最後に-1を入れ忘れている
char *p_buf;

p_buf = buffer;
while(*p_buf!=-1) {
printf(“%d\n”,*p_buf++);
}

このプログラムは-1が来るまで画面にbufferの中身を表示しますが、このようにデータの末尾に-1が無いと終わることができず無限ループとなり、システムが暴走してしまいます。

バッファオーバーランの回避方法

上のプログラムの場合、例えばこのようにサイズチェックを入れればループが止まらないということはなくなります。

char buffer[] = { 0, 1, 2, 3, 4, 5 }; // 最後に-1を入れ忘れている
char *p_buf;
int size = sizeof(buffer);

p_buf = buffer;
for(int i = 0; i < size; i++) {
if (*p_buf == -1) break;
printf(“%d\n”,*p_buf++);
}

C言語を使う職場では、この例のようにプログラムしなさいというコーディングルールがあるのが普通ですね。

3:ローカル変数を戻り値にする

もう一つポインタに絡むバグを紹介します。以下のようなプログラムを実装してはなりません。

void func(void) {
printf(“%s\n”,get_str());
}

*char get_str() {
char *str = “abc”;
return str;
}

これを実行しても問題なく動作する事もあると思います。何が悪いのでしょうか。それはget_str関数のローカル変数strの有効範囲はget_str内だけなので、他の関数に処理が移ると内容が保証されないのです。そのためデータが破壊されている可能性があります。

ローカル変数を戻り値にしない

上のようなことを行うなら、以下のようにしましょう。

void func(void) {
char buffer[4];
get_str(buffer, sizeof(buffer));
printf(“%s\n”,buffer);
}

void get_str(char *buf, int size) {
if (size < 4) return;
*buf++ = ‘a’;
*buf++ = ‘b’;
*buf++ = ‘c’;
*buf = ‘\0’;
}

関数を呼ぶ側で入れ物の変数を用意して、ポインタ渡しをすれば良いということです。

SE
C言語って難しいですが、マスターするととても便利な言語ということですね。
PM
そうですね。ぜひ、C言語を学んでいきましょう!

転職やキャリアアップに有利なC言語のポインタを使いこなそう


ここまで読んできて、「ポインタって怖い、C言語って難しい」と思った人もいるかもしれません。確かにC言語はBASICやJavaやC#などと違い、メモリに直接アクセスするのでより注意を要する言語だと言えます。しかしC言語はその分だけ処理能力が高速のため、組み込み系やゲーム開発ではC言語やC++言語が主流になっています。ポインタを使いこなせばC言語を極めたものと同然と言えるので、是非マスターしてくださいね。


.NET分野でのキャリアアップをお考えの方は、現在募集中の求人情報をご覧ください。

また、直接のエントリーも受け付けております。

エントリー(応募フォーム)

Search

Popular

reccomended

Categories

Tags