找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 9095|回复: 1
收起左侧

局部变量内存分配解疑

[复制链接]
ID:3721 发表于 2015-1-5 14:23 | 显示全部楼层 |阅读模式
     最近在做一个疲劳试验系统,一直在程序执行效率上做功夫。也记不清最早是从哪听说的:定义变量,能定义在函数内绝不定义成全局,能定义在循环内绝不定义成函数范围……深以为意!曾经犯过一个错误,在函数体内定义了一个和全局变量同名的变量,但在修改注释的时候不小心将这个局部变量的生命给//掉了……编译无错,执行总是不对,弄了很久才发现错误所在。现在写程序慎之又慎,尽量不使用全局变量,要使用也用规范的变量命名方式将全局与局部区分开。今天读了篇文章,摘录如下:

一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
一个正常的程序在内存中通常分为程序段,数据端和堆栈三部分。程序段里放着程序的机器码和只读数据,这个段通常是只读,对它的写操作是非法的。数据段放的是程序中的静态数据。动态数据则通过堆栈来存放。在内存中,它们的位置如下:
+------------------+ 内存低端
| 程序段 |
|------------------|
| 数据段 |
|------------------|
| 堆  栈  |
+------------------+ 内存高端
堆栈是内存中的一个连续的块。一个叫堆栈指针的寄存器(SP)指向堆栈的栈顶。堆栈的底部是一个固定地址。堆栈有一个特点就是,后进先出。也就是说,后放入的数据第一个取出。它支持两个操作,PUSH和POP。PUSH是将数据放到栈的顶端,POP是将栈顶的数据取出。
在高级语言中,程序函数调用和函数中的临时变量都用到堆栈。为什么呢?因为在调用一个函数时,我们需要对当前的操作进行保护,也为了函数执行后,程序可以正确的找到地方继续执行,所以参数的传递和返回值也用到了堆栈。通常对局部变量的引用是通过给出它们对SP的偏移量来实现的。另外还有一个基址指针(FP,在Intel芯片中是BP),许多编译器实际上是用它来引用本地变量和参数的。通常,参数的相对FP的偏移是正的,局部变量是负的。
当程序中发生函数调用时,计算机做如下操作:首先把参数压入堆栈;然后保存指令寄存器(IP)中的内容,做为返回地址(RET);第三个放入堆栈的是基址寄存器(FP);然后把当前的栈指针(SP)拷贝到FP,做为新的基地址;最后为本地变量留出一定空间,把SP减去适当的数值。
在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的"adgfdf"这样的字符串存放在常量区。
……
    写得我是心里痒痒啊……太好了!以前我有一个错误认识,示例:
void    aaa()
{
    int i;
    for ( i = 0; i < 100; ++ i )
    {
        int j;
        j = i;
    }
}

我总以为调用函数时 i 声明并分配内存一次,而 j 每循环一次分配一次内存。恐怕是和变量作用域混淆了。以前写程序不用太过关心效率,声明在循环里还是循环外都不以为意,执行起来也无甚差别。可现在,循环次数更加巨大,还要求在极短时间内执行完毕……我有点慌咯,我的局部变量假如还不是系统变量类型而是我自己声明的类……那光构造和析构都要人老命了……我到底是不是庸人自扰呢?
写程序试试吧……
.h
class TForm1
{
public:  
    void    aaa();
};

.cpp
void    TForm1::aaa()
{
    int i = 0;
    for ( i; i < 100; ++ i )
    {
        int j;
        j = i;
    }
}

主函数省略,也就是调用aaa()。然后第一次主动打开CPU观察器……居然是第一次,写了快4年程序的第一次!我想被人知道我得被鄙视个半死……幸好以前8086汇编学得还行,单片机也一直坚持用汇编编程,有这些基础,看起来也不是很难:


感谢上面说的那篇“神作”!可爱的 add esp,-0x08 原来一早就分配好区域了……[ ebp-0x04 ]就是i,[ ebp-0x08 ]就是j。感谢伟大的编译器帮我们干完这些……看来我是杞人忧天了。

以前一直以为变量的内存分配就是“new”一块内存……大错特错啊……栈区就摆在那,局部变量若直接声明,分配个P内存。但也由于栈区大小有限制,所以终于明白了一句话:优先用vector管理数组,堆是不限制咱的,vector本身就是几个指针而已。于是又明白了一点……以前在函数内建一个超大数组编译出错,不是内存不够,是栈区限制,以前还以为这是数组本身的特性所致。
    以前夜郎自大了……还是虚心学习吧……

回复

使用道具 举报

ID:79197 发表于 2015-5-8 11:02 | 显示全部楼层
好贴,mark一下,以后继续学习
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表