strlen()的奇怪行为,当在代码中的不同位置使用时,会给出不同的输出

Strange behavior of strlen(), giving different output when used at different place in code

本文关键字:位置 输出 代码 strlen      更新时间:2023-10-16

代码:

#include<iostream>
#include<stdio.h>
int main()
{
        char ch[10];
        std::cout<<"nnnnnnn";
        std::cout<<"Enter the string: ";
        gets(ch);
        std::cout<<strlen(ch)<<"n";
        std::cout<<ch<<"n";
        std::cout<<"sizeof ch"<<sizeof(ch)<<"n";
        int len=strlen(ch);
        std::cout<<strlen(ch)<<"n";
        std::cout<<len<<"n";
        std::cout<<"second last="<<ch[len-1]<<" last="<<(int)ch[len]<<"n";
        std::cout<<"nnnnnnn";
        return 0;
}
输出:1.在规定范围内输入(即小于10)输入字符串:123456788.12345678ch10的尺寸8.8.倒数第二=倒数第8=倒数第02.在给出超出定义范围的输入时输入字符串:123456789012341412345678901234ch10的尺寸1314倒数第二=倒数第0输入字符串:12345678901234567818123456789012345678ch10的尺寸1318倒数第二=倒数第8=倒数第0

我知道不应该使用gets,但我仍然想知道里面发生了什么,为什么最后一行输出是13??

唯一真正的答案是未定义的行为。一旦访问数组末尾以外的内存,就像gets在输入过大时所做的那样,任何事情都可能发生。

如果非要我猜测的话:最可能的解释是编译器将len放在内存中的ch之后。因此,分配给len将覆盖从数组末尾溢出的一些输入。该值的一些字节将为零(因为它是一个小数字),因此当它找到其中一个字节时,对strlen的下一次调用将停止,从而给出比以前更小的值。

在分配给len之前和之后,内存布局可能是这样的。我假设ASCII编码,所以'0'是48,'1'是49,等等。我假设int有四个字节,以"小端序"顺序排列,内存中的最低有效位在第一位,并且需要在四个字节的边界上对齐,需要两个填充字节将其存储在数组之后。

| ch, 10 bytes                  | pad   | len, 4 bytes| other    |
| 49 50 51 52 53 54 55 56 57 48 | 49 50 | 51 52 53 54 | 55 56 00 | before
| 49 50 51 52 53 54 55 56 57 48 | 49 50 | 18 00 00 00 | 55 56 00 | after

您可以看到,在这种情况下第二次调用strlen将在零值字节被解释为字符串末尾之前找到13个字符。这符合你的观察结果。

正如您所说,永远不要使用gets,因为没有办法避免甚至可靠地检测缓冲区溢出。对于固定大小的数组要非常小心,并且更喜欢更友好的C++习惯用法,而不是C风格的内存杂耍。使用std::string可以完全避免这种惨败。