本記事は、C言語における「配列」利用における注意点のみ記載します。
配列自体は難しい文法ではないため、基本的な使い方は割愛させていただきます。
配列自身は型である。
C言語(他の言語でもそうかもしれませんが)では配列も一つのデータ型になります。
1 |
int a[10]; |
配列は他の配列変数にコピー(代入)をすることができない。
配列は例えば、下記のような代入をすることができません。
1 2 3 4 |
int a[2] = {1,2}; int b[2]; b = a; |
もし、配列の内容を他の配列変数にコピーしたい場合は、コピー元の配列から値を一つ一つ取り出して、コピー先の配列に代入する必要があります。ただ、この方法だとメンドクサイと思いますので、別のメモリ領域にコピーしてくれるmemcpy関数を使うのが一般的でしょう。
なぜ代入できないのか?
配列自体は、添え字なしの場合(上記例でいえばaのみ)だとアドレス(&a[0])を指定したことと同じになります。
なので、bのアドレスにaのアドレスを代入するという、コピーではなく同じアドレスを指している二つの変数ができるという意味のない処理になってしまうためです。
関数の引数に配列を渡す場合の注意点(ポインタ渡し)
例えば、下記のように配列を引数にとる関数があったとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void func(int a[1]) { a[0] = 15; } int main() { int a[1]; a[0] = 2; //関数を呼び出す。 func(a); //結果は、15と表示される。 printf("%d\n", a[0]); } |
その際に、関数の呼び出し先で書き換わった配列の値が、呼び出し元の配列にまで処理結果が影響してしまいます。
基本的に、関数に渡した変数は、呼び出し先と呼び出し元で独立しているため、呼び出し元にまで影響はしないはずですが、なってしまっています。
これは、関数に配列を渡した際は、配列のアドレスを渡したと同じ事になるのです。
プログラムは実際に下記のような変換が行われています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void func(int* a) { //★★★★★:ポインタ型を渡すのと同じように読み替えられる。(int* aとint a[1]は同じ意味) *a = 15; //★★★★★:ポインタでメモリの内容を書き換えるように読み替えられる。(a[0]と*aは同じ意味) } int main() { int a[1]; a[0] = 2; //関数を呼び出す。 func(&a[0]); //★★★★★:配列の先頭アドレスを渡すのと同じように読み替えられる。 //結果は、15と表示される。 printf("%d\n", a[0]); } |
この事象のことを「ポインタ渡し」と言って、呼び出し元関数と呼び出し先関数で同じメモリ番地にアクセスするため変数の中を書き換えることができるのです。
この記事へのコメントはありません。