你问题的前半部分相当于这个:
我是新手,几周前开始了解道路交通。我看过一本书,说你要等绿灯才进入路口,但是当我不等待进入路口时,它就可以正常工作了。这怎么可能?
换句话说,你只是走运了。碰巧的是,即使您构造了一个没有适当\0终止符的字符数组,内存中恰好在ein之后有一个 0 字节apple,所以它仍然有效。但它根本不能保证会起作用,就像它不能保证你可以继续逆光过马路,最终不会被撞到一样。
继续你的第二个问题,当你读到“char是整数数据类型的一个子集”时,这根本不意味着你通常会使用 a 的任何地方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 |
+---+---+---+---+ +---+---+---+---+
这是一个正确以 null 结尾的 数组char:
char ca[] = { 'p', 'e', 'a', 'r', '\0' };
+---+---+---+---+---+
ca: | p | e | a | r |\0 |
+---+---+---+---+---+
当printf打印这个字符串或strlen计算它的长度时,它们从开头开始,一次一个字节地沿着字符串移动,直到找到\0.
但这里有一个数组int:
int ia[] = { 'p', 'e', 'a', 'r', '\0' };
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
ia: | p | e | a | r | \0 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
但是我画的有点不对,因为实际上,每个 int 中额外的三个字节不是用空格填充的,而是用零字节填充的。(就好像我们想用前导零来表示数字1,也就是0001。)所以更准确的图是这样的;
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
ia: | p \0 \0 \0 | e \0 \0 \0 | a \0 \0 \0 | r \0 \0 \0 | \0 \0 \0 \0|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
因此,当printforstrlen从开头开始并一次一个字节地处理数组以寻找终止符\0时,他们会立即找到一个,就在第一个字母之后。
这里要考虑的重要一点是printf和strlen被定义为对char. 而且由于 C 的工作方式,他们无法知道您已作弊并传递了一个数组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 *'
这些消息指的是 type char *,它是指向 - 的指针char,因为当您将数组传递给函数时,实际上传递的是指向数组第一个元素的指针。(但这是另一天的话题。但这与我所说的printf和strlen“从字面上获取相同的记忆并将其视为”它是一个字符数组有很大关系。)