C言語関係掲示板

過去ログ

No.164.配列の連結時の注意


No.1057

配列の連結
投稿者---akira(2002/02/15 17:45:22)


こんにちは。 いつも活用させて頂いてます。

配列をくっつっける(連結)させる処理を行おうとして以下の様にしました。
tmp[]の宣言時よりも大きな配列になるので、処理出来ないんだろうなぁ〜
と思いつつコンパイル・実行してみると正常に動作しました。
で、色々確認していくと連結後の配列のサイズが変わっていません。
どうしてこうなるのか教えて頂けませんでしょうか?
/* 6が表示される */ の部分では 11 が表示されると思っていました。

以下、処理内容です。
#include  < stdio.h >

void Push(short ary[],int len,short ary2[],int len2);
void main(){
    short tmp[]={0,1,2,3,4,100};
    short list[]={10,20,30,40,80};
    int i,len,len2,max,afterlen;
    len=(sizeof(tmp)/sizeof(short));
    len2=(sizeof(list)/sizeof(short));
    max=len+len2;
    Push(tmp,len,list,len2);  /* 連結処理 */
    for(i=0;i < max;i++){
        printf("tmp:%d\n",tmp[i]);  /* 正常に表示された */
    }
    afterlen=(sizeof(tmp)/sizeof(short));
    printf("afterlen:%d\n",afterlen); /* 6が表示される... */
}
void Push(short ary[],int len,short ary2[],int len2){
    int i,max;
    int j=0;
    max=len+len2;
    for(i=len;i < max;i++){
        ary[i]=ary2[j];
        j++;
    }
}


No.1058

Re:配列の連結
投稿者---fen(2002/02/16 00:34:29)


通りすがりですが、ちょいと
これは非常に危ない事をしています
バッファオーバーフローといい、バグの原因になります

プログラムにおいては、『エラーが起きない=正常』とは言えません
ただそのエラーが表面化されなかった場合もあります

C言語は言語レベルでのセイフティーな配列というのを用意していません
配列はただの連続した領域でしかなく、要素数等の情報は持っていません
これは以下のコードで確認できます
#include <stdio.h>
int arraysize(int a[]) {
return sizeof a;
}
int main(void) {
int a[] = {0, 1, 2, 3, 4};
printf("%d", arraysize(a));
return 0;
}

出力される数値は5にはなりません
retsizeの引数a[]が実際にはint *aと変わらないからです

C言語は要素数以上書き込んでも一向に文句を言いません
「範囲外ならなにをする」ということはプログラマ任せです



たとえばですが、Borland C Compiler(以下BCB)で以下の様なものを動かしてみます
#include <stdio.h>
int main(void) {
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int b[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
int i, j;
for(i = 0; i < 10; i++)
b[10 + i] = a[i] * 2;
for(i = 0; i < 10; i++)
printf("a[%d] = %d\n", i, a[i]);
return 0;
}

これを実行した場合以下の様に表示されます
a[0] = 0
a[1] = 2
a[2] = 4
a[3] = 6
a[4] = 8
a[5] = 10
a[6] = 12
a[7] = 14
a[8] = 16
a[9] = 18
配列aには代入を行なっていないはずですが、実際には数値が変わっています
これは、配列bの次に配列aがあったためです
b[10]はa[0]と同じ場所を表していた事になります

これは使用するコンパイラによって起きる現象が異なってきます
実際、akiraさんのコードをBCBでコンパイルし実行すると強制終了が起きます


No.1059

Re:配列の連結
投稿者---kikk(2002/02/16 00:44:21)


ども。

<PRE>
void Push(short ary[],int len,short ary2[],int len2){
int i,max;
int j=0;
max=len+len2;
for(i=len;i < max;i++){
ary[i]=ary2[j];
j++;
}
}
</PRE>

提示されているコードは結合ではなく、末尾に続けてコピーしているだけの
ものになります。

C言語では配列のサイズは常に固定です。うまくいったように見えるのは
おそらくtmpとlistが連続した領域に割り当てられているためかと。
printf("%p %p",tmp,list)などして、メモリマッピング状況を調べてみると
わかると思います。おそらくlistのアドレスはtmpより12(=6*2)大きい
でしょう。また、listにtmpをくっつけるとうまくいかないはずです(場合に
よってはOSにメモリ保護違反だかセグメンテーションフォールトと言われます)。


なんで配列の範囲を超えてアクセスしても怒られないかというと、C言語が
そういう言語だからとしかいえません。C言語は、プログラマは何でも知って
いてミスをしないという哲学にもとづいているので。。運がいいと、実行時
にOSに怒られるか、おかしな動作をしてバグに気づくことができます。運が
悪いと今回のように、バグがあるのにうまく動いているように見えてしまいます。


で、結局どうすればいいかというと、最初から十分大きな配列を用意するか
malloc()等で動的に必要なサイズ(合計のサイズ)だけ確保するかのどちらか
になります。前者の場合は簡単ですが、限界があることに変わりはありません。
後者の場合は、適当なタイミングでfree()してやらなければなりません。
また、(当然)ポインタの習得は必須です。なお、利用できる領域の伸縮は
realloc()でできます。

ただし、どちらにしろどこまで使えるか、使っているかの管理はしなければ
なりませんし、追加コピーは必要になります(上記のコードはそのままは使え
ないでしょう)。


では。

No.1060

Re:配列の連結
投稿者---akira(2002/02/16 23:15:37)


fenさん、kikkさん

アドバイスありがとうございます!

プログラム的に危ないことをしていたんですね。C言語の配列って要素数を
持っていなくて、ただ領域を持っているだけとはまた一つ勉強になりました。
kikkさんに教えて頂いたmalloc free realloc等はこれまであまり使ったこ
とないのですが、調べて再度2つの配列を1つにまとめる処理を考えてみます。
如何せんmallocに関しては未知の関数ですので、使い方から勉強です。
作成後、また処理内容に不安・疑問部分があればご質問させて頂くかと
思いますが、よろしくお願いします。

本当にありがとうございました。


戻る


「初心者のためのポイント学習C言語」 Last modified:2002.03.16
Copyright(c) 2000-2002 TOMOJI All Rights Reserved