我目前正在开发一个包含一些文件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无关的东西并且意识到我的所有文件路径似乎都已损坏时(例如“║½½½½½½½½■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ¯■“)。
我也收到了错误消息:
“释放后释放的自由堆块”
经过进一步调查后,我意识到这一切都发生在memcpy
的{{1}}之后。
然而,这一切似乎只有在从GDB运行时才会发生。这里出了什么问题?
答案 0 :(得分: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
(或snprintf
)在一行中执行此操作
size_t n_written = sprintf(path, "%s%s%s", a, separator, b);
assert(n_written + 1 == total_len);