C字符串表现得很奇怪

时间:2019-02-06 15:25:51

标签: c arrays string

第一个问题是有关字符串末尾的空字符\0的信息,何时需要\0 /自动添加有很多变体。 声明字符数组时,是否需要指定\0或否?或者在什么情况下,我应该指定\0,在什么情况下不?有人可以提供全面的摘要吗? (如this post中一样)。如果您觉得我的问题模棱两可,则更具体的问题是在C中声明字符串时,最好的方法是char string[] = "first string",因为例如,我可以这样做{{1} }不用担心尺寸问题?

第二个问题:我有

strcat(string, another_string)
  • 3:我只想将2个字节传递给b
  • 4: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 表现正常
  • 5:但是为什么变成8?
  • 6:为什么它会给我随机的东西,例如kekenny或keEkenny

我只是迷路了C字符串中正在发生的事情。我曾经经常使用C ++,但仍然不了解C字符串的行为。

5 个答案:

答案 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

所以现在我们知道bb = { 'k' , 'e' , ? },请注意b的第三个字符仍未初始化。

因此,当您这样做时:

printf("string length: %i\n", (int)strlen(b));
printf("%s\n", b);

您使用b就像是一个字符串,但不是。没有NUL终止。因此,函数(printfstrlen)给出了错误的结果。使用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)将停止。

这取决于您的内存,有时可能会出现段错误。