问题描述
我目前在使用带有多个分隔符的 strsep
时遇到了一些奇怪的结果。我的分隔符包括 TAB 字符、空格字符以及 >
和 <
。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char buffer[50];
char *curr_str = NULL;
const char delim[4] = "\t >";
//const char delim[4] = "\t ><"; // This does not work
snprintf(buffer,50,"%s","echo Hello");
char *str_ptr = buffer;
curr_str = strsep(&str_ptr,delim);
if (curr_str != NULL)
printf("%s\n",curr_str);
curr_str = strsep(&str_ptr,delim);
if (curr_str != NULL)
printf("%s\n",curr_str);
return (0);
}
这个输出正是我所期望的。
echo
Hello
但是,只要我为分隔符添加“
cho
不知何故,第一个字符被截断了。发生这种情况是否有原因?
谢谢。
解决方法
strsep
的第二个参数,delim
是一个空终止字符串(就像 C 中的所有字符串一样),所以你必须为终止字符留出空间:
const char delim[5] = "\t ><"; // This does work
//const char delim[] = "\t ><"; // or this
如果你不结束字符串,它会通过数组探索内存并找到许多新的分隔字符来使用,这就是你的情况。
,“...第一个字符被截断。发生这种情况的背后有什么原因吗?”
是的,未定义行为是由在 C 字符串函数中使用的非空终止字符数组引起的。
如果填充的 const char delim[4]
不包含空终止符,它将只是一个 char
数组,而不是一个 C string。它可能会也可能不会表现出奇怪行为,但如果与任何 undefined behavior(例如 {{1} }.
curr_str = strsep(&str_ptr,delim);
有 4 个字符的空间。
const char delim[4];
可以在内存中这样概念化:
"\t ><" //contains exactly 4 char
它应该包含以下内容:
|\t| |>|<|?|?|?| // ? = unknown content,possibly no null termination
^end of owned memory
在声明中需要更多空间,例如以下两个选项之一:
|\t| |>|<|\0|?|?| // null termination
^end of owned memory (5 char wide)
或
const char delim[5] = "\t ><";
,
const char delim[4] = "\t ><";
没有定义正确的 C 字符串,因为空终止符没有空格。因此,内存中 delim
之后的任何非零字节都将成为分隔符字符串的一部分。
这当然是未定义的行为,在您的情况下,编译器可能会在没有任何填充的情况下将 delim
定位在 buffer
之前,有效地继续使用所有的分隔符序列字符串 "echo Hello"
中的字符。这会导致对 strsep
的第一次调用返回一个空字符串。
您可以检查此 Godbolt instance 是否在 32 位模式下确实如此,但在 64 位模式下则不然(删除 -m32
编译器选项)。
这个问题很容易解决。您可以让编译器确定 delim
数组的长度:
const char delim[] = "\t ><";
或者你可以使用指向字符串常量的指针:
const char *delim = "\t ><";