SIGSEGV在动态分配内存以接收FTP服务器的LIST响应时

时间:2012-07-11 03:50:10

标签: c++ memory-management segmentation-fault c-strings

我正在使用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中使用了backtrace并发现了段错误发生在行delete[] GLOBAL_FRAGMENT;' and删除[]列表; in FTP_getList . Am I not properly deleting these arrays? I am calling删除[]`每次我分配它们一次,所以我不明白为什么它不会分配记忆正确...

旁注:在尝试删除数组之前,是否需要检查数组是否为NULL

另外,我知道使用STD :: Strings会更容易,但我正在尝试学习c风格的字符串作为练习,而且它崩溃的事实是我需要练习的一个完美的例子,我也会更改代码以将这些存储在一个动态分配的缓冲区中,该缓冲区仅在新的ptr大小大于前一个长度时重新分配,但我想弄清楚为什么当前代码不能先工作。 :-)任何帮助将不胜感激。

1 个答案:

答案 0 :(得分: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指针是完全安全的。