内存结构
内存结构
一个C 程序本质上都是由BSS(Block Started by Symbol) 段、Data 段、Text 段三个组成的。
BSS 段:在采用段式内存管理的架构中,BSS 段(Block Started by Symbol)通常是指用来存放程序中 未初始化的全局变量的一块内存区域。BSS 是英文Block Started by Symbol 的简称。BSS 段属于静态内存 分配,即程序一开始就将其清零了。比如,在C 语言之类的程序编译完成之后,已初始化的全局变量保存 在.data 段中,未初始化的全局变量保存在.bss 段中。
数据段:在采用段式内存管理的架构中,数据段(data segment)通常是指用来存放程序中已初始化的 全局变量的一块内存区域。数据段属于静态内存分配。
代码段:在采用段式内存管理的架构中,代码段(text segment)通常是指用来存放程序执行代码的一 块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可 能包含一些只读的常数变量,例如字符串常量等。
text 和data 段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而BSS 段不在可执行文件中,由系统初始化。
程序编译后生成的目标文件至少含有这三个段,这三个段的大致结构图如下所示:
其中data 段包含三个部分:heap(堆)、stack(栈)和静态数据区。
堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc 等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static 声明的变量,static 意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
stack 段存放函数内部的变量、参数和返回地址,其在函数被调用时自动分配,访问方式就是标准栈中的LIFO 方式。(因为函数的局部变量存放在此,因此其访问方式应该是栈指针加偏移的方式)
- malloc():申请一个内存空间
- realloc():在已有空间基础上在增加部分内存空间
- free():释放空间
动态内存的申请与回收
静态内存是由系统分配的,是栈内存中的连续内存空间,其运行效率非常高,且可以被系统自动回收。但是在某些情况下我们需要动态的申请一些内存空间,比如,在创建数组的时候我们不知道数组的长度是多少,那么我们就需要创建动态数组
动态内存是程序员手动申请的在堆内存中开辟的空间不一定是连续,,运行效率略慢,容易产生碎片需要手动回收
内存模型
void main()
{
int i = 0;
//指针数组
char * p1[] = {"123", "456", "789"};
//二维数组
char p2[3][4] = {"123", "456", "789"};
//手工二维内存
char **p3 = (char **)malloc(3 * sizeof(char *)); //int array[3];
for (i=0; i<3; i++)
{
p3[i] = (char *)malloc(10*sizeof(char)); //char buf[10]
sprintf(p3[i], "%d%d%d", i, i, i);
}
}
C语言与内存
程序 = 数据 + 算法
计算机运行程序的目的:得到一个结果;关注运行的过程
静态内存SRAM,动态内存DRAM
内存地址,内存单元(1个字节byte),内存编址是以字节位单位的
32位系统:32位数据线,32位地址线
三总线:地址总线,数据总线,控制总线
32位地址线:232 = 4G,地址线的数量决定了它可以寻址内存空间的大小
寄存器
register 寄存器,在CPU里面
void main()
{
//register int a = 3;
//&a;//地址是内存的地址,CPU没有地址
int a = 10;
int b; // b=a+5;
_asm // 内嵌汇编语言
{
mov eax, a
add eax, 5
mov b,eax
}
printf("%d", b);
getchar();
}
栈
#include<stdio.h>
#include<stdlib.h>
void test()
{
int a = 10;//自动分配,自动回收 ,栈的运行与更新非常快
printf("%p", &a);
printf("\n");
}
void main1()
{
test();
printf("\n\n");
test();
printf("\n\n");
//auto int a = 10;自动变量
getchar();
}
堆
- 动态分配,链接的,内存碎片化,因此速度比栈(连续的内存,自动分配自动释放)慢
- malloc() 只管分配,不管初始化
- calloc() 会对内存进行清零
- realloc()
- free()
代码区
函数名存放的是函数的入口地址,回调函数,劫持
#include<stdio.h>
#include<stdlib.h>
void gogogo()
{
printf("AAAAA\n\n\n\n");
}
void main1()
{
printf("%p", gogogo);//0x013A11A4,函数名存放函数的地址
//gogogo = gogogo;代码区只能读不可写
//void gogogo();
//void ()();
gogogo();//直接调用
void(*p)()=gogogo;
p();//间接调用
getchar();
}
void main()
{
char *p = "ABCDEFG";//p存储常量字符串的地址
printf("%d,%d\n", sizeof(p), sizeof("ABCDEFG"));
printf("\n%p", &p);//查看指针变量的地址
printf("\n%p", p);//查看字符串的地址
*p = 'a';//代码区只能读,常量字符串在代码区
getchar();
}
#include<stdio.h>
#include<stdlib.h>
//导出函数,可以加载的时候调用
_declspec(dllexport) void go()
{
//函数指针类型必须一致,否则无法调用成功
//int ** 变量名挖掉就是类型
//间接调用的方式调用函数,根据地址来调用
void(*p1)(int a) = (void(*)(int a)) 0x2c10eb; // 函数类型:void(*)(int a)
p1(10);
void(*p2)() = (void(*)())0x2c111d;
p2();
}
静态区
全局变量存在静态区,可以跨文件使用,声明可以有多个,定义只能有一个
static修饰全局变量,限定全局变量只在c文件内使用
extern int num;