1

我目前正在开发一个包含一些文件 I/O 的项目。

由于它是跨平台的,我需要考虑不同的路径分隔符,因此决定创建以下函数来简化加入路径的过程:

/**
 * @brief Joins two paths together.
 * 
 * path = a + seperator + b + '\0'
 * 
 * @param a First path.
 * @param b Second path.
 * 
 * @return A pointer to the two paths combined.
 */
char * join_paths(const char * a, const char * b)
{
    const char separator[] =
    #ifdef _WIN32
        "\\";
    #else
        "/";
    #endif


    /* Get the sizes of all the strings
       to join.                         */
    size_t a_len = strlen(a);
    size_t b_len = strlen(b);
    size_t s_len = strlen(separator);
    size_t total_len = a_len + b_len + s_len + 1; /* +1 for \0 */

    /* path will contain a, b, the separator and \0
       hence the total length is total_len.
    */
    char * path = malloc(total_len);
    check( path != NULL,
           "Error allocating memory for path. " );

    memcpy(path, a, a_len);          /* path will begin with a */

    strncat(path, separator, s_len); /* copy separator */
    strncat(path, b, b_len);         /* copy b         */

error:

    return path;
}

(这里check的宏在哪里:http: //pastebin.com/2mZxSX3S

直到现在一直运行良好,当我不得不使用 GDB 调试与文件 I/O 无关的东西时,我意识到我所有的文件路径似乎都已损坏(例如“¡║¯½½½½½½■¯■¯■¯■” )。

我也收到错误消息:

“释放后修改的空闲堆块”

经过进一步调查,我意识到这一切都发生memcpyjoin_paths.

然而,这一切似乎只发生在从 GDB 运行它时。这里出了什么问题?

4

1 回答 1

4

memcpy没有对目标缓冲区进行零终止。同时,strncat需要一个有效的字符串作为目标。如果没有适当的零终止,您的strncat调用会在缓冲区下方某个不可预测的位置开始连接,最终在缓冲区的末尾运行。

很可能你可以通过做来解决它

memcpy(path, a, a_len + 1);

以确保终止零也被复制。

但是你为什么要混合mem...函数和str...函数呢?str...可以正确执行此操作,但在使用任何功能之前,您必须不断注意正确的零终止等事项。

有人可以争辩说,当字符串的长度已知时,mem...函数比str..函数更合适、更有效。在这种情况下,只要坚持mem...功能。例如,在您的情况下,这应该可以正常工作

memcpy(path, a, a_len);
memcpy(path + a_len, separator, s_len);
memcpy(path + a_len + s_len, b, b_len + 1);

或者(甚至更好),您可以使用sprintf(or snprintf) 在一行中完成

size_t n_written = sprintf(path, "%s%s%s", a, separator, b);
assert(n_written + 1 == total_len);
于 2014-08-06T21:33:50.773 回答