C言語の配列で範囲外にアクセスできてしまう理由と対策法

長らくRubyやjavaScript等の高級言語を扱っていると、C言語の原始的な処理に戸惑う時がある。

実際に今日C言語で配列を扱っていた時に、自分が定義した配列の範囲外に誤ってアクセスしたせいで思わぬ動作をする、と言うミスをしてしまった。

そこで今回は、C言語とJavaScript等の高級言語における配列の違いや、C言語の配列での色々な実験をまとめていく。

C言語と高級言語における配列の違いとは?

C言語の配列の仕組みは、JavaScript等の高級言語と比べると違和感を覚える時がある。

JavaScript等の高級言語で配列の範囲外にアクセスしようとすると、未定義を示すundefinedとかnullが値として返される。これは、定義した配列の範囲内にアクセスしているかどうかを、言語側のインタプリタがチェックをしており、範囲外にアクセスしている場合は未定義undefinedを返すようにしているからだ。

自分が定義していない範囲外にアクセスする場合に未定義が返されるのは、直感的に分かりやすい仕様と言えるだろう。

しかし、C言語では配列の範囲内にアクセスしているかどうかをチェックする仕組み自体がなく、仮に範囲外にアクセスした場合はそのままそのアドレスにアクセスしてしまう。その結果、プログラム自体がクラッシュしたり、変な値を取ってきたりする。

実際にJavaScriptのC言語のコードを比べてみよう。

var a = [2, 'hello'];
console.log(a[0]);  // 2
console.log(a[4]);  // undefined

a[0]にアクセスすると2が返ってくるのに対し、配列の範囲外であるa[4]にアクセスしようとするとundefinedが返ってくる。このJavaScriptの言語の仕様は、人間にとって直感的で分かりやすい。

上記のコードと同じような事を、C言語でやってみる。

#include <stdio.h>
int main()
{
int a[] = {2, 4, 10};
printf("this element is %d\n", a[0]);
printf("this element is %d\n", a[5]);
return 1;
}

上記のコードは、2 undefinedの順番に表示されるのを期待するが、実際は期待とは異なる動きをする。

this element is 2
this element is 4199367  // what?

参考記事:c++ – Accessing an array out of bounds gives no error, why? – Stack Overflow

C言語の配列の範囲外アクセスの対策法はあるの?

色々調べたが、明確な対処法は存在しないっぽい。

ただ、自分の定義した配列が明らかに期待する動きと違っていれば、範囲外アクセスを起こしている可能性を探るべきだと思う。

ボクの場合はC言語でオセロを作る時に、以下の様な構造体を用意していた時に範囲外アクセスが起こった。

struct _Board {
int Disk[81];
int Stack[1000];
int *Sp;
};

_Board構造体の中に要素が81個のDisk[81]配列を作ったわけだが、間違えて88個目の配列にアクセスしようとした時に、Disk[81]配列の性質上あり得ない値が取り出される、と言う事態が起こった。

具体的には、Stack[1000]の7番目の要素の値を取り出していた。

ReratedPosts

C言語でTCPクライアント・サーバーを実装する
C言語のsizeofにおける配列、ポインタ、構造体などの動きのまとめ
ダブルポインタ(ポインタのポインタ)のメリットや使い道を紹介する
C言語のstrstr関数の色々な使い方
C言語のvoidの使い方について
socketのgetaddrinfoの使い方や仕組みについてまとめてみる