对 strlen 在 C 中的工作方式感到困惑

Confused with how strlen works in C

最近一直在学习C语言,看到一个strlen的源码,把我搞糊涂了,不得不去其他地方查了下,还是没看懂。

strlen:

#include <stdio.h>

int strlen(const char *str)
{
    const char* eos = str; // 1

    while (*eos++); // 2

    return (eos - str - 1); // 3
}

int main()
{
    int len = strlen("Hello");

    printf("Len: %d" , len);

    return 0;
}

我不明白为什么我们要使用局部变量 eos 以及为什么我们要在一个空的 while 循环中使用它然后从 strlen 函数返回最后一行?

为什么会有一个空的while循环?循环递增eos,直到它直接指向null终止符之后的位置。表达式 *eos++ 是告诉您的计算机获取当前指向的字符值 eos 的非常紧凑的方式,然后递增 eos 使其指向下一个字符。这个while循环不需要body。

为什么要使用局部变量?因为我们是递增一个指针(即eos),而且我们还需要一个指向字符串开头的指针在最后的计算中(即 str),我们不能简单地对所有内容使用 str。我们至少需要一个其他变量来完成这项工作。

最后一行是如何工作的?表达式eos - str做指针减法,所以它计算这两个指针和returns之间的距离作为一个整数。然后我们减去1使答案正确。

指针eos用于沿着字符串前进。 EOS 是字符串结尾的缩写。 while 循环是空的,因为它不需要做任何事情——因为只需要推进指针。一旦 eos 指向空终止符,循环就会退出。然后函数的最后一行从开始指针中减去结束指针,得到 eos 移动过去的字符数。最后一个 -1 是为了纠正这样一个事实,即由于 post 递增运算符,eos 总是比应有的提前一个字符。

一个不那么令人困惑的实现是:

int strlen(const char *str)
{
    const char* eos = str; // 1

    while (*eos)eos++; // 2

    return (eos - str); // 3
}

对于初学者来说,函数 return 类型应该是 size_t

size_t strlen(const char *str);

如果两个指针指向同一数组的元素,则指向具有较高索引的元素的指针与指向具有较低索引的元素的指针之间的差异产生两个索引之间的元素数。

例如,如果你有一个像

这样的数组
const char s[] = "12";

和两个指针

const char *p1 = &s[0];
const char *p2 = &s[1];

那么差

p2 - p1

产生值 1

原函数内的this指针

const char* eos = str;

将在传递的字符串中移动,直到找到终止零字符。

while (*eos++);

这个循环可以改写成

while ( *eos++ != '[=16=]' );

后缀表达式的值*esp++是指针递增前指向字符的值。

因此,当找到终止零时,指针 eos 将指向终止零字符之后的内存,并且循环停止迭代。

因此现在指针 str 指向传递的字符串的开头,指针 eos 指向终止零字符 '[=24=]' 之后的内存。

所以区别

(eos - str - 1)

给出传递的字符串中终止零字符之前的字符数 '[=24=]'

中的字符串是由所谓的空字符终止的字符数组;记住这一点也很有用,因为在 C 中没有明确的字符串类型,如果你想对你作为字符串处理的字符数组进行操作,你需要有一个指向第一个字符的指针。

话虽如此,eos 变量被初始化为指向字符串的开头(通过将其等同于 str - 请记住,它们都只是指向字符的指针,因此等同于他们意味着他们指向同一件事)。

现在,'empty' while 循环有副作用。因为递增 (++) 运算符用于评估条件,所以 eos 递增 - 因为它是一个指针,这意味着在循环的每次迭代中,eos 指向连续人物。它基本上是 'walking' 沿着字符串。

这一直持续到循环条件的计算结果为 false。在 C 语言中,只有空字符的计算结果为假(对于所有可能的字符值;更一般地说,零为假,非零为真)。所以基本上,eos 将在字符串末尾停止递增。

现在进入 return 语句。此时我们有两个变量——一个名为 str 的变量仍然指向字符串的开头,另一个名为 eos 的变量指向字符串的结尾。好吧 - 实际上 eos 由于在循环条件中完成的递增而溢出。这里准确的说是指向字符串末尾空字符后的内存地址。

所以,通过更多的指针运算,如果我们从 eos 中减去 str,然后减去溢出的 1...那么,我们得到地址的差异最后一个和第一个字符,即字符串的长度。

很奇怪在循环中使用了post-增量运算符。如果它被编码为 while (*++eos);,即使用 pre-increment 运算符,则递增将发生在 before 评估 eos 这意味着它不会超出字符串的末尾并且不需要在 return 语句中减去额外的 1 。好吧。