🖥️

【C言語超入門】 第19回 配列とポインタ

2024/12/25に公開

https://youtu.be/YgaZTze2F88

四国めたん
\textcolor{pink}{四国めたん: }教師役ですわ

ずんだもん
\textcolor{lime}{ずんだもん: }生徒役なのだ

\footnotesize \textcolor{pink}{四国めたん:} 皆さん、こんにちは。四国めたんです

\footnotesize \textcolor{lime}{ずんだもん:} ずんだもんなのだ。こんにちはなのだ

\footnotesize \textcolor{pink}{四国めたん:} 今回もC言語のお勉強をしていきましょう

\footnotesize \textcolor{lime}{ずんだもん:} レッツゴーなのだ

\footnotesize \textcolor{pink}{四国めたん:} 前回は ポインタ についてお話ししましたわ

\footnotesize \textcolor{lime}{ずんだもん:} すこし分かりづらかったのだ

\footnotesize \textcolor{pink}{四国めたん:} まぁ、少しずつ理解していくしかありませんわね

\footnotesize \textcolor{pink}{四国めたん:} 今回は 配列ポインタ の関係についてお話ししますわ

\footnotesize \textcolor{lime}{ずんだもん:} よろしくなのだ

配列のアドレスは?

\footnotesize \textcolor{pink}{四国めたん:} 配列 については以前にお話ししましたわ

\footnotesize \textcolor{lime}{ずんだもん:} おぼえているのだ

\footnotesize \textcolor{pink}{四国めたん:} 今回は 配列 のアドレスについてお話ししますわ

\footnotesize \textcolor{lime}{ずんだもん:} 配列 の各要素についても、通常の変数のようにアドレスを取得できるのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、通常の変数と同様、 配列 の各要素に アドレス演算子 &を使うことで得ることができますわ

\footnotesize \textcolor{pink}{四国めたん:} 具体的に見ていきましょう

#include <stdio.h>

void main() {
  char c[] = {'a', 'b', 'c'};

  for (int i = 0; i < 3; i++) {
    printf("c[%d]のアドレスは0x%pです。\n", i, &c[i]);
  }
}

各要素のアドレス(char)

\footnotesize \textcolor{lime}{ずんだもん:} 各要素のアドレスが表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} char型配列 の場合は、各要素が1バイトなので、アドレスは1ずつ変化していますわね

\footnotesize \textcolor{lime}{ずんだもん:} 他の型の 配列 の場合はどうなのだ?

\footnotesize \textcolor{pink}{四国めたん:} では int型配列 を見てみましょう

#include <stdio.h>

void main() {
  int a[] = {1, 2, 3};

  for (int i = 0; i < 3; i++) {
    printf("a[%d]のアドレスは0x%pです。\n", i, &a[i]);
  }
}

各要素のアドレス(int)

\footnotesize \textcolor{lime}{ずんだもん:} アドレスが4ずつ変化しているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、 int型配列 の場合は、各要素が4バイトなので、アドレスは4ずつ変化していますわ

\footnotesize \textcolor{pink}{四国めたん:} ところで、 配列 の要素のアドレスではなく、配列の名前のみを指定するとどうなるでしょうか?

\footnotesize \textcolor{lime}{ずんだもん:} わからないのだ

\footnotesize \textcolor{pink}{四国めたん:} とりあえず確認してみましょう

#include <stdio.h>

void main() {
  char c[] = {'a', 'b', 'c'};
  int a[] = {1, 2, 3};

  printf("c[0]のアドレスは0x%p、cのアドレスは0x%pです。\n", &c[0], c);
  printf("a[0]のアドレスは0x%p、aのアドレスは0x%pです。\n", &a[0], a);
}

配列名とアドレス

\footnotesize \textcolor{lime}{ずんだもん:} 配列 の名前のみの場合と 配列 の先頭の要素のアドレスが一致しているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、 配列 の名前のみを指定する場合、 アドレス演算子 &を指定しなくても、アドレスを取得できるのですわ

\footnotesize \textcolor{pink}{四国めたん:} そして 配列 の先頭の要素のアドレスと同じになりますわね

配列とポインタは似ていますよね

\footnotesize \textcolor{pink}{四国めたん:} ところで 配列 の名前がアドレスを示すのって何かに似ていませんか?

\footnotesize \textcolor{lime}{ずんだもん:} ポインタ?

\footnotesize \textcolor{pink}{四国めたん:} そう、ポインタですわ

\footnotesize \textcolor{pink}{四国めたん:} ポインタ自体は何も演算子を付けなければアドレスを指しますわ

\footnotesize \textcolor{lime}{ずんだもん:} ということは、ポインタを使って 配列 の要素にアクセスできるのか?

\footnotesize \textcolor{pink}{四国めたん:} 鋭いですわね

\footnotesize \textcolor{pink}{四国めたん:} ということで、ポインタに 配列 のアドレスを代入して各要素にアクセスしてみましょう

#include <stdio.h>

