1

個人的な使用と学習体験のためにC++でFTPクライアントを構築していますが、LIST応答を格納するためのメモリを割り当てるときに問題が発生しました。FTPリクエストに使用しているライブラリはlibcurlで、サーバーからの応答を受信すると次の関数を呼び出します。

size_t FTP_getList( char *ptr, size_t size, size_t nmemb, void *userdata) {
    //GLOBAL_FRAGMENT is global
    //libcurl will split the resulting list into smaller approx 2000 character
    //strings to pass into this function so I compensate by storing the leftover
    //fragment in a global variable.
    size_t fraglen = 0;
    if(GLOBAL_FRAGMENT!=NULL) {
        fraglen = strlen(GLOBAL_FRAGMENT);
    }
    size_t listlen = size*nmemb+fraglen+1;
    std::cout<<"Size="<<size<<" nmemb="<<nmemb;
    char *list = new char[listlen];
    if(GLOBAL_FRAGMENT!=NULL) {
        snprintf(list,listlen,"%s%s",GLOBAL_FRAGMENT,ptr);
    } else {
        strncpy(list,ptr,listlen);
    }
    list[listlen]=0;
    size_t packetSize = strlen(list);
    std::cout<<list;
    bool isComplete = false;
    //Check to see if the last line is complete (i.e. newline terminated)
    if(list[size]=='\n') {
        isComplete = true;
    }
    if(GLOBAL_FRAGMENT!=NULL) {
        delete[] GLOBAL_FRAGMENT;
    }
    GLOBAL_FRAGMENT = GLOBAL_FTP->listParse(list,isComplete);
    delete[] list;
    //We return the length of the new string to prove to libcurl we
    //our function properly executed
    return size*nmemb;
}

上記の関数は、次の関数を呼び出して、返された各行を個々の文字列に分割し、さらに処理します。

char* FTP::listParse(char* list, bool isComplete) {
    //std::cout << list;
    //We split the list into seperate lines to deal with independently
    char* line = strtok(list,"\n"); 
    int count = 0;
    while(line!=NULL) {
        count++;
        line = strtok(NULL,"\n");
    }
    //std::cout << "List Count: " << count << "\n";
    int curPosition = 0;
    for(int i = 0; i < count-1 ; i++) {
        //std::cout << "Iteration: " << i << "\n";
        curPosition = curPosition + lineParse((char*)&(list[curPosition])) + 1; 
    }
    if(isComplete) {
        lineParse((char*)&(list[curPosition]));
        return NULL;
    } else {
        int fraglen = strlen((char*)&(list[curPosition]));
        char* frag = new char[fraglen+1];
        strcpy(frag,(char*)&(list[curPosition]));
        frag[fraglen] = 0;
        return frag;
    }
}

次に、上記の関数は以下の関数を呼び出して、1行の個々のエントリを個別のトークンに分割します。

int FTP::lineParse(char *line) {
    int result = strlen(line);
    char* value = strtok(line, " ");
    while(value!=NULL) {
        //std::cout << value << "\n";
        value = strtok(NULL, " ");
    }
    return result;
}

このプログラムは比較的小さなリスト応答で機能しますが、最大10,000個のファイルを含むリモートディレクトリのリストを取得してストレステストを試みたところ、プログラムがSIGSEGVをスローしました... gdbでバックトレースを使用したところ、セグメンテーション違反が発生することがわかりました行delete[] GLOBAL_FRAGMENT;' anddelete[]リスト; inFTP_getList . Am I not properly deleting these arrays? I am callingdelete [] `を割り当てるたびに1回だけなので、メモリが正しく割り当てられない理由がわかりません...

補足:アレイを削除する前に、アレイが存在するかどうかを確認する必要がありNULLますか?

また、これはSTD :: Stringsを使用すると簡単になることはわかっていますが、練習としてcスタイルの文字列を学習しようとしています。クラッシュしているという事実は、練習が必要な理由の完璧な例です。また、新しいptrサイズが以前の長さよりも大きい場合にのみ再割り当てされる動的に割り当てられたバッファーにこれらを格納するコードですが、現在のコードが最初に機能しない理由を理解したいと思います。:-)どんな助けもいただければ幸いです。

4

1 に答える 1

3

このコードでは

size_t listlen = size*nmemb+fraglen+1;
std::cout<<"Size="<<size<<" nmemb="<<nmemb;
char *list = new char[listlen];
if(GLOBAL_FRAGMENT!=NULL) {
    snprintf(list,listlen,"%s%s",GLOBAL_FRAGMENT,ptr);
} else {
    strncpy(list,ptr,listlen);
}
list[listlen]=0;

listバッファをオーバーランしています。バイトを割り当てましたが、最後に割り当てられたバイトの1つ後listlenの値を書き込みます。0これにより、未定義の動作が発生します。より実際的には、ヒープの破損を引き起こす可能性があり、これにより、観察した種類のエラーが発生する可能性があります。

私はあなたが呼んでいる方法に問題は見られませんでしたdelete[]

NULLポインタを削除するのは完全に安全です。

于 2012-07-11T04:08:09.003 に答える