C言語のポインタとは?基本的な記述法と配列、構造体、関数への渡し方

ENGINEER.CLUB編集部

C言語を扱う上で、ポインタは避けて通れない部分です。上手に使えるようになればプログラミングの幅が広がり、記述行数も少なくすることも可能です。

この記事では、そんなポインタの基礎から実際の使用方法を紹介したいと思います。


1.ポインタとは

1-1.ポインタとは

ポインタを一言で表すと、変数のアドレスを格納している変数になります。アドレスが何かというと、変数のメモリ上の位置(番地)のことを意味しています。

変数を宣言すると、使用するコンピュータのメモリが変数のサイズ分割り当てられます。このメモリ上の位置を表す言葉がアドレスになります。

以下にイメージ図を記します。以下の図における変数”a”のポインタは、10024になります。

ポインタについて解説!

 

※上記の例ではアドレスを10進数として表記していますが、16進数で表示するのが一般的です。

1-2.ポインタの記述方法

ポインタ変数の宣言方法は、変数名の先頭に「*」(アスタリスク)を付けることで、宣言した変数がポインタであることを意味します。

また、通常の変数のアドレスを取得する場合は、変数名の先頭に「&」(アンパサンド)を付けます。

簡単な例を以下に記します。

int  number;         // 通常の変数の宣言
int *p_number;     // ポインタ変数の宣言

p_number = &number;  // 変数numberのアドレスをp_numberに代入

上記の例では、ポインタ変数p_numberに、変数numberのアドレスを代入する処理となります。また、変数の型としてint型を使用していますが、型は全ての型で使用する事が可能です。

例えば、構造体をtypedefで型宣言した構造体型にも適用可能です。構造体のポインタについては2-2章で後述しています。


2.ポインタの使い方

2-1.ポインタと配列

配列として宣言した変数もポインタとして扱うことができます。

宣言方法は他のポインタと同様に、「*」を先頭に付加するだけで、使用する際も先頭に&を付加するだけです。

char  c_str1[5];  // 通常の変数の宣言
char *p_str1;     // ポインタ変数の宣言

p_str1 = &c_str1[0];  // 変数c_str1のアドレスをp_str1に代入

ここまでは配列でないポインタと変わりませんが、配列の場合、配列の要素数を表す大カッコ([n])を省略するとアドレス(つまり配列変数のポインタ)を指すことになります。

ですので、次のように記述しても同じになります。

char  c_str1[5];  // 通常の変数の宣言
char *p_str1;     // ポインタ変数の宣言

p_str1 = c_str1;  // 変数c_str1のアドレスをp_str1に代入

ここでの注意点は、[n]を省略した場合は必ず先頭の要素であることです。つまり、配列のn番目を指定したい場合は省略することはできません。

 また、strcpy、strcmp等、文字列を操作する標準関数を目にすることがあるかと思います。これらの関数で引数に使用している型はchar型のポインタになります。

char buf1[4], buf2[4];
int result;

strcpy(buf1, “AAA”);   // char型の配列変数に、「AAA」という文字列を代入
strcpy(buf2, “AAB”);   // char型の配列変数に、「AAB」という文字列を代入

out = strcmp(buf1, buf2);   // buf1とbuf2を比較し、比較結果をoutに代入

2-2.ポインタと構造体

1-2章で少し触れましたが、構造体もポインタとして使用する事ができます。構造体ポインタと通常の構造体と異なる点は、メンバ変数へのアクセスするための演算子が異なる点です。

通常のポインタでメンバ変数にアクセスする場合は、「.」(ドット演算子)を使用しますが、ポインタで宣言された構造体のメンバ変数にアクセスするには「->」(アロー演算子)を使用します。

以下に例を記します。

// 構造体定義
typedef struct {
  int    m_num1;
  int    m_num2;
} struct_sample;

void func1(struct_sample *inout)
{
  struct_sample * p_struct;   // 構造体型のポインタの宣言

  if(inout == NULL)
  {
    return;
  }

  // ポインタを代入
  p_struct = (struct_sample*)inout;

  if(p_struct->m_num1%10 < 5) // メンバ変数m_num1と10の剰余が5未満の場合
  {                                                 // ポインタ構造体のメンバ変数へのアクセスはアロー演算子を使用
    // m_num1を3倍した数をm_num2に代入
    p_struct->m_num2 = p_struct->m_num1 * 3;
  }
}

