C语言中数组和字符串的混淆问题

3

S1S2S3有什么区别呢?

char S1[6];
S1[0] = 'A';
S1[1] = 'r';
S1[2] = 'r';
S1[3] = 'a';
S1[4] = 'y';

char S2[6] = {'A','r','r','a','y'};

string S3 = "Array";

当我使用if (strcmp(a,b) == 0)运行程序时,其中a, b = S1, S2, S3。它显示S2S3相同,而S1S2不同。 为什么会这样?为什么不是所有三个都相等?
当我将'\0'加回到S1bS1c中,所有3个字符串都相同。这是可以理解的。
但为什么在我的第一个尝试中,S2S3相同呢?我也没有包含'\0'。我怀疑S1S2应该是相同的,但不是S2S3
有谁能告诉我我的想法错在哪里吗?
感谢您的答案。我已经尝试并更改了以下设置:
char S1[5];
S1[0] = 'A';
S1[1] = 'r';
S1[2] = 'r';
S1[3] = 'a';
S1[4] = 'y';

char S2[5] = {'A','r','r','a','y'};

string S3 = "Array";

现在显然 S2S3 不同,因为它们之间有一个'\0'的区别。 然而,如果我使用strcmp来比较这两个字符串,为什么S1S2这次还是不同呢?


3
“string” 不是标准的 C 类型。 - Basile Starynkevitch
S1[5] 未初始化,可能包含 '\0'S2[5] == '\0',因为当对象被初始化(不同于赋值)时,任何未指定的元素都被设置为零。 - Keith Thompson
strcmp函数依赖于\0字节来决定何时停止比较,这就是为什么对于该函数s1和s2不相同的原因:它会比较可能包含随机数据的更多字节。 - Rodrigo Gómez
你是否将名称 string 定义为 typedef? - Keith Thompson
当然...实际上我一直在使用它,并调用.h头文件来使用字符串... - CHANist
4个回答

4

比较数组的实际内存值:

  1. S1有6个元素大小,但您只为0-5指定了值,第6个元素没有明确设置,因此它保留先前分配给该内存位置的任何值。
  2. S2与S1类似,只提供了5个元素,但是在使用{,}语法时,任何额外的元素都会被清零。因此,char foo[5] = { 1, 2 }char foo[5] = { 1, 2, 0, 0, 0 }相同。
  3. S3使用字符串语法初始化数组,这将创建一个带有额外元素\0(空终止符)的char(或wchar_t)数组。

可视化效果如下:

S1 = 0x41, 0x72, 0x72, 0x61, 0x79, 0x??
S2 = 0x41, 0x72, 0x72, 0x61, 0x79, 0x00
S3 = 0x41, 0x72, 0x72, 0x61, 0x79, 0x00

注意,使用 strcmp 存在安全问题:它没有长度参数,会一直搜索直到遇到 \0,这可能永远不会发生(即导致段错误或访问冲突)。相反,请使用更安全的函数,如 strncmp 或(如果使用 C++)std::string 类型。

解释一下{}初始化所有额外元素为0,这就是为什么S2包含\0而S1不包含的原因。 - LoPiTaL

2

它显示S2和S3是相同的,而S1和S2是不同的。

S3包含了S1没有的空终止符。这个字符串S3 =“Array”;的意思是

| A | r | r | a | y | \0 |

当S2处于

| A | r | r | a | y | \0 |

当S1处于

| A | r | r | a | y | Garbage |

比较S1和S2可能导致UB(我猜测),因为S1没有以null结尾,并且我们没有传递长度给strcmp

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

int main(void) 
{
    char S1[6];
    S1[0] = 'A';
    S1[1] = 'r';
    S1[2] = 'r';
    S1[3] = 'a';
    S1[4] = 'y';
    S1[5] = 0;

    char S2[6] = {'A','r','r','a','y', 0};
    printf("%d" ,strcmp(S1,S2));
    return 0;
}

输出:

0

那么为什么S1和S2不同呢? - CHANist
我知道S3包含空终止符,而S2不包含。 那么为什么它们是相同的? 虽然S1和S2都没有空终止符,但为什么它们不同? - CHANist
@CHANist - S2 包含空终止符,而 S1 不包含。 - Sadique

1

strcmp()函数从两个字符串的第一个字符开始比较。如果它们相等,就继续比较下一组字符,直到字符不同或到达终止的空字符为止。

我认为使用这种方法比较S1和S2是不安全的。strcmp的输入是第一个字符的地址。S1没有以空字符结尾。虽然在两种情况下都分配了6个字节,但S1[5]没有初始化。有可能它们有一些垃圾值。这里的风险是strcmp将继续比较未分配的内存,寻找字符差异或空字符。这甚至可能导致段错误或访问冲突。

S1、S2、S3的内存对齐可视化可能是这样的:

S1 = A | r | r | a | y | ?
S2 = A | r | r | a | y | 0
S3 = A | r | r | a | y | 0

S2和S3之间的任何比较都是安全的。S1与S2或S3之间的比较可能不安全。


0

只是在现有的答案上添加一些内容

char S2[6] = {'A','r','r','a','y'};

string S3 = "Array";

两者都是以NULL结尾的,因此strcmp()可以很好地工作并且表明它们是相同的。虽然对于S1,赋值是显式完成的,但是该数组没有NULL终止符,因此在C中这不是一个有效的字符串。因此,使用strcmp()可能会导致未定义的行为。

关于S3的重点是S3是一个只读的字符串字面量。大多数这样的值都存储在只读位置。因此,在初始化后尝试向S3写入内容时,可能会看到崩溃。因此,在使用类似S3的赋值时,我们应该记住这一点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接