整理C/C++内存管理的一些常见问题

首先有一道程序如下:

void getmemmory(char *p)
{
    p = (char *)malloc(100);
}
void main()
{
    char *str = NULL;
    getmemory(str);
    strcpy(str, "Hello World");
    printf(str);
}

当我们运行该程序时,程序会直接崩溃。
这是一道很经典的错误程序,有很大的借鉴意义。错误很明显,在malloc那里,不过我刚开是看的时候还是有没想通的地方,后来仔细分析一下,才搞明白了。
首先在main函数里面申明了一个字符指针类型的str,指向NULL,现在我们假设str的值是a1,a1这个地址那里的值是NULL。
之后调用getmemory,将str传给p,这样在刚进入getmemory函数时,p的值就是a1,而*p就是NULL。
之后我们对p进行malloc,malloc函数是这样运作的:

void *malloc(long NumBytes)

该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。
所以说malloc分配成功后返回的是指向分配的那快内存的地址,那么假设malloc分配了一块内存空间M,M的地址是a2,那么a2就是malloc的返回值,此时执行:

p = (char *)malloc(100);

p的值就变成了a2,此时str还是a1,这里仅仅是对p重新赋值了,对str没任何影响。
最后getmemory执行完,str依然是原来的str,strcpy这个函数就出错了,因为它没法把”HEllo World”这个字符串放到str那里。
同时,这里还有另外的问题——内存泄露,而内存泄露就发生在p处。
这里需要一小点函数调用和内存分配的相关知识,在函数中申明局部变量或者参数传递,都是在该线程的栈中进行,而malloc中申请的存储空间是由os在堆中分配而来的。函数返回的时候,栈中的东西会被释放。
现在回到这个问题,在getmemory中,p最后的值是a2(在栈中),指向的地址空间是M(在堆中),此时函数getmemory一声不吭的返回了,p就消失了,a2就找不到了,而a2是程序唯一可以找到M的途径。此时尽管a2没了,但是M并未被释放,所以M成了被泄露的内存。

至此,这个程序基本就分析完全了,下面说说对malloc和free的一些新的理解。
malloc和free一定要成对出现,否则必造成内存泄露。实际情况下,我们的程序如果很小,其实就算不free,程序结束后,OS一般情况下也会想办法处理我们泄露的内存。但是如果是server级别的程序,不停的跑的,那问题就挺大的了。
之前我还遇到过一个问题,就是我malloc申请了一个空间给p,然后我重复free(p),这样就会出错(p==NULL除外)。重复free也是会出问题的。第一次free的时候,实际上是告诉os,p指向的内存我不要了,可以收走了,但是此时虽然p指向的地方已经不属于我了,但是p还是有一个值的,再次free说通俗点就是:这个东西的不是我的,但是这个东西没用的了,os你收销毁吧。这就坑爹了,不是你的东西你怎么知道它没用了。所以好习惯就是每次free(p)后都调用p=NULL将p置空。
重复free其实挺常见的,我们容易这么写代码(我就这么干过)。

p=malloc(100);
//...一堆和p相关的操作...
q=p;//这里想换个名字方便用
//...一堆和q相关的操作...
free(p);
//...
free(q);//重复free

正确的malloc和free使用逻辑应该是,malloc时用if判断是否分配成功,最后匹配唯一的free,然后把指针置空。

还有一些别的内存相关的代码:

char *GetMemory(void)
{
    char p[]=”helloworld”;
    return p;
}
void main()
{
    char *str = NULL;
    str = GetMemory();
    printf(str);
}

这段代码最后输出结果是乱码。

char *GetMemory(void)
{
    char *p=”helloworld”;
    return p;
}
void main()
{
    char *str = NULL;
    str = GetMemory();
    printf(str);
}

这段代码最后输出木有问题。(和上面一比涉及到常量区,堆栈什么的,具体分析可以参考深入分析C++中char 和char []的区别,这篇写的很明白,表示感谢)

void GetMemory2(char **p, int num)
{
    *p = (char*)malloc(num);
}
void main()
{
    char *str = NULL;
    GetMemory(&str, 100);
    strcpy(str, “hello”);
    printf(str);
    free(str);
}

这段会成功输出”Hello”。

关于上面三题的前两道又有新的理解~
先上程序:

#include<stdio.h>
#include<string.h>
void main()
{
	char *c1 = "abc";
	char c2[] = "abc";
	char *c3 = (char *)malloc(4);
	c3 = "abc";
	char *c4 = (char *)malloc(4);
	strcpy(c4, "abc");
	printf("1. %p %p %s\n", &c1, c1, c1);
	printf("2. %p %p %s\n", &c2, c2, c2);
	printf("3. %p %p %s\n", &c3, c3, c3);
	printf("4. %p %p %s\n", &c4, c4, c4);
	getchar();
}

输出:
1. 0xbfd2affc 0x8048670 abc
2. 0xbfd2b008 0xbfd2b008 abc
3. 0xbfd2b000 0x8048670 abc
4. 0xbfd2b004 0x9d91018 abc
c1在栈上,”abc”在常量区
c2中abc都在栈上
c3在栈上,开了堆内存,最后指向了常量区的”abc”,内存泄露
c4在栈上,”abc”在开给c4的堆上了

——
致谢以下参考文章:
http://blogread.cn/it/article/1266?f=wb
https://blog.delphij.net/2009/10/free.html
http://www.nohouse.net/index.php/2010/10/22/re_free_address_error/index.html
http://www.cnblogs.com/skynet/archive/2011/02/20/1959162.html
http://www.bccn.net/Article/kfyy/cyy/jszl/200608/4238.html

Tags :

0 thoughts on “整理C/C++内存管理的一些常见问题”

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Click the right image To submit your comment: