一 .学习体系
语言: C C++算法: 算法 数据结构
基础(系统核心Kernek编程):
Linux/Unix Window MacOSX
pc 服务器 ARM
操作系统五大能力:设备驱动/进程管理/内存管理/文件目录管理/IO
内存管理
文件目录
IO
进程管理:进程创建 进程控制 进程通信 进程同步
线程管理:线程创建,同步(控制,控制),通信
应用:网络 数据库(pro*c/c++) UI(QT) shell编程
定位:提高编程的能力,为设备驱动与window应用奠定基础
二 内存管理
硬件层次:内存结构管理内核层次:内存映射 堆扩展
语言层次:c:malloc c++:new
数据结构层次:STL(厂商:sgi 惠普 IBM ) 智能指针
实例代码
malloc.c
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p1 = new int;
int *p2 = new int;
int *p3 = new int;
int *p4 = new int;
int *p5 = new int;
//int *p1 = malloc(4);
//int *p2 = malloc(4);
//int *p3 = malloc(4);
//int *p4 = malloc(4);
//int *p5 = malloc(4);
printf("%p\n",p1);
printf("%p\n",p2);
printf("%p\n",p3);
printf("%p\n",p4);
printf("%p\n",p5);
return 0;
}
1.问题:malloc怎么分配空间? malloc与new的关系?
2.Linux对内存的描述
示例代码
test.c
#include <unistd.h>
#include <stdio.h>
main()
{
printf("%d\n",getpid());
while(1);
}
gcc test.c -omain main
输入命令行 ps aue 查看进程号
/proc/$(pid)/ 存放进程运行时候所有的信息(包括内存结构)
然后在另起一个终端 cd /proc/进程号 ls即可看到该进程有关的文件
cat maps(查看程序运行时所有的内存结构)
结论:任何程序的内存空间分成4个基本部分 (工具 ldd 可执行程序 查看可执行程序依赖哪些动态库)
1)代码区(有个动态库把程序加载到代码空间 代码区首地址是固定的 主函数全局常量 调用函数放代码区) 可以这样执行main函数 /lib/ld-linux.so.2 main 该动态库是个可执行文件 将程序加载到代码空间并将首地址定位到main 并且分配全局栈
2)全局栈区
3)堆
4)局部栈
2.理解程序的变量与内存空间的关系 见varmem.c 再见statckleap.c
varmem.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int add(int a,int b)
{
return a+b;
}
int a1=1;
static int a2=2;
const int a3=3;
main()
{
int b1=4;
static b2=5;
const b3=6;
int *p1=malloc(4);
printf("a1:%p\n",&a1);//全局区
printf("a2:%p\n",&a2);//全局区
printf("a3:%p\n",&a3);//代码区
printf("b1:%p\n",&b1);//栈
printf("b2:%p\n",&b2);//全局区
printf("b3:%p\n",&b3);//栈
printf("p1:%p\n",p1);//堆
printf("main:%p\n",main);//代码区
printf("add:%p\n",add);//代码区
printf("%d\n",getpid());
while(1);
}
statckleap.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main()
{
int a1=10;
int a2=20;
int a3=30;
int *p1=malloc(4);
int *p2=malloc(4);
int *p3=malloc(4);
printf("%p\n",&a1);<span style="font-family: Arial, Helvetica, sans-serif;">//0xbff82048</span>
printf("%p\n",&a2);<span style="font-family: Arial, Helvetica, sans-serif;">//0xbff8204c</span>
printf("%p\n",&a3);<span style="font-family: Arial, Helvetica, sans-serif;">//0xbff82048</span>
printf("%p\n",p1);<span style="font-family: Arial, Helvetica, sans-serif;">//0x9dee008</span>
printf("%p\n",p2);<span style="font-family: Arial, Helvetica, sans-serif;">//</span><span style="font-family: Arial, Helvetica, sans-serif;">0x9dee018</span>
printf("%p\n",p3);//<span style="font-family: Arial, Helvetica, sans-serif;">0x9dee028</span>
printf("%d\n",getpid());
while(1);
}
3.理解malloc的工作原理
malloc使用一个数据结构(链表)来维护分配空间
链表的构成:分配的空间/上一个空间的数据/下一个空间/空间大小等信息
对malloc分配的空间不要越界访问,因为容易破坏后台的维护结构,导致malloc/free
/calloc/realloc不能正常工作
示例代码
mallocstruct.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main()
{
int *p1=malloc(4);
int *p2=malloc(4);
int *p3=malloc(4);
*p1=1;
*(p1+1)=2;
*(p1+2)=3;
*(p1+3)=4;
*(p1+4)=5;
*(p1+5)=6;
*(p1+6)=7;
*(p1+7)=8;
//free(p1);会出错链表结构被破坏
printf("%d\n",*p2);
}
4.C++的new与malloc的关系
malloc new new[]
realloc new()定位分配
calloc new[]
free delete delete[]
newmalloc.cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int *p1=(int*)malloc(4);
int *p2=new int;
int *p3=(int*)malloc(4);
int *p4=new int;
int *p5=new int;
int *p6=new int;
//Stu *p=new Stu[30];
//delete p;
//delete[] p;
printf("%p\n",p1);
printf("%p\n",p2);
printf("%p\n",p3);
printf("%p\n",p4);
printf("%p\n",p5);
printf("%p\n",p6);
return 0;
}
结论:new的实现使用的是malloc来实现的区别:new使用malloc后,还要初始化空间
基本类型,直接初始化成默认值(int成0 bool类型是false)
UDT(用户自定义的)类型,直接调用指定的构造器
delete调用free实现
delete负责调用析构器,然后再调用free
new与new[]区别:
new只调用一个构造器初始化
new[]循环对每个区域调用构造器
delete与delete[]区别: Stu *p = new Stu[30] delete p(释放一个);delete[]p(释放一组);
5.函数调用栈空间的分配与释放
函数执行时使用栈空间作为自己的临时栈,3种方式决定编译器清空栈的方式
5.1.总结:
1.函数执行的时候有自己的临时栈(在局部栈里).
C++成员函数拥有两个栈空间,一个是函数本身的栈空间,另一个是对象的栈空间
因为函数本身是在对象的栈空间里运行
C里函数只有一个栈空间
2.函数的参数就在临时栈中.如果函数传递实参.则用来初始化临时参数变量.
不管哪种传递,实质传递的都是值,一般变量传的是数据,而指针传的是地址值
3.通过寄存器返回值.(使用返回值返回数据)
4.通过参数返回值.(参数必须是指针)
指针指向的区域必须事先分配.
5.如果参数返回指针.参数就是双指针.
5.2.__stdcall __cdecl __fastcall
1.决定函数栈压栈的参数顺序.
2.决定函数栈的清空方式:不管是哪种方式,都是在编译时编译器负责产生清空栈的函数 代码
_stdcall表示每个调用者负责清空自己调用的函数的临时栈
_fastcall函数自己在返回前自己清空临时栈,然后返回值退出
_cdecl表示所有调用者只有一个清空函数来负责清空所有被调用函数的临时栈
3.决定了函数的名字转换方式.在底层其实并不支持重载,会转换成不同的函数名。
在window下,函数名前加_stdcall
6.far near huge指针
near 16位
far 32位
huge综合
Win32统一采用far指针,这三个指针在window下会碰到