int main()
{
  struct_sample  strct_sample;  // 構造体の宣言

  // strct_sample構造体を0で初期化(各メンバ変数に0を代入する処理と同等)
  memset(strct_sample, 0x00, sizeof(strct_sample));

  strct_sample.m_num1 = 102;  // 構造体のメンバ変数m_num1に102を代入
// メンバ変数のアクセス方法はドット演算子を使用
  func1(&strct_sample);             // 構造体をポインタで関数に渡す

  printf(“m_num1の値は%dです\n”, strct_saple.m_num1);
  printf(“m_num2の値は%dです\n”, strct_saple.m_num2);

【出力結果】

m_num1の値は102です
m_num2の値は306です

上記の構造体を使用した例は、mainで宣言した構造体をポインタで関数func1に渡し、関数内でさらに加工するというものになります。

ポインタは構造体のある場所を指しているので、双方向で変数を編集することが可能です。

2-3.関数ポインタ

関数ポインタとは、関数が格納されたアドレスです。

関数も変数と同様にポインタとして使用する事が可能です。これの意味するところは、関数ポインタを変更することで、同じ関数ポインタで別の関数を呼び出すことができるということです。ただし、関数のプロトタイプを合わせることが必要です。

それでは、使い方から見ていきましょう。

typedef double (*func)(double, double);   // 関数ポインタの型宣言

// 関数ポインタに登録する関数1
int rectangle(double num1, double num2)
{
  // 引数1と引数2を辺とした長方形の面積を返す
  return(num1 * num2);
}

// 関数ポインタに登録する関数2
double circle(double  num1, double num2)
{
  // 引数1を半径とした円の面積を返す
  num1 * num1 * 3.14;
}

int main()
{
  double  num1, num2, result;
  func *p_func;

  num1 = 10;
  num2 = 7;

  p_func = &rectangle;                  // 関数ポインタにrectangleのポインタを代入
  result = p_func(num1, num2);
  printf(“関数ポインタp_func()の戻り値は%.2fです\n”, result);

  p_func = &circle;                         // 関数ポインタにcircleのポインタを代入
  result = p_func(num1, num2);
  printf(“関数ポインタp_func()の戻り値は%.2fです\n”, result);

【出力結果】

関数ポインタp_func()の戻り値は70.00です
関数ポインタp_func()の戻り値は314.00です

上記の例において、2番目の円の面積を求める関数では引数2を使用していませんが、問題はありません。

関数ポインタを使用するためには、型を合わせる必要があるため、使用しない引数があっても問題はないのです。つまり、複数の関数を関数ポインタで使用する場合は一番引数が多い関数に合わせることで、関数ポインタを使用する事ができます。


3.さいごに

以上、C言語におけるポインタの基本的な使い方をまとめました。C言語を扱う上でポインタは必須であり、ポインタを覚えればこれほど便利なものはありません。

C言語は最近の高級言語では見えない部分であるメモリとの関連性などが分かりやすい言語であり、ポインタを理解することがその一歩ではないかと思います。

尚、本記事内で例として使用したコードはあくまで使い方を示すためのものであるため、実務での使用方法に合わないものを含んでいます。

私たちは、全てのエンジニアに市場価値を高め自身の望む理想のキャリアを歩んでいただきたいと考えています。もし、今あなたが転職を検討しているのであればこちらの記事をご一読ください。理想のキャリアを実現するためのヒントが見つかるはずです。

『技術力』と『人間力』を高め市場価値の高いエンジニアを目指しませんか?

私たちは「技術力」だけでなく「人間力」の向上をもって遙かに高い水準の成果を出し、関わる全ての人々に感動を与え続ける集団でありたいと考えています。

高い水準で仕事を進めていただくためにも、弊社では次のような環境を用意しています。

  • 定年までIT業界で働くためのスキル(技術力、人間力)が身につく支援
  • 「給与が上がらない」を解消する6ヶ月に1度の明確な人事評価制度
  • 平均残業時間17時間!毎週の稼動確認を徹底しているから実現できる働きやすい環境

現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。

まずは以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。

コメント

文系IT未経験歓迎!【23卒対象】新卒採用のエントリーを受付中!
文系IT未経験歓迎!
【23卒対象】新卒採用のエントリーを受付中!