首先,这是一个非常有趣的问题!
我也是最近看了一本《C++高效编程:内存与性能优化》的书才弄明白其中的奥秘!
我们先来看一个“奇怪”的现象:
CODE:
struct A { char a; long b; char c; long d; };
按照常理,输出都应该是 10 ,其实实际运行结果是16,12 !是不是很奇怪???
struct B { char a; char c; long b; long d; };
cout << sizeof(A) <<endl;
cout << sizeof(B) <<endl;
为什么会出现这个现象呢?因为编译器会强制char类型的变量与long类型的变量在内存中对齐!
举例看结构A在内存中的布局如下:
地址 内 容
00 字符a
01 未用
02 未用
03 未用
04 long b, byte 0
05 long b, byte 1
06 long b, byte 2
07 long b, byte 3
08 字符c
09 未用
......
可以看到其中的原因了吧!
再看看LZ的程序的原因。
C++的系统函数strcat()的功能只是简单的拼接字符串,自身没有检测是否越界的功能!所以,当拼接后的新的字符串长度大于原始的空间时,strcat()会修改邻接的内存空间!所调用strlen()所取得的长度是截止到 '/0' 的长度!但是调用sizeof()会发现其空间大小还是10!
按照上面的说法,字符数组a后面的空间应该被修改,所以b的内容会变化,但是C++编译器分配内存空间是按照倒栈的顺序的,如果把b放到a前面定义,才有可能被修改!但是由于开始说的内存对齐问题的存在,因此字符数组b的内容还是没有被改变。
大家可以看看下面的程序,分别输出a,b的地址。
CODE:
int main(){
运行结果为:
char b[10] = "good";
char a[10] = "java is";
strcat(a," ");
strcat(a,b);
cout << int(&b) << endl;
cout << int(&a) << endl;
return 0;
}
2293600
2293584
(该运行结果取决于不同的机器与编译器,但是反映的问题还是很明显的!)
a,b的头地址相距16Byte,而a的默认空间只有10Byte,还有6Byte的空间是什么呢?
当执行strcat()时,越界的部分就覆盖了这6Byte的内容,但是还没有影响到b的内容,为了证明strcat()确实存在越界的问题,我们可以构造一个特例,程序如下:
CODE:
int main(){
我们把b放得足够大,然后调用strcat(),使得拼接后的字符串的长度大于16,这样的话,不仅仅是a,b之间的6Byte被覆盖,b的内容也被覆盖了!
char b[30] = "good, but i don't think so!";
char a[10] = "Java is";
strcat(a," ");
strcat(a,b);
cout<<a<<endl;
cout<<b<<endl;
cout << int(&b) << endl;
cout << int(&a) << endl;
return 0;
}
程序运行结果如下:
Java is good, but i don't think so!
t i'dont think so!
可以看到,越界是存在的!
最后总结一下:
strcat()存在越界的问题,因此我们在使用该函数之前需要确保目标字符数组的空间足够大!在原始的程序中,由于变量较少,运行似乎没错,但是还是有很严重的BUG的!