Linux C 内存管理-实例分析
Linux C 内存管理其实可以总结成一句话:管理你该管理的。即由系统静态分配的内存不要去释放,系统会在变量生命期结束时自动释放内存,而自己动态分配的内存则需要主动释放,同时要注意,同一块内存不要释放两次,更多具体关于Linux 内存管理的介绍可参考Linux内存管理以及段错误(核心已转储)。接下来通过一个实例来讲述下内存管理的过程。
给定了一个字符串指针,并为其分配了一定的地址空间,如下所示:
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main (){
char* testData = (char*)malloc(5120);
//....
if(testData != NULL){
free(tesData);
testData = NULL;
}
return 0;
}
接下来需要针对此字符串指针(地址)完成一些操作:
一、为其赋值并拷贝至其它字符串中:
我们一般可能会这样做:
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main (){
char* testData = (char*)malloc(5120);
memcpy(testData, "Hello World", sizeof("Hello World")); //这里注意,虽然字符串长度为11,但实际上向testData拷贝了12个字节的数据,其中最后一个数据为'\0'终止符,在进行字符串拷贝时需要注意。
char *tarData = (char*)malloc(1024);
tarData = testData;
printf("%s\n", tarData);
if(tarData != NULL){
free(tarData);
tarData = NULL;
}
if(testData != NULL){
free(tesData);
testData = NULL;
}
return 0;
}
//double free or corruption (out):
以上代码虽然通过了编译,但是运行时会发现提示double free or corruption (out)
,这种提示便是我们对同一块地址空间释放了两次或者数据越界,但上面那种情况便是前者了。接下来分析下为什么会这样,在定义tarData时,我们为其动态分配了一块空间,而后将tarData指针指向了testData的地址,没错,此时tarData所指向的空间已不再是我们为其分配的空间了,如此一来,不管有没有输出成功,在我们想释放为tarData分配的空间时,即free(tarData)
(注意这里主动将tarData=NULL;的目的是防止通过tarData指针操作了不该操作的空间)后,其实释放的是一开始为testData所分配的空间,这样一来,后面free(tesData);
再次对testData指向的空间进行释放,自然就报了上面的错误,当然还有小伙伴在为tarData分配了空间后,并未对其进行释放,这样虽然不会报错,但是只申请空间却不释放,不是个好习惯,甚至还会报出头疼的段错误。正确做法应该是,命名一个字符串指针直接指向testData所指向的地址,即:
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main (){
char* testData = (char*)malloc(5120);
memcpy(testData, "Hello World", sizeof("Hello World")); //这里注意,虽然字符串长度为11,但实际上向testData拷贝了12个字节的数据,其中最后一个数据为'\0'终止符,在进行字符串拷贝时需要注意。
char *tarData = testData ;
printf("%s\n", tarData);
if(testData != NULL){
free(tesData);
testData = NULL;
}
return 0;
}
//out:Hello World
二、将其截取分析后并拷贝至其它字符串中或对其进行其它操作
比如一开始我是这么写的:
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
void encode(char *buf)
{
char *header = (char*)malloc(1024);
header = buf;
if (header[0] & 0x80 == 0)
{
//....
}
if(header != NULL)
{
free(header);
header = NULL;
}
}
int main (){
char* testData = (char*)malloc(5120);
memcpy(testData, "Hello World", sizeof("Hello World")); //这里注意,虽然字符串长度为11,但实际上向testData拷贝了12个字节的数据,其中最后一个数据为'\0'终止符,在进行字符串拷贝时需要注意。
encode(testData);
char *tarData = testData ;
printf("%s\n", tarData);
if(testData != NULL){
free(tesData);
testData = NULL;
}
return 0;
}
//segmentation fault
于是我很正常的领到了“段错误”的error,其实和示例一一样,在encode()函数中,随手写的释放空间,其实释放的是为testData分配的空间(指针作为函数参数入参时,传的是地址),这样在经过encode()函数后,testData指向的空间已被释放,此时testData所指向的内存地址便不存在了,再将tarData指向至此时,就产生了试图访问一个不存在的内存地址的问题,于是很自然的报了段错误,正确做法应该是像示例一一样定义后直接指向testData所指向的地址,或者将testData指向地址内的值拷贝至header中,而后再单独对header进行释放,即:
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
void encode(char *buf)
{
char *header = (char*)malloc(1024);
memcpy(header, buf, 1024);
//char *header = buf; //示例一的做法
if (header[0] & 0x80 == 0)
{
//....
}
if(header != NULL) //将值拷贝到单独分配的地址中,之后便可正常释放空间
{
free(header);
header = NULL;
}
}
int main (){
char* testData = (char*)malloc(5120);
memcpy(testData, "Hello World", sizeof("Hello World")); //这里注意,虽然字符串长度为11,但实际上向testData拷贝了12个字节的数据,其中最后一个数据为'\0'终止符,在进行字符串拷贝时需要注意。
encode(testData);
char *tarData = testData ;
printf("%s\n", tarData);
if(testData != NULL){
free(tesData);
testData = NULL;
}
return 0;
}
//out:Hello World