C字符串的行为很奇怪

C字符串的行为很奇怪,c,arrays,string,C,Arrays,String,第一个问题是关于字符串末尾的空字符\0,在需要/自动添加\0的时间方面有很多变化。 声明字符数组时,是否需要指定\0或否?或者在什么情况下,我应该指定\0,而在什么情况下不指定?有人能给出一个全面的总结吗?(如中所示)。如果您觉得我的问题模棱两可,那么更具体的一个问题是,在C中声明字符串时,最好的方法是什么,是char string[]=“first string”,因为例如,通过这种方法,我可以在不考虑大小问题的情况下执行strcat(string,另一个字符串) 第二个问题:我有 1 c

第一个问题是关于字符串末尾的空字符
\0
,在需要/自动添加
\0
的时间方面有很多变化。 声明字符数组时,是否需要指定
\0
或否?或者在什么情况下,我应该指定
\0
,而在什么情况下不指定?有人能给出一个全面的总结吗?(如中所示)。如果您觉得我的问题模棱两可,那么更具体的一个问题是,在C中声明字符串时,最好的方法是什么,是
char string[]=“first string”
,因为例如,通过这种方法,我可以在不考虑大小问题的情况下执行strcat(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 
  • 3:我只想将2个字节传递给b
  • 4:
    sizeof
    行为正常
  • 5:但是为什么会变成8
  • 6:为什么它会给我一些随机的东西,比如kekenny或keEkenny

我刚刚弄丢了C字符串中发生的事情。我过去经常使用C++,但仍然不理解C字符串是如何运行的。

< p>你必须以某种方式将字符串终止符0添加到B中。printf(“%s\n”,b)将在找到\0时停止


这取决于内存中的内容,有时可能会出现seg故障

如果您阅读了
strncpy
的文档,它非常清楚地指出,如果您指定的大小不包括NUL终止符,它将不会添加NUL终止符:

strncpy()函数与之类似,不同的是src的最大字节数为n 抄袭的。警告:如果src的前n个字节中没有空字节, 放置在dest中的字符串不会以null结尾

所以在下面的例子中,您只复制了2个字符,它们都不是NUL终止符,所以您需要自己添加它

strncpy(b, a, (int)(sizeof(b) - 1));

根据定义,在声明中:

char string[] = "first string"
strcat(string, anythingElse);
字符串
精确填充了它所能容纳的所有内容:

在内存中,它看起来是这样的:

|f|i|r|s|t| |s|t|r|i|n|g|\0|?|?|?|
/// end of legal memory    ^
…说明为什么会有以下声明:

char string[] = "first string"
strcat(string, anythingElse);
是。(有些人称之为。)

另外,关于用法。由于不能保证在使用后包含
nul
字符,建议始终将
nul
显式附加到新字符串中的正确位置:

strncpy (target, source, n);
target[n] = 0;
在您的示例中,
n
=
(sizeof(b)-1)

注意:当将
sizeof
用作
strncpy(,*)
的第三个参数的类型时,上述表达式中不需要对
(int)
进行强制转换:

char *strncpy (char Target_String[], const char Source_String[], size_t Max_Chars);

另一方面,的用法是将
nul
字符附加到结果目标字符串的末尾,从而消除了显式附加
nul

的需要。关于C字符串的一点是,它们的级别非常低,需要记住许多额外的事情,有时还需要“手动”执行

(相比之下,C++std::字符串几乎是完全正常的高级类型。)

在回答您的具体问题时:

您几乎不需要显式地提供
\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);
你得到的是一个bug,纯粹而简单。这不是真的“这样你就不用担心尺寸问题”。当您说
char string[]=“…”
时,编译器会将字符串的大小调整到足以容纳初始值设定项(及其
\0
),在本例中,
“第一个字符串”
为13个字节。
[]
并不意味着“让这个字符串足够大,可以容纳我要塞进的任何文本”


使用
strncpy
时必须更加小心。事实上,我的建议是根本不要使用
strncpy
。它的实际功能是不寻常的、特殊的、难以解释的,而且通常不是你想要的。(首先,如果您让它复制的字符串少于一个完整字符串,它不会向目标添加一个“\0”,这有助于解释为什么会出现“kekenny”这样的情况)。

第一个问题

当你这样做的时候

char string[] = "first string";
            ^
            No size specified
编译器将保留可以精确保存文本“第一个字符串”和NUL终止的内存。如果打印字符串的大小,将得到13。换句话说-变量可以而不是保存更多的数据,因此连接另一个字符串是没有意义的

你可以做:

char string[100] = "first string";
然后可以连接另一个字符串

第二个问题

首先要知道的是C中的字符串是包含NUL终止的字符数组

当您这样做时:

char b[3];
strncpy(b, a, (int)(sizeof(b) - 1));
printf("string length: %i\n", (int)strlen(b));
printf("%s\n", b);
您将得到一个未初始化的数组,即
b
可以包含任何内容,如
b={?,?,?}

然后你会:

char b[3];
strncpy(b, a, (int)(sizeof(b) - 1));
printf("string length: %i\n", (int)strlen(b));
printf("%s\n", b);
这意味着您将前两个字符从
a
复制到
b

现在我们知道
b
b={'k','e',?}
注意
b
的第三个字符仍然没有初始化

所以当你这样做的时候:

char b[3];
strncpy(b, a, (int)(sizeof(b) - 1));
printf("string length: %i\n", (int)strlen(b));
printf("%s\n", b);
您使用
b
就像它是一个字符串,但它不是。没有NUL终止。因此,函数(
printf
strlen
)给出的结果不正确。使用字符数组调用这些函数,而不使用