3

我目前正在学习 C,我对 char 数组和字符串之间的差异以及它们的工作方式感到困惑。

问题一:

为什么源代码 1 和源代码 2 的结果存在差异?

源代码1:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char c[2]="Hi";
    printf("%d\n", strlen(c));   //returns 3 (not 2!?)
    return 0;
}

源代码2:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char c[3]="Hi";
    printf("%d\n", strlen(c));   //returns 2 (not 3!?)
    return 0;
}

问题2:

字符串变量与 char 数组有何不同?如何用最小要求的索引号声明它们,允许存储 \0(如果有的话)(请阅读下面的代码)?

char name[index] = "Mick";   //should index be 4 or 5?

char name[index] = {'M', 'i', 'c', 'k'};   //should index be 4 or 5?

#define name "Mick"   //what is the size? Is there a \0?

问题 3:

终止的 NUL 是否只跟随字符串而不跟随字符数组?那么字符串“Hi”的实际值为[H][i][\0],而char数组“Hi”的实际值为[H][i]?

问题4:

假设 c[2] 将存储 "Hi" 后跟 \0 (不确定这是如何完成的,使用gets(c)可能?)。那么 \0 存储在哪里?它是存储在 c[2] 之后“某处”变成 [H][i]\0 还是 c[2] 附加一个 \0 变成 c[3] 即 [H][i][\0 ]?

有时字符串/字符数组后面有一个 \0 并且当我比较两个变量时会引起麻烦,if (c1==c2)因为它很可能返回 FALSE (0),这非常令人困惑。

详细的答案表示赞赏。但是保持你的回答简短有助于我的理解:)提前谢谢你!

4

4 回答 4

3

问题一:

为什么源代码 1 和源代码 2 的结果存在差异?

源代码1:

#include <stdio.h>
#include <string.h>

int main()
{
    char c[2]="Hi";
    printf("%d", strlen(c));   //returns 3 (not 2!?)
    getchar();
}

源代码2:

#include <stdio.h>
#include <string.h>

int main()
{
    char c[3]="Hi";
    printf("%d", strlen(c));   //returns 2 (not 3!?)
    getchar();
}

回答: 因为在第一种情况下,c[]只是拿着“Hi”。strlen 在最后寻找一个零,并且根据后面的确切内容,c[]迟早会找到一个,或者崩溃。我们不能说不知道c[]数组后面的内存中有什么。

问题2:

字符串变量与 char 数组有何不同?如何用最小要求的索引号声明它们,允许存储 \0(如果有的话)(请阅读下面的代码)?

char name[index] = "Mick";   //should index be 4 or 5?

char name[index] = {'M', 'i', 'c', 'k'};   //should index be 4 or 5?

答案 真的取决于你想做什么。如果您想实际将内容用作字符串,则可能为 5。但是没有什么说你不能将“Mick”存储在一个 4 个字符的数组中——你只是不能使用 strlen 来找出它有多长,因为 strlen 将继续为 5 并且很可能(很多)进一步找到长度,如果接下来的几个内存位置没有零,则可能导致崩溃,因为最终将没有有效的内存地址可供读取。

#define name "Mick" //what is the size? Is there a \0?

这完全没有大小,直到您在某处使用名称。#defines 不是编译器所看到的一部分——如果你在任何地方使用,预处理器将被替换name——希望这是编译器可以理解的地方。然后适用与上一个答案相同的规则 - 这取决于您要如何使用字符数组。要正确操作、和几乎所有其他函数,最后需要一个零。"Mick"namestrlenstrpystr...

问题 3:

终止的 null 是否仅跟随字符串而不跟随 char 数组?那么字符串“Hi”的实际值为[H][i][\0],而char数组“Hi”的实际值为[H][i]?

是的,不,也许。这完全取决于您如何使用"Hi"字符串文字(这是“双引号内的东西”的技术名称)。如果编译器是“允许的”,它会在最后放一个零。但是,如果您将数组初始化为给定的大小,它将把字节填充在那里,如果没有零空间,那是您的问题,而不是编译器的问题。

