快速的strlen问题

6
我是一名有用的助手,可以为您翻译文本。

我又来打扰大家了,这次是一个可能非常简单的C语言问题。

使用以下代码:

int get_len(char *string){

    printf("len: %lu\n", strlen(string));

    return 0;
}

int main(){

    char *x = "test";
    char y[4] = {'t','e','s','t'};

    get_len(x); // len: 4
    get_len(y); // len: 6

    return 0;
}

两个问题。它们为什么不同,y为什么是6?谢谢大家。

编辑:抱歉,我知道该怎么解决了,我只是想理解发生了什么。所以strlen是否只是一直向前移动指针直到找到\ 0?另外,当我在主函数中而不是在get_len函数中执行strlen时,两者都为4。这只是巧合吗?


是的,那只是巧合。 - AnT stands with Russia
除了回答(虽然有点重复,但都很好),我想指出的是,虽然"test"是序列't','e','s','t',0的方便语法,但你实际上可以通过特殊情况使用char y [4] ="test";初始化字符数组,在这种情况下,最后的0将被省略(在y`中根本没有空间)。 - Pascal Cuoq
9个回答

15

y没有以null字符结尾。strlen()函数计算字符数直到遇到null字符。在你的代码中,它在找到第6个字符后找到了一个null字符,但它可以是任何数字。试试这个:

char y[] = {'t','e','s','t', '\0'};

下面是一个实现strlen()的例子(我脑海中的想法,没有我的K&R书方便,但我相信那里提供了一个实现):

size_t strlen(const char* s)
{
    size_t result = 0;
    while (*s++) ++result;
    return result;
}

1
@LearningC:没错。它会不断地增加指针并查看那里的内容。当它找到一个零时,它就停止并返回它查看了多少个字符(不包括零)。 - Fred Larson
4
@LearningC:是的。或者直到出现段错误而崩溃。或者直到格式化您的硬盘。如果您的输入没有以零结尾,那么其行为是未定义的。任何事情都有可能发生。 - AnT stands with Russia
AndreyT是正确的。在实践中,它通常会在出现任何问题之前找到一个空值。但你永远不能确定。我怀疑它是否会真正格式化你的硬盘,但你肯定会进入禁止访问的内存并导致分段错误。 - Fred Larson
@Fred:实际上,当它跨越空值应该存在的字节时,会发生一些不好的事情。由于(假设是8位整数),空字节被意外放置的概率并不比1/256好多少,在实践中,经常会发生一些不好的事情。 - sbi
2
@LearningC:这将成为您函数调用者的要求,即字符串必须以空字符结尾,就像调用strlenstrcpy时输入必须是以空字符结尾的字符串一样。 - Steve Jessop
显示剩余4条评论

4

This

char y[4] = {'t','e','s','t'};

不是一个正确的以零结尾的字符串。它是一个由四个字符组成的数组,没有结束标记'\0'strlen()只会计算到出现零为止的字符数量。但使用y时,它会一直计算到数组末尾并偶然发现一个零字节。
这样做会导致未定义的行为。代码有可能破坏你的硬盘。

您可以通过使用特殊的字符数组初始化语法来避免此问题:

char y[] = "test";

这将使用'\0'自动追加,初始化y五个字符


请注意,我也没有指定数组的大小。编译器会自己计算,并在我更改字符串长度时自动重新计算。

顺便说一下,这是一个简单的strlen()实现:

size_t strlen(const char* p)
{
    size_t result = 0;
    while(*p++) ++result;
    return result;
}

现代实现可能不会获取单个字节甚至使用CPU内部函数,但这是基本算法。


哇,我在加我的之前没看到你的strlen()。它们完全一样!思想相同啊...8v) - Fred Larson
GNU libc 字符串函数的源代码非常值得探究,而且令人惊讶地复杂。 - James Morris
@Fred:这是C库中最基本的算法之一,也是K&R的一个典型例子。难怪我们大多数人都将其铭刻在记忆中。 - sbi

3
以下不是以 null 结尾的字符数组:
 char y[4] = {'t','e','s','t'};
strlen()函数的约定之一是要提供一个指向以空字符结尾的字符串的指针。由于strlen(y)没有满足这个条件,所以你会得到未定义的行为。在你的特定情况中,返回值是6,但任何事情都可能发生,包括程序崩溃。
从C99的7.1.1“术语定义”中可以看出:

一个字符串是由连续字符序列组成的,以包含第一个空字符为止。


3

strlen 适用于 字符串。 字符串被定义为以 \0 字符结尾的字符序列(数组)。

您的 x 指向一个字符串。 因此,strlenx 作为参数可以正常工作。

您的 y 不是一个字符串。 因此,将 y 传递给 strlen 会导致未定义的行为。 结果毫无意义且不可预测。


2
你需要在y的末尾添加一个空字符来进行终止。
int get_len(char *string){

    printf("len: %lu\n", strlen(string));

    return 0;
}

int main(){

    char *x = "test";
    char y[5] = {'t','e','s','t','\0'};

    get_len(x); // len: 4
    get_len(y); // len: 4

    return 0;
}

strlen()基本上接受您提供的指针并计算直到内存中下一个NULL之间的字节数。恰好在您的内存中两个字节后有一个NULL。


那么,strlen只是将指针转发,直到它在任何地方找到一些\0吗? - LearningC
有两个问题。首先,NULL是指空指针,而不是空字符。其次,你仍然有char y[4],所以你有一个额外的初始化器。你需要五个位置才能有一个“test”字符串。 - David Thornley
抱歉,我忘记递增索引了,但我不知道NULL的事情。每天都会学到新东西。 - Cory Walker

1

一个实际的C类型字符串比它的字符数多一个,因为它需要一个终止空字符。

因此,char y[4] = {'t','e','s','t'};不形成一个字符串,因为它有四个字符。char y[] = "test";char y[5] = "test";将形成一个字符串,因为它们将具有一个由五个字符组成的字符数组,以空字节终止。


0
char y[5] = {'t','e','s','t','\0'};

会和

char *x = "test"; 

1
对于 strlen() 函数来说,是的。但是,y 是一个包含五个 char 的数组,其内容可以随意修改。x 是一个指向 char 的指针,在这种情况下指向一个不能可靠地被修改的字符串。另一方面,可以重新分配一个值给 x,但不能给 y - David Thornley
@David Thornley 这个答案是在初学者的背景下给出的,只是为了说明根据 OP 的描述出了什么问题。现在已经有更好的答案了。没有必要点踩。 - stacker
IME确切的差异就像这个(x[0]='x';在你说char x[]="X";时没问题,但在你说char* x="X";时致命)一样微妙,尤其是对于初学者来说,这种差异永远不可能强调得太多。 - sbi

0
正如其他人所说,您只需要确保以0或'\0'字符结束字符串。附带说明一下,您可以查看此内容:http://bstring.sourceforge.net/。它具有O(1)字符串长度函数,而C/C++ strlen具有错误率高且速度缓慢的O(N),其中N是非空字符数。我不记得上次使用strlen及其相关函数是什么时候了。选择安全快速的函数/类吧!

0

当您使用单引号时,请始终使用'/0',但在双引号中避免在strlen()中使用'/0'

请注意,strlen()函数在计算长度时不会计算空字符\0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接