0

私はsplicejs の関数のように機能する関数を作成しています:(任意の型の)配列を指定し、指定されたインデックスで始まる要素を削除し、ギャップに新しい要素を詰め込みます(必要に応じて元の配列を拡張または縮小します)。

Windows7 で MinGw/Eclipse CDT を使用しています。これが私のコードです:

void* splice(int typesize,void* arr,
        int size,int start, int length,
            void* stuff,int size2){
    //length is the number of elements to remove
    //and size2 is the number of elements to fill in the gap

    //so size-gap will be the size of the new array after the function
    //when gap is a minus number, the array grows
    //and when gap is a positive number, the array shrinks
    int gap = length-size2;
    void* ptr = malloc(typesize*(size-gap));//--------(1)--------
    if(ptr==NULL){
        puts("error");
        return NULL;
    }
    //now the ptr array is empty, copy the original array(arr)
    //to the ptr until the 'start' index
    memmove(ptr,arr,typesize*start);

    //fill the new array 'stuff' into ptr starting from 
    //the index after 'start'
    memmove(ptr+typesize*start,stuff,typesize*size2);

    //and copy the rest of the original array (starting from 
    //the index start+length, which means starting from 'start' index
    //and skip 'length' elements) into ptr
    memmove(ptr+typesize*(start+size2),arr+typesize*(start+length),
            typesize*(size-start-length));

    return ptr;
}

また、いくつかのテスト コードも作成します。以下のスニペットはlong longタイプ用です。

int main(){
    setbuf(stdout,NULL);
    int start = 1;
    int delete = 6;
    long long* oldArray= malloc(sizeof(long long)*7);
    long long* stuff = malloc(sizeof(long long)*3);
    oldArray[0]=7LL;
    oldArray[1]=8LL;
    oldArray[2]=4LL;
    oldArray[3]=1LL;
    oldArray[4]=55LL;
    oldArray[5]=67LL;
    oldArray[6]=71LL;
    stuff[0]=111LL;
    stuff[1]=233LL;
    stuff[2]=377LL;
    int newsize = 7-(delete-3);
    void* newArray = splice(sizeof(long long),oldArray,7,start,delete,stuff,3);
    if(newArray){

        //------------crash happens here-----------
        //free(oldArray);
        //-------------

        oldArray =  newArray;
        int i=0;
        for(;i<newsize;i++){
            printf("%I64d\n",oldArray[i]);
        }
    }
    return 0;
}

7、111、233、および 377 を出力する必要があります (インデックス 1 から 6 つの要素を削除し、111、233、および 377 を配列に詰め込みます)。

char、int、および long 型の配列をテストしたところ、すべての状況でコードが機能しました。1 つの問題を除いて: 古い配列を解放できません。によって何度かアクセスされたメモリ ブロックは、再利用できないようmemmoveです。

(1) で malloc を realloc に変更すると、free() はクラッシュしなくなりますが、関数を正しく動作させることはできなくなります (そして、free() 関数が実際に動作したかどうかはわかりません)。

この問題がどのように発生するか、およびコードを改善するにはどうすればよいかについて、アドバイスをお願いします。

4

1 に答える 1

3

この行を見てください:

    memmove(ptr,arr,typesize*size);

typesize * size バイトを ptr に移動しようとします。ただし、 typesize*(size - gap) バイトのみを割り当てました。よほど不運でない限り、gap > 0 の場合はクラッシュにつながります。

最初に見つけたバグの後でチェックをやめたので、もっとあるかもしれませんが、コードが何をするのかをわざわざ調べようとはしませんでした。私が推測したり質問したりせずに実装できるように、関数が十分に機能する必要があることを説明するコメントを追加する必要があります。

于 2014-11-28T15:57:17.820 に答える