CODE:
#include<iostream>
#include<stdio.h>
int main()
{
char ch[10];
std::cout<<"\n\n\n\n\n\n\n";
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<<"\n\n\n\n\n\n\n";
return 0;
}
OUTPUT: 1. On giving input within defined range(i.e less than 10) Enter the string: 12345678 8 12345678 sizeof ch10 8 8 second last=8 last=0 2. On giving input beyond defined range Enter the string: 12345678901234 14 12345678901234 sizeof ch10 13 14 second last= last=0 Enter the string: 123456789012345678 18 123456789012345678 sizeof ch10 13 18 second last=8 last=0
我知道不应该使用获取但仍然想知道里面发生了什么,为什么第三行的输出会给出13?
答案 0 :(得分:5)
唯一真正的答案是未定义的行为。一旦你访问超出数组末尾的内存,如果输入太大就会gets
,那么任何事情都可能发生。
如果我不得不猜测:最可能的解释是编译器在len
之后将ch
放在内存中。因此,分配给len
会覆盖从数组末尾溢出的一些输入。该值的一些字节将为零(因为它是一个小数字),因此下一次调用strlen
将在找到其中一个字节时停止,给出的值比以前小。
在分配给len
之前和之后,内存布局可能如下所示。我假设是ASCII编码,因此'0'
为48,'1'
为49,等等。我假设int
有四个字节,以“little-endian”顺序排列内存中最不重要的第一个,需要在四字节边界上对齐,需要两个填充字节才能在数组之后存储它。
| 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
可以完全避免这种惨败。