void main() {
  char c[] = {'a', 'b', 'c'};
  char* pc = c;

  for (int i = 0; i < 3; i++) {
    printf("c[%d]の値は%cです。\n", i, pc[i]);
  }
}

ポインタで要素指定

\footnotesize \textcolor{lime}{ずんだもん:} 問題なく実行できたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、ポインタを 配列 のように角括弧[]による要素指定でアクセスすることができましたわ

\footnotesize \textcolor{lime}{ずんだもん:} でも、本来のポインタで値にアクセスするための 間接演算子 *は使えないのか?

\footnotesize \textcolor{pink}{四国めたん:} 使えますわね

\footnotesize \textcolor{pink}{四国めたん:} 例えば*(p + i)のように、ポインタpにインデックスiを足したアドレスに、 間接演算子 *を使いますわ

\footnotesize \textcolor{lime}{ずんだもん:} 具体的におねがいしたいのだ

\footnotesize \textcolor{pink}{四国めたん:} とりあえず書き換えてみましょう

#include <stdio.h>

void main() {
  int a[] = {1, 2, 3};
  int* pa = a;

  for (int i = 0; i < 3; i++) {
    printf("a[%d]のアドレスは0x%pで値は%dです。\n", i, (pa + i), *(pa + i));
  }
}

ポインタと*演算子

\footnotesize \textcolor{lime}{ずんだもん:} たしかにポインタにインデックスを足すと、各要素のアドレスになっているのだ

\footnotesize \textcolor{pink}{四国めたん:} int型 のポインタですので、 配列 と同様にインデックスが1変わるとアドレスが4バイト分変化しますわ

\footnotesize \textcolor{lime}{ずんだもん:} そして 間接演算子 *を使って、 配列 の要素の値を取得できているのだ

配列とポインタは別ものです

関数に配列やポインタを使おう

\footnotesize \textcolor{pink}{四国めたん:} ここで一歩進んで、配列とポインタを関数で使ってみましょう

\footnotesize \textcolor{lime}{ずんだもん:} 関数の引数に配列やポインタを使うのか?

\footnotesize \textcolor{pink}{四国めたん:} それもありますが、戻り値を配列やポインタにするとどうなるかも確認してみましょう

\footnotesize \textcolor{lime}{ずんだもん:} 普通に使えるのではないのか?

\footnotesize \textcolor{pink}{四国めたん:} そうでもないのですわ

関数の引数に配列を使おう

\footnotesize \textcolor{pink}{四国めたん:} ところで、前回、関数の引数や戻り値にポインタを使った例を確認しましたわ

\footnotesize \textcolor{lime}{ずんだもん:} おぼえているのだ

\footnotesize \textcolor{pink}{四国めたん:} 今回は、まず関数の引数に配列を使う場合の例を見てみましょう

#include <stdio.h>

int add(int a[]) {
  int c = a[0] + a[1];
  return c;
}

void main() {
  int a[] = {1, 2, 3};
  int c = add(a);

  printf("配列を使った足し算の結果は%dです。\n", c);
}

引数に配列を使った関数

\footnotesize \textcolor{lime}{ずんだもん:} 「配列を使った足し算の結果は3です。」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、add関数では、渡された配列の0番目と1番目の要素を足した値を返していますわ

\footnotesize \textcolor{lime}{ずんだもん:} 関数の引数についての説明をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

\footnotesize \textcolor{pink}{四国めたん:} 関数の引数に配列を使う場合は、引数名に角括弧[]を付けますわ

\footnotesize \textcolor{lime}{ずんだもん:} 要素数の指定は、しなくてもいいのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、要素数の指定は行わなくてもかまいませんわ

\footnotesize \textcolor{lime}{ずんだもん:} 関数の呼び出し側の引数の指定はどうするのだ?

\footnotesize \textcolor{pink}{四国めたん:} 関数の呼び出し側では、引数として配列名のみを指定するようにしますわ

関数のポインタ型の引数に配列を渡しましょう

\footnotesize \textcolor{pink}{四国めたん:} 次に関数の引数にポインタ型を指定した場合に、配列を渡す例を見てみましょう

\footnotesize \textcolor{lime}{ずんだもん:} 関数のポインタ型の引数にポインタを渡せるなら配列も渡せるはずなのだ

#include <stdio.h>

int add(int* pa) {
  int c = *pa + *(pa + 1);
  return c;
}

void main() {
  int a[] = {1, 2, 3};
  int c = add(a);

  printf("ポインタを使った足し算の結果は%dです。\n", c);
}

引数にポインタを使った関数

\footnotesize \textcolor{lime}{ずんだもん:} 「ポインタを使った足し算の結果は3です。」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、add関数では、渡されたポインタの0番目と1番目の要素を足した値を返していますわ

\footnotesize \textcolor{lime}{ずんだもん:} 関数の引数についての説明をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

\footnotesize \textcolor{pink}{四国めたん:} 今回の関数では、引数の型にポインタ型を使っていますわ

\footnotesize \textcolor{lime}{ずんだもん:} 前回の例では、この引数に変数のアドレスを渡していたのだ

\footnotesize \textcolor{pink}{四国めたん:} そして関数の呼び出し側で配列を渡す場合には、引数として配列名のみを指定するようにしますわ

\footnotesize \textcolor{lime}{ずんだもん:} 配列の引数の関数の場合と同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

配列の引数で値を返す

\footnotesize \textcolor{pink}{四国めたん:} ところで以前に関数の引数はコピーなので、引数を通して値を直接返すことはできないとお話ししましたわ

\footnotesize \textcolor{lime}{ずんだもん:} おぼえているのだ

\footnotesize \textcolor{pink}{四国めたん:} さらに前回、ポインタを関数の引数に使うと、元の変数の値を変更できることもお話ししましたわ

\footnotesize \textcolor{lime}{ずんだもん:} それもおぼえているのだ

\footnotesize \textcolor{pink}{四国めたん:} 関数の引数に配列を使った場合も元の配列の各要素にアクセスすることができますわ

\footnotesize \textcolor{lime}{ずんだもん:} 関数の引数にポインタを使った場合と同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} とりあえずプログラムを見てみましょう

#include <stdio.h>

void add(int a[]) {
  a[0] = a[1] + a[2];
  return;
}

void main() {
  int a[] = {1, 2, 3};
  add(a);

  printf("配列を使った足し算の結果は%dです。\n", a[0]);
}

配列による値の戻し

\footnotesize \textcolor{lime}{ずんだもん:} 「配列を使った足し算の結果は5です。」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、元の配列のa[0]は1で初期化されていましたが、add関数内で、変更されましたわ

\footnotesize \textcolor{lime}{ずんだもん:} add関数内で、足し算の答えが代入された結果、関数コール後は5になったのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、関数の引数に配列を指定すると、配列がコピーされるのではなく、元の配列そのものが渡されることになりますわ

\footnotesize \textcolor{lime}{ずんだもん:} ところで関数内で、配列の内容を変えてほしくない場合には、どうするのだ?

\footnotesize \textcolor{pink}{四国めたん:} その場合にはconstキーワードを使いますわ

\footnotesize \textcolor{lime}{ずんだもん:} 具体的には?

\footnotesize \textcolor{pink}{四国めたん:} 例えば関数の引数をvoid add(const int* pa)void add(const int a[])のようにしますわ

\footnotesize \textcolor{pink}{四国めたん:} ただし、その場合はa[0] = a[1] + a[2];の部分はエラーとなりますわね

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

関数の戻り値に配列を使えますか?

\footnotesize \textcolor{pink}{四国めたん:} 次に関数の戻り値に配列を使用できるか、確認してみましょう

#include <stdio.h>

int[] get_array() {
  int a[] = {1, 2, 3};
  return a;
}

void main() {
  int* a = get_array();
  printf("a[0]は%dです。\n", a[0]);
  printf("a[1]は%dです。\n", a[1]);
  printf("a[2]は%dです。\n", a[2]);
}

配列の戻り値エラー

\footnotesize \textcolor{lime}{ずんだもん:} 「E0040 識別子が必要です」と「E0065 ; が必要です」というエラーが出ているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、実はC言語では配列そのものを関数の戻り値に指定することはできませんわ

\footnotesize \textcolor{pink}{四国めたん:} ですので、通常はポインタとして返しますわ

\footnotesize \textcolor{lime}{ずんだもん:} うん?関数内で宣言した変数をポインタで戻すのは御法度だったのでは?

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{pink}{四国めたん:} とりあえず確認してみましょう

#include <stdio.h>

int* get_array() {
  int a[] = {1, 2, 3};
  return a;
}

void main() {
  int* a = get_array();
  printf("a[0]は%dです。\n", a[0]);
  printf("a[1]は%dです。\n", a[1]);
  printf("a[2]は%dです。\n", a[2]);
}

ポインタの戻り値

\footnotesize \textcolor{lime}{ずんだもん:} 実行はできたけど、結果がおかしいのだ

\footnotesize \textcolor{pink}{四国めたん:} 「ローカル変数またはテンポラリのアドレスを返します」と云うワーニングが出ますわね

ポインタの戻り値のワーニング

\footnotesize \textcolor{pink}{四国めたん:} 基本的には、関数からポインタ型で戻す配列は、特別な方法で確保することが必要ですわ

\footnotesize \textcolor{lime}{ずんだもん:} 特別な方法?

\footnotesize \textcolor{pink}{四国めたん:} はい、それについては後ほど説明しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 今はがまんするのだ

まとめ

\footnotesize \textcolor{pink}{四国めたん:} お疲れさまでした

\footnotesize \textcolor{lime}{ずんだもん:} おつかれさまなのだ

\footnotesize \textcolor{pink}{四国めたん:} 配列とポインタ についての説明は以上ですわ

Discussion