strlen(&string[]) 是如何工作的?

How does strlen(&string[]) work?

这个问题可能有点长。我正在用 C 测试一些字符数组,所以出现了这段代码。

char t[10];
strcpy(t, "abcd");
printf("%d\n", strlen(&t[5]));
printf("Length: %d\n", strlen(t));

现在显然 strlen(&t[5]) 产生 3 而 strlen(t) returns 4.

我知道字符串长度是4,从插入四个字符就可以看出这一点。但是为什么 strlen(&t[5]) return 3?

我的猜测是

String:   a | b | c | d | 0 | 0 | 0 | 0 | 0 | [=11=]
Position: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 

strlen(&t[5])看6、7、8位组成的字符串的长度(因为第10个字符是NULL终止符对吧)?

好的,那我做了一些实验,稍微修改了一段代码。

char t[10];
strcpy(t, "abcdefghij");
printf("%d\n", strlen(&t[5]));
printf("Length: %d\n", strlen(t));

现在 strlen(&t[5]) 产生 5,而 strlen(t) 是 10,正如预期的那样。如果我正确理解字符数组,状态现在应该是

String:   a | b | c | d | e | f | g | h | i | j | '[=13=]'
Position: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10

为什么这次 strlen(&t[5]) return 5?我已经声明了一个长度为 10 的字符数组,那么,按照上面应用的相同逻辑,结果应该是 4 吗?

此外,我不应该 运行 陷入一些编译器错误,因为 NULL 终止字符实际上位于第 11 位吗?我是 C 的新手,非常感谢任何人的帮助。

首先让我告诉你,你的"assumption"

String:   a | b | c | d | 0 | 0 | 0 | 0 | 0 | [=10=]
Position: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 

不正确。根据您的代码,这些值仅 "guaranteed" 到索引 4,不超过索引 4。

对于第一种情况,在你的代码中

  printf("%d\n", strlen(&t[5]));

由于各种原因是错误的,

  • 您应该使用 %zu 作为 size_t 类型。
  • &t[5] 没有指向有效的 string.

以上任何一个(或两者)都会导致undefined behavior并且任何输出都无法证明。

详细说明,定义如

char t[10];
strcpy(t, "abcd");

您为 t 填充了索引 0 到 3,索引 4 包含空终止符。 t[5] 之后的内容是不确定的。

因此,&t[5] 不是指向字符串第一个元素的指针,因此不能用作 strlen() 的参数。

  • 它可能 运行 在搜索空终止符时越界并遇到无效的内存访问,并且作为副作用,会产生分段错误,
  • 它可能会在边界内找到空终止符(只是另一个垃圾值)并报告 "seemingly" 有效长度。

这两种可能性都是一样的,而且不太可能,真的。 UB就是UB,没有理由。

那么,对于第二种情况,你说的

char t[10];
strcpy(t, "abcdefghij");

再次越界访问内存。

您总共有 10 个数组元素来存储一个字符串,因此您可以有 9 个其他 char 元素,外加一个空终止符(以将 char 数组限定为字符串)。

但是,您正试图放置 10 个 char 元素,外加一个空字符(在 strcpy() 中),所以您是差一个,访问的内存超出限制,调用 UB。

char t[10]; 未初始化,因此它仅包含垃圾值 1)strcpy(t, "abcd"); 用字符串 "abcd" 和空终止符覆盖前 5 个字符。

然而,&t[5]指向空终止后的第一个字符,这仍然是垃圾。如果你从那里调用 strlen,任何事情都可能发生,因为传递的指针不太可能指向空终止字符串。


1) 垃圾 = 不确定的值。假设一个健全的 2 的补码系统,缓冲区的地址 t 被采用,因此代码不会调用未定义的行为,直到 strlen 开始读取数组 t 的边界之外. Reference.

问题 1:

My guess is that

String:   a | b | c | d | 0 | 0 | 0 | 0 | 0 | [=10=]
Position: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 

这个假设是错误的。 该数组未初始化为包含 0 个值,但包含一些 "random" 垃圾。 复制 "abcd" 后,数组的上半部分(t[5] 等)仍未触及,由于未定义的行为导致字符串长度为 "random"。

问题 2:

If I understand character arrays correctly, the state should now be

String:   a | b | c | d | e | f | g | h | i | j | '[=11=]'
Position: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10

又错了。 您的数组仅包含 10 个字符。它们的索引为 0..9。索引 10 超出范围。 您的复制操作可能会导致此布局,也可能会在写越界时崩溃。

但这并没有被编译器检查。如果您 运行 遇到问题,那么它将在 运行 时间内出现。