cckj 2008-12-10 16:01
简单实例让你理解C语言的内存分配次序
复制内容到剪贴板
代码:
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char* argv[])
{
char str[5];
char str1[5];
bzero(str, 5);
bzero(str1, 5);
strcpy(str, "abcdef");
printf("str = %s\nstr1 = %s\n", str, str1);
exit(0);
}
先看以上代码
按照我们教科书里教的来分析,程序运行的时候肯定会有断错误。
因为str就分配了5个字节,而strcpy的时候给了7个字节的字符串。
但事实呢?
答案是
str = abcdef
str1 =
再看
复制内容到剪贴板
代码:
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char* argv[])
{
char str1[5];
char str[5];
bzero(str, 5);
bzero(str1, 5);
strcpy(str, "abcdef");
printf("str = %s\nstr1 = %s\n", str, str1);
exit(0);
}
我们把 char str1[5]; 放在了前面
输出结果是什么?
答案是
str = abcdef
str1 = f
别看这一个简单的例子,这里涉及到了C语言的内存分配问题,教科书里总习惯把堆栈连在一起叫,其实堆和栈是不一样的。
有何感想?
cckj 2008-12-10 16:02
comment :[img]http://www.lupaworld.com/bbs/images/smilies/default/smile.gif[/img]
实际上,这个这个 'f' 字符并没有分配给str 或者str1
通过debug工具是可以观察到的,内存中只有前5个字符
通常分配5个空间的字符串,默认是6个,隐含一个给'\0',
实际上,你给很多个字符,比如strcpy(str, "abcdefghia c");
照样显示,但是实际上,都没有给拷贝过去,真正
拷贝过去,只有前面5个字符,这个对于开发嵌入式产品,是非常严格要求对待的.
附件
[img]http://www.lupaworld.com/bbs/images/attachicons/image.gif[/img]debug.JPG (80.33 KB)
2008-11-1 11:10
debug
[img]http://www.lupaworld.com/bbs/attachment.php?aid=7787&k=855559b9197dc4593b905c4e99ffc2d4&t=1228894403&noupdate=yes[/img]
cckj 2008-12-10 16:02
原理
通常分配5个空间的字符串,默认是6个,隐含一个给'\0',
但是现在多拷贝的字符挤占了这个内存空间,因此实际上就没有结束标志了 当打印时候
打印收到的指令成为 指向 名为str的字符串指针,但是没有结束符,因为原有的结束符被挤了.
这个现象在嵌入式开发中是危险的,容易发生系统崩溃.
cckj 2008-12-10 16:02
忘记告诉楼主一声
其实,楼主,你的系统该升级了
因为, 其实这个其实是一个系统bug
如果你是在windows 系统上编译的话,那无所谓
如果是在linux系统上编译的话,那么你的系统应该是在federal core3
左右,
具体的bug 原理是这样,黑客,通过挤占'\0'把他的代码上传到到服务器,看上去只
拷贝5个字节,实际指向后面十几K
在Federal 4以后的内核中已经修复了这个bug ,再编译不会出现这样的错误了
其实这样的问题不应该让用户来考虑,是系统问题.
cckj 2008-12-10 16:02
呵呵 分析的很全面了
不过 我认为 这不是系统bug 而是C语言的特性 能让用户直接深入内部 进行各种特殊操作
我的系统是ubuntu 8.04 kernel 2.6.24 gcc 4.2.4
关于内存分配,我们以
复制内容到剪贴板
代码:
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char* argv[])
{
char str1[5];
char str[5];
bzero(str, 5);
bzero(str1, 5);
strcpy(str, "abcdef");
printf("str = %s\nstr1 = %s\n", str, str1);
exit(0);
}
为例
看main函数的汇编代码
截取了main函数部分
复制内容到剪贴板
代码:
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
movl $5, 4(%esp)
leal -14(%ebp), %eax
movl %eax, (%esp)
call bzero
movl $5, 4(%esp)
leal -9(%ebp), %eax
movl %eax, (%esp)
call bzero
movl $7, 8(%esp)
movl $.LC0, 4(%esp)
leal -14(%ebp), %eax
movl %eax, (%esp)
call memcpy
leal -9(%ebp), %eax
movl %eax, 8(%esp)
leal -14(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC1, (%esp)
call printf
movl $0, (%esp)
call exit
再用gdb跟踪
str1的内存地址是
(gdb) p &str1
$6 = (char (*)[5]) 0xbfc1b90f
str的内存地址
(gdb) p &str
$5 = (char (*)[5]) 0xbfc1b90a
可以看出str的内存地址在str1前面
而strcpy的操作则从 0xbfc1b90a 开始往后, 他不会去考虑str所分配的长度, 他只是把需要拷贝的字符串一个个的往里放进去
这就是出现的内存溢出,如果遇到函数调用的时候,就出现段错误,但我们现在这里只是变量赋值,所以程序会正常运行。无非是改变了排在后面的str1的内容而已。
(gdb) p str
$3 = "abcde"
(gdb) p str1
$4 = "f\000\000\000"
莫非windows下已经消除了这个bug ???