第一个问题是有关字符串末尾的空字符\0
的信息,何时需要\0
/自动添加有很多变体。
声明字符数组时,是否需要指定\0
或否?或者在什么情况下,我应该指定\0
,在什么情况下不?有人可以提供全面的摘要吗? (如this post中一样)。如果您觉得我的问题模棱两可,则更具体的问题是在C中声明字符串时,最好的方法是char string[] = "first string"
,因为例如,我可以这样做{{1} }不用担心尺寸问题?
第二个问题:我有
strcat(string, another_string)
1 char a[] = "kenny";
2 char b[3];
3 strncpy(b, a, (int)(sizeof(b) - 1));
4 printf("%i\n", (int)sizeof(b)); // 3
5 printf("string length: %i\n", (int)strlen(b)); // string length: 8
6 printf("%s\n", b); // give me random stuff like kekenny or keEkenny
表现正常我只是迷路了C字符串中正在发生的事情。我曾经经常使用C ++,但仍然不了解C字符串的行为。
答案 0 :(得分:4)
根据定义,在语句中:
char string[] = "first string"
string
完全填充了它可以容纳的所有内容:
在内存中看起来像这样:
|f|i|r|s|t| |s|t|r|i|n|g|\0|?|?|?|
/// end of legal memory ^
...说明以下原因:
strcat(string, anythingElse);
是 undefined behavior 。 (否则称为 nasal demons 。)
关于 strncpy(,,) 的用法。由于不能保证在使用后包含nul
字符,因此建议始终将nul
显式附加到新字符串中的适当位置:
strncpy (target, source, n);
target[n] = 0;
在您的示例中,n
== (sizeof(b) - 1)
请注意,在使用(int)
时,在上面的表达式中不需要强制转换为sizeof
,因为strncpy(,,*)
的第三个参数的类型为size_t
:
char *strncpy (char Target_String[], const char Source_String[], size_t Max_Chars);
strncat 的用法,确实将nul
字符附加到结果目标字符串的末尾,从而消除了需求明确附加一个nul
。
答案 1 :(得分:4)
关于C字符串的事情是它们是非常低级的,您还需要记住很多额外的东西,有时还需要“手工”完成。
(相比之下,C ++ std :: strings几乎完全是普通的高级类型。)
回答您的特定问题:
几乎不需要显式提供\0
。几乎唯一要做的就是完全手工构建字符串。例如,此代码有效:
char str[10];
str[0] = 'c';
str[1] = 'a';
str[2] = 't';
str[3] = '\0';
printf("%s\n", str);
但是,如果您省略了对str[3]
的明确分配,它将表现为异常。 (但是,如果您不像这样手动创建字符串,则不必担心太多。)
使用strcpy
复制字符串时必须非常小心。您必须确保目标字符串(“缓冲区”)足够大。 C语言中的任何内容都不会为您解决这个问题–没有任何东西可以确保目标足够大;如果它不够大,没有什么警告您。但是,如果它不够大,则可能会发生最奇怪的事情-包括它似乎起作用,即使它不起作用。 (其正式名称是“未定义的行为”。)
特别是如果你写
char string[] = "first string";
strcat(string, another_string);
您所得到的只是一个简单而纯净的错误。 “以这种方式您不必担心大小问题”,这不是真的。当您说char string[] = "..."
时,编译器将字符串的大小调整到足以容纳初始化程序(及其初始化\0
)的大小,在这种情况下,"first string"
为13个字节。 []
并不是 的意思是“使此字符串足够大,以至于我无法尝试推入的任何文本”。
使用strncpy
时必须更加小心。实际上,我的建议是完全不使用strncpy
。它实际上所做的是不寻常的,特殊的,难以解释的,而且无论如何通常不是您想要的。 (一方面,如果复制的内容少于完整字符串,则不会在目标位置添加“ \ 0”,这有助于解释为什么会出现“ kekenny”之类的东西。)
答案 2 :(得分:2)
第一个问题
这样做的时候
char string[] = "first string";
^
No size specified
编译器将保留可以完全容纳文本“第一个字符串”和NUL终止符的内存。如果打印字符串的大小,将得到13。换句话说,变量不能保存更多数据,因此连接另一个字符串毫无意义。
您可以这样做:
char string[100] = "first string";
然后可以连接另一个字符串。
第二个问题
首先要知道的是C中的字符串是包含NUL终止符的字符数组。
当您这样做:
char b[3];
您得到一个未初始化的数组,即b
可以包含任何内容-如b = { ? , ? , ? }
然后您这样做:
strncpy(b, a, (int)(sizeof(b) - 1));
表示您将前两个字符从a
复制到b
。
所以现在我们知道b
是b = { 'k' , 'e' , ? }
,请注意b
的第三个字符仍未初始化。
因此,当您这样做时:
printf("string length: %i\n", (int)strlen(b));
printf("%s\n", b);
您使用b
就像是一个字符串,但不是。没有NUL终止。因此,函数(printf
,strlen
)给出了错误的结果。使用char数组不调用这些函数会导致NUL终止,这是未定义的行为,即可能发生任何事情。
似乎发生了两件事:
a)b
中未初始化的字符恰好是一个“ E”(在您的一个示例中)
b)字符串文字“ kenny”恰好位于变量b
之后的内存中。
因此,两个字符串函数确实会看到带有len 8的字符串“ keEkenny”。
要解决此问题,您可以执行以下操作:
strncpy(b, a, (int)(sizeof(b) - 1));
b[sizeof(b) - 1] = '\0';
或干脆做:
char b[3] = { 0 };
因为这将初始化所有b
,即b = { '\0' , '\0' , '\0' }
答案 3 :(得分:1)
如果您阅读了strncpy
的文档,则很清楚地指出,如果您指定的大小不包含NUL终止符,则不会添加NUL终止符:
strncpy()函数类似,除了src的最多n个字节是 复制。警告:如果src的前n个字节中没有空字节, 放在dest中的字符串将不会以空值结尾。
因此,在以下情况下,您仅复制2个字符,而且它们都不是NUL终止符,因此您需要自己添加它。
strncpy(b, a, (int)(sizeof(b) - 1));
答案 4 :(得分:0)
您必须以某种方式将字符串终止符\ 0添加到b中。当找到\ 0时,printf(“%s \ n”,b)将停止。
这取决于您的内存,有时可能会出现段错误。