strlen() 如何计算当前函数中未定义的字符串的长度?

How does strlen() calculate the length of a string not defined in the current function?

据我了解,在 C 中,数组和指向字符串的指针并不隐式包含有关相应数组中元素数量的信息。这就是为什么除了 argv 之外还必须将 argc 传递给 main() 的原因。但是,在我的机器上,以下代码确实正确打印了字符串 "Hello" 的长度,即 5 即使该字符串未在与 printf().[=25= 相同的范围内定义]

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

void p1(char *a)
{
    printf("%lu\n", strlen(a));
}

void p2(char a[])
{
    printf("%lu\n", strlen(a));
}

int main(int argc, char *argv[])
{
    char *a = "Hello";
    char b[] = "Hello";
    char c[] = {'H', 'e', 'l', 'l', 'o', '[=10=]'};
    char d[] = {'H', 'e', 'l', 'l', 'o'};

    p1(a);
    p2(a);

    printf("\n");

    p1(b);
    p2(b);

    printf("\n");

    p1(c);
    p2(c);

    printf("\n");

    p1(d);
    p2(d);

    return 0;
}

这是我的结果和机器类型:

$ ./a.out
5
5

5
5

5
5

6
6

$ uname -a
Linux loathe 3.16.0-37-generic #51-Ubuntu SMP Tue May 5 13:45:59 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

$ cat /etc/issue
Ubuntu 14.10 \n \l

strlen()如何确定字符串长度?为什么即使当字符串作为指针显式发送时,这仍然有效,如在函数调用函数 p1(a) 中?如果此行为仅出现在 gccclang 的较新版本中,它是什么时候开始的,我可以在我的程序中依赖它吗?


编辑:

此外,为什么strlen()在字符串中不包含空字节时会报告一个额外的字符? (编辑中添加)

编辑:我已经通过找到空字节解决了 strlen() 工作的断言。

How is strlen() determining the string length?

strlen() 搜索终止的 null ('[=12=]'),并从开始(指针)开始计算字符(即字节数),直到 null,不包括 null 本身。

记住,C 风格的字符串根据定义以 null 结尾。

请注意,由于 strlen() 的 return 类型是 size_t,因此应使用 %zu 格式说明符来打印 return 值.


编辑:

如果 char 数组不是空终止的,即 有资格被称为 字符串 。在任何 string 相关的库函数中使用这种数组肯定会调用 undefined behaviour.

有关详细信息,请查看 man page 以获得 strlen()

Additionally, why does strlen() report an additional character when the null byte is not including in the string? (added in edit)

明确回答您的编辑:您的假设是错误的。它可能 return 42、崩溃甚至删除您的文件。阅读之前评论所建议的未定义行为。

它报告 6 的技术原因是,偶然地,在您的最终程序中,您的数组后面有一个 0 字节 2 字节。这不会使数组成为有效的 C 字符串。

刚看完标准。它清楚地定义了什么strlen does你的断言是错误的。

您的 char 数组 d 在与字符串函数一起使用时调用 undefined behaviour(最后一句话)。

首先,您的函数 p1p2 完全相同。 不是一种情况,一种是把字符串当作指针,另一种是把字符串当作数组。根据定义,字符串是字符数组。 但是,每当我们操作数组时(特别是当我们将数组传递给函数时),我们实际使用的是指向数组第一个元素的指针。所以你的函数p2,尽管它看起来像是在接收一个数组,但实际上是在接收一个指针。另见 this question in the C FAQ list.

现在,其次,C 字符串 确实 明确地包含其长度的指示:所有适当的字符串都包含空终止符,特殊字符 '[=13=]',这标志着它的结束。确定字符串长度的方法是通读它,计算字符数,直到找到 '[=13=]'。这正是 strlen 所做的。

最后,我们来到你的数组d。正如我想您知道的那样,您已安排此数组看起来有点像字符串 "Hello",但 没有 终止空字符。因此,当 strlen 尝试计算 d 的长度时,它将直接越过末尾,并开始检查数组末尾之后存在的任何随机内存区域。可能会发生三种情况:

  • 内存中数组之后的下一个内容可能恰好是 0 字节。在这种情况下,纯属偶然,strlen 恰好会计算出正确答案 5.
  • 更有可能是内存中跟在数组后面的一些其他随机字节,并且其中某处是一个0字节。在那种情况下,strrlen 将计算出一个大于——也许大得多——的答案。这似乎已经发生了。
  • 在找到 0 字节之前,strlen 可能会 运行 超出分配给您的进程的内存末尾,在这种情况下,您会收到一些操作系统级别的错误消息,例如分段冲突或总线错误或 "This program has terminated unexpectedly" 或蓝屏死机。

strlen

的实现
size_t strlen(const char * str) {
    const char * s;
    for (s = str; *s; ++s);
    return(s - str);
}

循环终止条件*s是内部*s != '[=13=]'。因此,它循环直到找到终止 null 字符。而且,它对 pointer to charchar array 的行为相同。