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;
    }
}

然后上面的函数调用下面的函数将一行中的各个条目拆分为单独的标记:

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;' and删除[]列表;in每次分配它们时, FTP_getList . Am I not properly deleting these arrays? I am callingdelete[]` 恰好一次,所以我不明白为什么它不能正确分配内存......

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缓冲区。您已经分配了字节,但是您在最后分配的字节listlen之后写入了一个值。0这会调用未定义的行为。更实际地说,它可能会导致堆损坏,这可能会导致您观察到的那种错误。

我没有看到你打电话的方式有任何问题delete[]

NULL删除指针是完全安全的。

于 2012-07-11T04:08:09.003 回答