又想起了malloc 与 free
malloc 负责申请内存,free 负责释放。 malloc 申请多少, free 就释放多少,不管该内存片里是什么数据。
举个例子
先申请了100MB的空间,指针 p 指向该内存段的头地址
void *p = malloc(1024 * 1024 * 100);
申请100MB的空间,指针 p 指向该内存段的头地址,P 是一个指向整形的指针
int *p = (int*)malloc(1024 * 1024 * 100);
无论 p 是什么类型的指针,执行 free(p); 的时候都将释放 100 MB 的内存。
在释放该段内存之前再定义一个指针 pp ,指向该内存段的第 sizeof(int) 个字节
void *pp = (int*)p + 1;
向该指针拷贝一块内存,并以 INT 的形式输出
int i = 100;
memcpy(pp, &i, sizeof(int));
cout << "*pp = " << *((int*)(pp)) << endl;
一切都很正常,没有问题。如果我们改成这样
int i = 100;
memcpy(pp, &i, sizeof(int));
cout << "*pp = " << *((int*)(pp)) << endl;
free(p);
cout << "*pp = " << *((int*)(pp)) << endl;
在准备执行最后一句的时候,这100MB的内存已经被释放掉了。此时执行最后一句会出现什么样的情况呢?
在 windows 下用 VC2005 编译后运行,系统提示错误,告诉我们访问内存出错。
在 SunOS 5.8 下,用 gcc version 3.4.2 编译后运行,一切正常。而且 pp 指向的数据也没有变化。
于是我想:
一种可能是 VC 在 free 的时候将该内存段全部清零或初始化,而在 GCC 下只是将该段内存的占用权
释放掉了,对该段内存数据没有做什么修改。
另一种可能是 VC 和 GCC 都没有修改该段内存,只是 VC 在访问该段内存时要先做占有权的判断,如果没有
占有权则认为读写操作是非法的。而占有权并不是给特定的指针,只要编译器 MALLOC 过了就算有占有权了。
再看一例代码:
定义一个结构体
typedef struct AA
{
int _i;
char _c;
double _d;
} AAA;
cout << "sizeof(AAA) = " << sizeof(AAA) << endl; //长度是 16
AAA aaa;
aaa._i = 10;
aaa._c = 'c';
aaa._d = 1.5;
开始我们的测试
void* p = malloc(1);
memcpy(p, &aaa, sizeof(AAA)); // 只申请一个字节,但是给它拷贝一个 AAA 结构体过去
cout << "((AAA*)p)->_i = " << ((AAA*)p)->_i << endl;
cout << "((AAA*)p)->_c = " << ((AAA*)p)->_c << endl;
cout << "((AAA*)p)->_d = " << ((AAA*)p)->_d << endl;
cout << "sizeof(*((AAA*)p)) = " << sizeof(*((AAA*)p)) << endl;
一直到此处都很“正常”,sizeof(*((AAA*)p)) = 16。
如果我们此时 free(p); 在 VC 下会有问题,是内存堆栈被破坏,但在 GCC 下就没有问题。
换个花样来看看
AAA *pa = (AAA*)malloc(1);
pa->_i = aaa._i;
pa->_c = aaa._c;
pa->_d = aaa._d;
cout << "pa->_i = " << pa->_i << endl;
cout << "pa->_c = " << pa->_c << endl;
cout << "pa->_d = " << pa->_d << endl;
cout << "sizeof(*pa) = " << sizeof(*pa) << endl;
一直到此处都很“正常”,sizeof(*pa) = 16。
如果我们此时 free(pa); 在 VC 下会有问题,还是内存堆栈被破坏,但在 GCC 下就没有问题。
还是最上面那句话:malloc 申请多少, free 就释放多少,不管该内存片里是什么数据。但是 VC(也可能是 WINDOWS) 就会做检查,
当指针的类型大小小于等于 malloc 的数量的时候则忽略过去,反之则报错。这些都是内存越界造成的,windows会出于安全的
角度去考虑这个问题,但是用 GCC 编译出来的程序在 SUN OS 5.9 上则不会进行这样的检测,虽然提高了效率但是不安全。
因为对于越界的这部分内存中的内容将会是不确定的。
我在想,这个检测是 GCC(打开了警告参数也没有警告) 的机制还是SUN OS 5.9的?
还可以联想另外一个问题,编译器中的数据类型只是个描述,给编译器处理(寻找)数据的时候用的,无论你将
数据类转换来转换去,都不会对数据的实体进行变更,数据还是那个数据,只是有可能后面追加了一些东西或是
“切”掉了一部分。此处的“切”并不是真正把数据抹去了,而是编译器根据新的数据类型去寻址就有可能有一些数据看不到了。
就像将 DOUBLE 转换成 INT ,内存中的数据没有变,只是编译器只能看到前面4个字节。