C专家编程精华第二篇----C对内存的使用,底层探索
这里以Linux中C编程为例,有些东西可能在不同的系统中处理是不一样的
/**************运行时:内存的布局****************/
以下面这段程序为例:
栈段是一般是由内存高地址向底地址生长,首先main的过程活动记录入栈,然后调用Recall函数,Recall的过程活动记录入栈,反复,知道printf函数执行了,再由printf函数返回,挨着返回,挨着退栈,知道main结束,这是整个递归函数的调用过程。
一般来说,对于非递归的简单函数,是不会让它入栈的,因为编译器会考虑到效率问题,从上面的递归函数调用原理来看,每一个Recall函数的参数、局部变量、前一个活动的指针和返回地址都要入栈,这大大拖慢了程序的速度,虽然经典的栈模式可以很好的解决递归问题,但是先入栈、最后等都执行完了再出栈,这种方法肯定慢了很多,效率不高,所以一般就不要用递归函数解决方法,即使它真的很利于大家理解。
从上面对递归函数调用原理的解说,我们可以更深刻的理解这么一个问题:不能从函数中返回一个指向该函数内部局部变量的指针。
当这个函数结束的时候,函数的过程活动记录退栈,内存被回收,其他的函数可能入栈覆盖掉了该函数,那么里面记录的所有值都是不确定的了,接着指针就成了野指针,这可是相当危险的事情。
但是我们仍然可以从函数中返回一个如我们希望的指针,看看最开始的C源文件在可执行文件的布局,数据段的生存周期是和程序一样的,它包括全局变量和静态变量,这也就是说我们把函数中的指针定义为静态变量就可以将这个指针轻松的返回了,因为它不会被回收,在程序结束之前。
/***********内存使用************/
(1)查看程序能被分配的最大内存
如果将name大小改为25,有4*8=32字节容纳它,多了7个字节,能够容纳number,那么就相当于多余了7-4=3字节了,显然id不能放到3字节里,开辟8字节容纳id,这样sizeof之后仍未5*8=40字节
为了更好的节省内存空间,我们需要注意类似的变量定义的先后顺序,将number和name的位置交换结果又不一样,因为指令是逐条执行的。
(3)段错误
(还有一种总线错误,真心还没弄懂,求高手指点)
段错误是由于内存管理单元异常所致,导致段错误有以下几个原因:
1、解引用一个包含非法值的指针,也就是对一个已经free掉的内存继续使用
2、解引用一个空指针,一般就是初始化为NULL了还对它进行解引用,还可能是从函数中返回了NULL却没有进行检查就直接使用
3、对超过了申请内存边界的内存进行解引用,有点像数组的越界一样
4、把你的电脑可用动态内存用完了。。。。这个几乎很困难