2008.07.31
Pythonのリストと、C言語の配列を比較
再び、PythonとC言語を比較してみよう。
今日はメモリ内の値を直接書き換えられちゃうC言語なお話。
Pythonでは [1,2,3] のような配列を 「リスト」 と呼び、中の要素は後で追加できる。
例えば、
>>> a = [] #空っぽのリスト
>>> a
[]
>>> a.append(1) #要素追加
>>> a
[1]
>>> a.append(2) #要素追加
>>> a
[1, 2]
1行目の宣言のようなものは Python では不要だけど、このように append というメソッドで後から要素を追加することができる。
「えっ?! これって当たり前じゃないの?」
と思う人は 2.0系 エンジニアです(笑
C言語は「変数」を使う場合は事前に宣言する必要があり、もし配列を使うのならば配列の型だけでなく、中の要素がいくつあるのかも事前に決めておく必要がある。
(配列宣言をポインタみたいに扱う場合は違うけど)
例えば、int型の配列を用意する場合、
int a[3];
のように、[]内に必要な要素数を記入する。この場合は int型 の箱を3つ用意するという意味になり、その分のメモリが確保される。
値の代入は次のように書けばできる。
a[0] = 1;
a[1] = 2;
a[2] = 3;
では、
a[3] = 4;
a[4] = 5;
と書くとどうなるのか。実はこれでもコンパイルは成功し、実行ファイルができる(ことがある)。けど、これがとっても危険。ホントに危険。
これでも問題なく動いてしまうことがあるけど、それは”たまたま”であって、本来なら書き換えてはいけないメモリ領域を破壊してしまう。
そんなバカな、というメモリ破壊の例を1つ。
C言語には(標準では)文字列を扱う型はなく、文字型(char)の配列として文字を扱う。
これがまたやっかいで、文字列の長さが事前にわかっていない場合は大きめに確保しておく必要がある。
配列は単純に = でコピーできないので、文字列のコピーには strcpy という関数を使う。
では、さっそく書いてみよう。
char str1[20];
strcpy(str1, “変数1に代入します。”); printf(” str1 = [%s]\n”, str1);
>> 実行結果
> str1 = [変数1に代入します。]
おそらく多くの環境では str1 を出力すればちゃんと値が入っているはずだ。
では、もう1つ変数を追加してみよう。
char str1[20];
char str2[20];
strcpy(str1, “変数1に代入します。”);
strcpy(str2, “変数2に代入します。”);
printf(” str1 = [%s]\n”, str1);
printf(” str2 = [%s]\n”, str2);
>> 実行結果
> str1 = []
> str2 = [変数2に代入します。]
!?!?
変数1が消えてしまった。(WindowsXP+BorlandC++Compiler)
これは、str2の領域が実は 9文字 分しか確保されていいないのに「変数2に代入します。」という全角10文字を代入したために変数1の領域を破壊してしまったために起きている。
str2の文字列の終わりを示す \0 の1文字分がはみ出してしまったのだ。
(注:str1がメモリ番地としては後ろになる)
str2:[変][数][2][に][代][入][し][ま][す][。][\0]
とメモリに配置されるのだが、最後の[\0]は11文字目にあたるので str1 の領域に食い込んで
str1:[\0][変][数][1][に][代][入][し][ま][す][。][\0]
となってしまい、str1の1文字目が文字列の終わりを示す \0 になっているというわけだ。
そこで C言語 の本では、文字列の宣言は「必要な文字数+1の領域を確保すること」とか「十分に大きめな量を確保すること」とか解説されている。
こんなカンタンに予期せぬメモリ領域を書き換えてしまう C言語 にも驚くけど、C言語 のこうした苦労が今に活きている面もあるので勉強することは悪くないと思う。
Trackback URL
Comment & Trackback
Comment feed
Comment