我是编程的新手,几周前开始学习。我已经读过一本书,字符数组应该以{{1}}结尾,但是当我创建一个没有\0
的数组时,它可以正常工作。怎么可能??
\0
上面的代码输出是
#include<stdio.h> #include<string.h> int main() { char a[] = {'a','p','p','l','e'}; printf("%d\n",strlen(a)); printf("%s\n",a); return 0; }
我也读到char是整数数据类型的子集,但是当我用int数据类型创建上述数组时,它不能正常工作。
5
apple
上面的代码输出是
#include<stdio.h> #include<string.h> int main() { int a[] = {'a','p','p','l','e'}; printf("%d\n",strlen(a)); printf("%s\n",a); return 0; }
为什么只考虑数组的第一个元素?
答案 0 :(得分:2)
您的问题的前半部分与此等效:
我是新手,几周前开始学习道路交通知识。我已经读过一本书,您应该在进入交叉路口之前等待绿灯,但是当我不等待进入交叉路口时,它可以正常工作。怎么可能?
换句话说,你很幸运。碰巧的是,即使您构建了没有适当的pip install --upgrade certifi
终止符的字符数组,\0
中e
之后的内存中也恰好有一个0字节,因此无论如何工作。但这并不能保证一定能正常工作,甚至不保证您可以一直过马路,不会最终受到打击。
关于第二个问题,当您读到“ apple
是整数数据类型的子集”时,这并不意味着您通常会在任何地方使用char
,使用char
。
这是内存中的一些字符。它们每个都是一个字节大小:
int
内存中有一些整数。在现代计算机上,每个字节的大小可能约为四个字节:
char c1 = 'p', c1 = 'e', c3 = 'a', c4 = 'r';
+---+ +---+
c1: | p | c2: | e |
+---+ +---+
+---+ +---+
c3: | a | c4: | r |
+---+ +---+
这里是int i1 = 'p', i1 = 'e', i3 = 'a', i4 = 'r';
+---+---+---+---+ +---+---+---+---+
i1: | p | i2: | e |
+---+---+---+---+ +---+---+---+---+
+---+---+---+---+ +---+---+---+---+
i3: | a | i4: | r |
+---+---+---+---+ +---+---+---+---+
的数组,以空值结尾:
char
当char ca[] = { 'p', 'e', 'a', 'r', '\0' };
+---+---+---+---+---+
ca: | p | e | a | r |\0 |
+---+---+---+---+---+
打印此字符串或printf
计算其长度时,它们从头开始并沿字符串一次移动一个字节,直到找到strlen
。 / p>
但这是\0
的数组:
int
但是我把它稍微画错了,因为实际上,每个int中的三个额外字节没有用空格填充,而是用零字节填充。 (这就像我们要用前导零表示数字1,即0001。)因此,更准确的图片如下所示:
int ia[] = { 'p', 'e', 'a', 'r', '\0' };
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
ia: | p | e | a | r | \0 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
因此,当 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
ia: | p \0 \0 \0 | e \0 \0 \0 | a \0 \0 \0 | r \0 \0 \0 | \0 \0 \0 \0|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
或printf
从头开始并一次处理数组一个字节以寻找终止的strlen
时,它们会在第一个字母之后立即找到一个。
这里要考虑的重要一点是, \0
和printf
被定义为对strlen
的数组进行操作。而且由于C的工作方式,他们无法知道您已经作弊并通过了char
数组。他们实际上使用了相同的内存并将其视为int
的数组,因此得到的结果与您期望的完全不同。
由于这样的错误很容易发生,因此,如果您这样做的话,好的编译器会警告您。对于您的代码,我的编译器向我发出了以下警告:
char
这些消息引用类型warning: incompatible pointer types passing 'int [5]' to parameter of type 'const char *'
warning: format specifies type 'char *' but the argument has type 'int *'
,它是指向char *
的指针,因为将数组传递给函数时,实际上传递的是指向数组第一个元素的指针。 (但这是另一天的话题。但这与我所说的char
和printf
“在字面上占用相同的内存并将其视为”一样,这与字符数组有关,而不是。)
答案 1 :(得分:2)
在char a[] = {'a','p','p','l','e'};
中,编译器计算您提供的值的数量,即5。然后,它将创建一个由五个char
组成的数组,并使用这些值对其进行初始化。
然后,在printf("%d\n",strlen(a));
和printf("%s\n",a);
中,行为不是C标准定义的,因为要求您在数组中使用零元素来指示结束位置。在尝试这种情况的情况下,可能发生了a
数组后的内存为零,导致程序打印“ 5”和“ apple”。但是,这并不总是会发生。
另外,strlen
的结果类型为size_t
,应使用%zu
而不是%d
打印。
在int a[] = {'a','p','p','l','e'};
中,编译器创建int
的数组。当您在printf("%s\n",a);
中使用它时,当int
期望指向printf
的指针时,您将传递指向char
的指针。 C的标准未定义其行为。常见的结果是,printf
将int
数组中的字节当作char
的数组来处理,尽管这不能依靠-C实现的实际行为可能变化。
由于int
比char
宽,因此包含值int
的{{1}}通常包含一个值为a
的字节和一个或多个带有值{值零。它还可能包含填充位。 a
中字节的顺序不是由C标准定义的。如果包含int
的字节恰好是内存中的第一个字节,而随后的字节为零,则a
可能会打印“ a”。但是,如果首先包含零的字节,则printf
会将其视为字符串的末尾,并且不打印任何内容。
同样,该行为不是由C标准定义的。上面的内容仅说明了您看到的内容将如何打印,而不是其他情况下的预期结果。
答案 2 :(得分:1)
将int[]
传递到strlen()
是错误的。 strlen()
需要字符。即使您提供了其他内容(并且关闭或忽略了编译器的所有警报警告),strlen()
也会将给定地址解释为char*
(无论其实际包含什么)。
严格来说,这是undefined behavior。
进行一些调查,我们可以探讨可能发生的情况:
char a[] = {'a','p','p','l','e'};
定义5个字符的数组。从内存中转储时,看起来可能是这样:
0x61 0x70 0x70 0x6c 0x65 ???? ???? ????
int a[] = {'a','p','p','l','e'};
,假设{end {1}}是32位,采用低字节序,则可能看起来像这样:
int
将0x61 0x00 0x00 0x00 0x70 0x00 0x00 0x00
0x70 0x00 0x00 0x00 0x6c 0x00 0x00 0x00
0x65 0x00 0x00 0x00 ???? ???? ???? ????
重新解释为a[]
(char*
会做什么),这将导致字符串长度为一。
但是,它仍然是未定义的行为...
答案 3 :(得分:1)
取决于硬件和实现,int
的长度可以超过2个字节。
在小尾数系统上,第一个字节为ASCII码“ a”,第二个字节为零(最大为sizeof(int))。因此,任何字符串函数都将其视为单个字符串。
Big endian系统将具有相反的字节顺序,如果我们将此int arrar解释为char数组,则第一个字符将为零,这将终止该字符串,并且其长度将为零。
您的第二个示例是错误的,因为您没有结尾的零,并将其用作字符串会调用UB。
您的char表初始化应为:
char a[] = {'a','p','p','l','e', 0};
或
char a[] = "apple";
由于字符串文字初始化也添加了终止nul。
答案 4 :(得分:0)
在32位编译器上,int
占用4个字节,char
占用1个字节。
如果将整数数组传递给strlen
,它将扫描整数a
中的第一个字节,下一个
3个字节为0,因此strlen
在第二个字节处停止,并将length
显示为1
。
答案 5 :(得分:0)
我在一本书中读过,字符数组应以
\0
结尾...
仅当您要将字符数组解释为字符串时才需要。在C语言中,字符串实际上是一维字符数组,以空字符\0
结尾。
在您的第一个示例中,char
数组a
只是字符数组。您很幸运strlen
和printf
给出了预期的输出。 strlen
函数返回终止的空字符之前的字符数。在这种情况下,数组a
之后的内存必须为0
。因此,您将从strlen
获得预期的输出。出于同样的原因,printf
也可以按预期方式工作,因为它写入的每个字节一直到并且不包括第一个空终止符。
在第二个示例中,您将一个整数指针传递给strlen
:
printf("%d\n",strlen(a));
编译器必须在其上发出警告消息,因为strlen
的参数类型为const char *
,并且您正在传递它int *
。
此外,在printf
中,您将参数作为整数指针。 %s
格式说明符期望使用char
指针。在这种情况下,行为是不确定的。