您的位置:首页 > 编程语言 > C语言/C++

C语言内存管理基础知识总结

2013-09-19 14:37 113 查看
《一》:内存的分配

内存分配一般是有五种方式:静态存储区域分配、堆区、栈区、常量区、代码区;

静态存储区域一般对应的是整个程序运行期间都一直存在,内存在编译的时候就已经分配好了,常见的全局变量,静态变量(static)。

堆区也就是动态内存分配,一般是通过程序员通过(malloc/new )进行分配使用之后需要程序员自己利用(free/delete)进行资源释放,使用灵活、但也容易造成内存泄露、段错误等问题。

(注意:这个地方的堆和数据结构的堆不是一个概念,一定要注意区分)

栈区栈上创建(FILO)局部变量,函数参数等都是定义在栈区,由系统统一分配和回收使用方便,还有就是和数据结构中的栈相同的性质,用于函数调用的现场保护和复原。

常量区 存储的都是一些字符串常量,有系统自行分配和回收。

《二》:内存操作的常见错误分析

常见的内存错误编译器不会报错,只有在程序执行的过程中才会出现异常,有时候能难捕捉,所以在使用内存的时候一定要注意一下几个方面:

第一:动态分配内存的时候一定要注意大小和检验是否分配成功并进行防错处理;

第二:内存分配成功之后的初始化;这种错误在初学者经常会犯,一点是动态非配内存之后没有初始化的概念,另外一点就是误以为系统会将分配的内存置为0.(有些时候确实默认值为0)但是在我们写程序的时候一定要有初始化这个意识;

第三:内存操作越界,这就回到第一点当中动态分配内存的时候一定要注意申请内存空间的大小。

第四:动态申请内存使用完了之后一定要注意释放,malloc/new和free/delete一定要成对出现就像你匹配括号一样;释放之后还要注意将这个指针指向NULL,否则可能出现“野指针”;

第五:释放之后的内存继续使用,造成这个问题主要是因为有些内存是系统释放的比如说“栈内存” 、“指针” 、“引用”等这些内存在函数体结束的时候就自动释放了,这个时候再用return语句返回并继续时候的时候就容易出现这种错误;

《三》:指针和数组

在实际变成中我们需要利用数组和大量的指针操作,很多时候我们会发现好像指针跟数组在使用的时候没什么区别,很多地方使用指针和数据都可以达到我们操作数据的目的,但是原理上他们是有区别的。

指针和数组的对比:

1.数组定义在栈区或者是静态存储区(全局数组或static类型的),数组名对应的是一块内存,地址和容量在它的生命期内是不会改变的,引用数据的时候可以直接应用,并且可以对内容直接修改重新赋值;例如

char *ptr = “hello world!”;(在ANSI C中初始化指针所创建的字符串常量被定义为只读,“hello world!”是定义在常量区的,是不能被修改的)

char arr[] = “hello world!”;(这个是定义在栈区的)

在输出、引用、显示的时候二者是没有区别的;

使用的时候首先找到ptr指向的内存中的内容,然后再把内存中的内容作为地址,之后在从这个地址+偏移量 提取数据;

2.在The C Programming Language,第二版,Kernighan&Ritchie一书中提到了在作为函数定义的形式参数的时候数组下标表达式可以改写为带偏移量的指针表达式,即是

char arr[] = “asdfasdf”;

function(char * ptr);

在function函数调用的时候可以直接写成function(arr);

关于数组和指针什么时候相同C语言标准对此作了说明:

规则1:表达式中的数组名被编译器当做一个指向该数组第一个元素的指针;

规则2:数组的下表总是和指针的偏移量相同;

规则3:函数参数的声明中,数组名被编译器当做指向该数组的第一个元素的指针;

3.指针参数的传递

这个问题也是在函数编写的过程中经常会遇到的一个问题,有时候不仔细的想明白很容易出错而且不容易调试

Void Getmemory(char *p, int num)

{

P = (char *)malloc(sizeof(cahr ) * num);

}

Void main()

{

Cahr * ptr = NULL;

GetMemory(ptr, 100);

}

在试图用这样的方式分配内存的时候,涉及到指针参数的传递编译器总是会为每一个传递过来的参数***一个临时副本 ptr的副本是_p编译器在编译程序的时候会使_p = ptr如果函数执行过程中修改了_p的值 对应的ptr的值就会被相应的修改 这就是指针可以作为输出参数的原因 所以 在本例中只是修改了_p的指向的内存 修改了 并且每调用一次 都会分配内存 没有释放 所以很容易造成内存泄露

(如果确实需要使用指针来分配内存 可以采用指向指针的指针)

Void Getmemory(char **p, int num)

{

*P = (char *)malloc(sizeof(cahr ) * num);

}

Void main()

{

Cahr * ptr = NULL;

GetMemory(&ptr, 100);

}

(同时也可以使用返回指针来实现)

Char *Getmemory( int num)

{

Char *P = (char *)malloc(sizeof(cahr ) * num);

Return *p;

}

Void main()

{

Cahr * ptr = NULL;

Ptr = GetMemory(&ptr, 100);

}

(用函数返回值来传递动态内存的时候 注意不能返回栈内存指针因为栈区在函数调用之后就被自动释放了)

这些是自己在看内存管理、指针和数组这个模块的时候的一点个人总结;

附1:数据结构中的堆

堆可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示(普通的一般的二叉树通常用链表作为基本容器表示),每一个结点对应数组中的一个元素。二叉堆一般有两种形式:最大堆个最小堆,比如最大堆的特点是,每个父节点的元素值都不小于其孩子结点(如果存在)的元素值,因此,最大堆的最大元素值出现在根结点(堆顶)最小堆的性质与最大堆恰好相反.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: