見出し画像

C言語教室 第4回 - 配列を関数に渡す

前回、配列をやりましたが、配列を関数に引き数で渡すにはどうすれば良いでしょうか。

C言語教室 第3回 - 配列という入れ物

まずは第1回の課題だった合計を求める関数をもとに、配列の値の合計を求めてみましょう。

2つの引き数の合計を求める関数は、こんなでしたよね。

#include <stdio.h>

int sum(int x, int y) {
  int s;

  s = x + y;
  return s;
}

void main() {
  int a;
  int b;
  int s;

  a = 2;
  b = 9;
  s = sum(a, b);
  printf("Sum(%d,%d)=%d\n", a, b, s);
}
Sum(2,9)=11

これを参考に配列を渡して、それぞれの値の合計を求めるような関数にしてみます。


#include <stdio.h>

int sum_array(int x[], int z) {
  int s = 0;
  int i;
  
  for (i = 0; i < z; i++) {
    s += x[i];
  }
  return s;
}

void main() {
  int a[3];
  int s;
  int i;

  a[0] = 1;
  a[1] = 2;
  a[2] = 4;

  for ( i = 0; i < 3; i++) {
    printf("a[%d]=%d\n", i, a[i]);
  }
  s = sum_array(a, 3);
  printf("Sum=%d\n", s);
}

C言語では関数を呼び出す時に引き数の値がコピーされるのは既に説明しましたが、渡せる型は整数であるとかポインタであるとかの単純な型に限られます。配列をまるごとコピーして渡すことはできません。そこで配列を渡す場合には、配列の参照(ポインタ)を渡します。これは swap で行った参照渡しと同じです。結果的に配列を渡した場合、渡すのはポインタなので、渡した配列の値を関数で書き換えることができます。

また配列変数から配列のサイズを知ることは出来ないので、サイズも引き数で渡します。配列変数を渡すときは引き数の型宣言では大きさを書かない型名(int [])で宣言します。

なお int s = 0; というように、変数を宣言する文で初期値を設定することができます。また、s += x[i]; というのは s = s + x[i]; と同じ意味で、+= という演算子を使うことで、s を2度書かないで済ますことができます。

実行してみましょう。

a[0]=1
a[1]=2
a[2]=4
Sum=7

1+2+4は7なので大丈夫ですね。

さて以前に出た swap も、配列のそれぞれの値を交換するようにしてみましょう。最初はひとつずつ swap を呼ぶ形でやってみましょう。

#include <stdio.h>

void swap(int *x, int *y) {
  int z;

  z = *x;
  *x = *y;
  *y = z;
}

void main() {
  int a[3];
  int b[3];
  int i;

  a[0] = 1;
  a[1] = 2;
  a[2] = 4;
  b[0] = 8;
  b[1] = 16;
  b[2] = 32;

  for( i = 0; i < 3; i++ ) {
    printf("a[%d]=%d\n", i, a[i]);
  }
  for( i = 0; i < 3; i++ ) {
    printf("b[%d]=%d\n", i, b[i]);
  }

  for ( i = 0; i < 3; i++) {
    swap(&a[i], &b[i]);
  }

  for( i = 0; i < 3; i++ ) {
    printf("a[%d]=%d\n", i, a[i]);
  }
  for( i = 0; i < 3; i++ ) {
    printf("b[%d]=%d\n", i, b[i]);
  }
}

実行すると

a[0]=1
a[1]=2
a[2]=4
b[0]=8
b[1]=16
b[2]=32
a[0]=8
a[1]=16
a[2]=32
b[0]=1
b[1]=2
b[2]=4

と表示されるはずです。

これを配列のそれぞれの値を交換する関数にしてみます。

#include <stdio.h>

void swap_array(int *x, int *y, int s) {
  int i;
  int z;

  for ( i = 0; i < s; i++) {
    z = x[i];
    x[i] = y[i];
    y[i] = z;
  }
}

void main() {
  int a[3];
  int b[3];
  int i;

  a[0] = 1;
  a[1] = 2;
  a[2] = 4;
  b[0] = 8;
  b[1] = 16;
  b[2] = 32;

  for( i = 0; i < 3; i++ ) {
    printf("a[%d]=%d\n", i, a[i]);
  }
  for( i = 0; i < 3; i++ ) {
    printf("b[%d]=%d\n", i, b[i]);
  }

  swap_array(a, b, 3);

  for( i = 0; i < 3; i++ ) {
    printf("a[%d]=%d\n", i, a[i]);
  }
  for( i = 0; i < 3; i++ ) {
    printf("b[%d]=%d\n", i, b[i]);
  }
}

関数に渡すポインタは配列変数でも変数の時と同じint *になります。つまり先頭のある場所を渡して、残りはその後ろに連続しているはずだということになるのですね。

※int[]で渡すことも出来るはずなんですが、どうもコンパイラによって妙な挙動を示すこともあるので避けたほうが良いです。

結果を確認してみましょう。

a[0]=1
a[1]=2
a[2]=4
b[0]=8
b[1]=16
b[2]=32
a[0]=8
a[1]=16
a[2]=32
b[0]=1
b[1]=2
b[2]=4

ちゃんと交換されているようです。

次回は文字と文字列に進みます。


課題

  • 配列のそれぞれの値の平均値を小数で返す関数を書きなさい。

  • 配列のそれぞれの値の中でもっとも大きい値を返す関数を書きなさい。


前回の課題の回答例

大きさで指定した範囲を超えた値を使ってみて、何が起こるか確認してください。

配列の中身を表示するコードを前回書いたので、これをそのまま使って、範囲を超えて表示させてみましょう。


#include <stdio.h>

void main() {
  int a[3];
  int i;

  a[0] = 3;
  a[1] = 5;
  a[2] = 7;

  for( i = 0; i < 10; i++) {
    printf("%d\n", a[i]);
  }
}

宣言では3個しか確保していないのに10個目までを表示させます。C言語では宣言で確保した数について、何のチェックもされないので、エラーにはなりません。

3
5
7
5284504
0
5799380
655364
5799484
5284504
0

3,5,7までは予定通りですが、それ以降はどんな数字が表示されるかは、わかりません。当然、これとは異なる結果になることがあります。

2つの配列の中身をそれぞれ入れ替える処理を、以前に使ったswap関数を使って書いてください。

こちらは今回使ったコードに回答例がありますね。


いいなと思ったら応援しよう!