问题4:

假设 c[2] 将存储 "Hi" 后跟一个 \0 (不确定这是如何完成的,也许使用 gets(c) ?)。那么 \0 存储在哪里?它是存储在 c[2] 之后“某处”变成 [H][i]\0 还是 c[2] 附加一个 \0 变成 c[3] 即 [H][i][\0 ]?

在 c[2] 中,除了 'H'、'i' 之外,不知道存储了什么 [从技术上讲,它很可能是“地球的尽头”——用计算机术语来说,那是“无法存储的内存”读取 - 在这种情况下strlen,您的程序会崩溃,因为 strlen 读取的内容超出了地球的尽头]。但 if 也可以是零、一、字母“a”、数字 42 或任何其他 8 位[1] 价值。

当我通过 if (c1==c2) 比较两个变量时,有时在 string/char 数组后面有一个 \0 并且会导致麻烦,因为它很可能返回 FALSE (0),这很令人困惑。

如果 c1 和 c2 是 char 数组,那总是错误的,因为 c1 和 c2 永远不会有相同的地址,并且当以这种方式在 C 中使用数组时,它变成“在内存中的第一个元素的地址大批”。所以无论 c1 和 c2 的内容是什么,它们的地址永远不会相同[因为它们是两个不同的变量,并且两个变量在内存中不能有相同的位置 - 这就像试图将两辆车停在一个停车位大到只能容纳一辆车——不,在我们的思想实验中不允许压碎任何一辆车]。

[1] Char 不保证为 8 位。但是,让我们暂时先讨论一下。

于 2012-12-31T22:11:31.770 回答
3

答案 1:在代码 1 中,您有一个不是字符串的 char 数组;在代码 2 中,您有一个 char 数组,它也是一个字符串。

答案 2:字符串是一个 char 数组,其中(至少)一个元素具有值0;如果您将大小部分留空,编译器将自动用可能的最小值填充它。

char astring[] = "foobar"; /* compiler automagically uses 7 for size */
printf("%d\n", (int)sizeof astring);

答案 3:一个 char 数组,其中一个元素是NUL字符串;没有元素的 char 数组NUL不是字符串。

答案 4:定义为容纳两个元素 ( ) 的数组char c[2];不能容纳三个元素。如果它将是一个字符串,则它只能是空字符串或具有 1 个字符的字符串。

于 2012-12-31T21:46:57.670 回答
1

strlen()适用于\0终止字符并且在C所有字符串中都应该\0终止。因此,当您只为 2 个字符提供了 2 个空格Hi没有\0. 因此,您正在Undefined Behavior进入strlen()。如果 char c[3] = "Hi";\0第三位,strlen() 将计算实际长度。

How to declare them with the minimum required index numbers allowing \0 to be stored if any ?

当您不确定 char 数组的大小时,请这样做:

char c1[] = "Mike"; // strlen = 4 
char c2[] = "Omkant" // strlen = 6

笔记 :

编辑:在上述没有明确提及尺寸的情况下,不要sizeofstrlen().

strlen()仅返回 sizeof 字符数给出字符数加一(对于\0字符)。

所以 sizeof 总是比 strlen() 返回的数字大 1。

于 2012-12-31T21:31:59.343 回答
1

运行源代码一是未定义的行为,因为strlen()需要一个以 NUL 结尾的字符串,而事实c[2] = "Hi"; /* = { 'H', 'i' } */并非如此。字符串与 char 数组的不同之处在于,字符串是一个 char 数组,在数组的某处至少有一个 NUL 字节。

剩下的答案应该很容易从这个事实中得出。

要在初始化时自动调整 char 数组的大小以匹配字符串文字的大小,只需不指定数组大小:

char c[] = "This will automatically size the c array (including the NUL).";

请注意,您不能将 char 数组与 == 运算符进行比较。你必须使用

if (strcmp(c1, c2) == 0) {
   /* Equal. */
} else {
   /* Not equal. */
}
于 2012-12-31T21:32:46.367 回答