分配4G的虚拟空间,用多少占用多少,然后1G内核。
进程的内存空间从低地址到高地址内存布局依次是: 保留区 –> 文本段(包含程序和字符串常量)–>初始化的变量(.data,包含全局变量和静态变量)—>未初始化的变量(.bss,包含全局变量和静态变量)—>堆—>共享库或mmap—>栈–>命令函参数(main函数的参数)–>环境变量—>内核空间
- 栈向低地址推
- 堆向高地址推
- malloc的时候(堆)
- brk,将堆的最高地址往高出推,只能高地址释放,低地址才能释放
- mmap,随便释放,找一块空闲的虚拟内存
虚拟内存解决了一个物理空间不连续问题,将断断续续的物理内存映射到虚拟内存上,起到看起来是连续的一段空间。
1、brk是将堆的最高地址指针_edata往高地址推;
2、mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存。
这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
缺页中断===》一页=4096字节=4KB(注意是Byte,1B=8bit)
14.C语言如何面向对象多态
#include "stdio.h"
typedef struct Person{
char *name;
void (*eat)();
}Person;
void fun1(){
printf("student eat!rn");
}
void fun2(){
printf("teacher eat!rn");
}
int main(){
Person student={"student",fun1};
Person teacher={"teacher",fun2};
student.eat();
teacher.eat();
}
15.const 用法
const 右边靠近谁,谁就不可变,靠近*就是指向的不可变,靠近变量命,就是该变量不可变
const int *p; //const 修饰*p,p 是指针,*p 是指针指向的对象,不可变 int const *p; //const 修饰*p,p 是指针,*p 是指针指向的对象,不可变 int *const p; //const 修饰 p,p 不可变,p 指向的对象可变 const int *const p; //前一个 const 修饰*p,后一个 const 修饰 p,指针 p 和 p 指向的对象16.IIC了解吗?速率怎么样?时序和读写数据的过程?
IIC 总线协议定义如下:
- 只有在总线不忙时才能启动数据传输。
- 总线空闲
在停止条件之后、启动条件之前的时间内,数据线和时钟线都保持高电平。
- 在数据传输期间,只要 SCLx 时钟线为高电平,数据线就必须保持稳定。
- 在 SCLx 时钟线为高电平时,数据线的电平变化将被解析为启动或停止条件。
- 从器件应答(ACK、NACK)
所有的数据字节传输都必须由接收器应答 (ACK)或不应答 (NACK)。接收器将 SDAx 线拉低则发出 ACK,释放 SDAx 线则发出 NACK。应答为一位周期,使用一个 SCLx 时钟。
IIC协议学习笔记 - 知乎
主机写的时序
- 产生start位
- 传送器件地址ID_Address,器件地址的最后一位为数据的传输方向位,R/W,低电平0表示主机往从机写数据(W),1表示主机从从机读数据(R)。ACK应答,应答是从机发送给主机的应答,这里不用管。
- 传送写入器件寄存器地址,即数据要写入的位置。同样ACK应答不用管。
- 传送要写入的数据。ACK应答不用管。
- 产生stop信号。
主机读的时序:
- 产生start信号
- 传送器件地址(写ID_Address),ACK。
- 传送字地址(写REG_Address),ACK。
- 再次产生start信号
- 再传送一次器件地址,ACK。
- 读取一个字节的数据,读数据最后结束前无应答ACK信号。
- 产生stop信号。
总结:
启示信号:SCL高,SDA出现下降沿
结束信号:SCL高,SDA出现上升沿
应答信号:在数据传输后(8位),从机拉低SDA,则为应答ACK
写:在地址(7位)后加上0,发三(从机地址,寄存器地址,数据)应答三;
读:先假写(7位后+0),再读(7位后+1),启动两次,发三(从机地址,寄存器地址,从机地址)读一(读就行了)应答3,最后没有应答,从机写完数据之后就释放总线。
17. 开漏输出和推挽输出的区别?IIC为什么一定要用开漏输出?开漏输出和推挽输出的区别?
开漏输出与推挽输出 - 知乎
推挽输出的最大特点是可以真正能真正的输出高电平和低电平,在两种电平下都具有驱动能力。
常说的与推挽输出相对的就是开漏输出,对于开漏输出和推挽输出的区别最普遍的说法就是开漏输出无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动。
所谓的驱动能力,就是指输出电流的能力。
IIC为什么一定要用开漏输出?
STM32F1xx的IIC通信+推挽/开漏区别+是否上拉_暴躁的野生猿的博客-CSDN博客_i2c 推挽
为什么IIC必须要接上拉?
换句话说,IIC的从器件(如24C04等),不具备拉高总线的能力,从机控制SDA上的电平,只能通过令SDA接地或不接地来实现输出0或1,而无法通过接地或接VCC来实现输出0或1。这就要求IIC总线上必须是默认上拉的,否则从机无法令SDA为高。
从另一方面讲:只有开漏上拉,才能满足“线与”逻辑,也即SDA线上多个从机中的任何一个拉低了SDA,就会导致SDA为0。
主机为什么使用开漏?试想,主机如果使用了推挽,当主机释放SDA时(令SDA=1时),相当于SDA被接通到VCC,这时从机无法控制SDA为0;
主机如果使用了开漏,当主机释放SDA时(令SDA=1时),相当于SDA被悬空上拉到VCC,这时从机可以拉低SDA,使SDA=0,也可以释放SDA使SDA=1,这样从机才能发数据。
IIC的SCL和SDA都要求接上拉电阻,也即,只要主机和从机都还没开始干活,那么SCL和SDA就都是空闲的(默认的高电平状态被称为“空闲”,也可以称总线被“释放”),同理,SCL或SDA为low时,就称总线被占用或正忙。
IIC协议要求:SCL和SDA必须都是【开漏+上拉】!
18. 数组和指针有什么联系?指针就是指针,指针变量在 32 位系统下,永远占 4 个 byte,其值为某一个内存的地址。指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到。
数组就是数组,其大小与元素的类型和个数有关。定义数组时必须指定其元素的类型
和个数。数组可以存任何类型的数据,但不能存函数。
- 以下标的形式访问在本质上与以指针的形式访问没有区别,只是写法上不同罢了
- 指针和数组根本就是两个完全不一样的东西。只是它们都可以“以间接访问(*)形式”或“以下标形式”进行访问。
- 一个是完全的匿名访问(指针),一个是典型的具名+匿名访问(数组)。
int a[5]={1,2,3,4,5};
19. 堆和栈的区别
栈:保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容
也会自动被销毁。其特点是效率高,但空间大小有限。
堆:由 malloc 系列函数或 new 操作符分配的内存。其生命周期由 free 或 delete 决定。
在没有释放之前一直存在,直到程序结束。其特点是使用灵活,空间比较大,但容易出错。
一文读懂堆与栈的区别_恋喵大鲤鱼的博客-CSDN博客_堆和栈的区别
堆与栈实际上是操作系统对进程占用的内存空间的两种管理方式,主要有如下几种区别:
(1)管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;
(2)空间大小不同。每个进程拥有的栈的大小要远远小于堆的大小。
(3)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。
(4)分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。栈的分配系统会自动分配,用户也可以申请(通过alloca);
(5)分配效率(速度)不同。栈有底层指令实现,速度快;堆使用C库函数运算来申请和管理,速度慢;
(6)存放内容不同。栈存放的内容,函数返回地址(调用函数的指令的下一条指令)、相关参数、局部变量和寄存器内容等。 注意静态变量是存放在数据段或者BSS段,是不入栈的。 而堆中具体存放内容是由程序员来填充的。
20. 线程和进程的区别进程和线程的概念、区别及进程线程间通信 - 云+社区 - 腾讯云
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。
- 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。)
- 进程是资源分配的最小单位,线程是CPU调度的最小单位;
- 系统开销: 进程切换的开销也远大于线程切换的开销。
- 通信:由于同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现,也变得比较容易。进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
- 进程间不会相互影响 ;线程一个线程挂掉将导致整个进程挂掉
共享内存就是通过两个或连个以上的进程共享同一块内存区域来实现进程间的通信。
存放在共享内存的数据,任何进程都可以直接对其进行读取和修改。
因此共享内存所实现的进程通信是最快的。
多个进程在同时读写某一块共享内存时,内存中的数据会混乱,需要借助其他进程通信方式(比如信号量),来进行进程间的同步。
22. malloc底层原理linux一页的大小
获取Linux 内存页大小的命令_lansky54的博客-CSDN博客
获取Linux 内存页(基页)大小的命令:getconf PAGE_SIZE ,一般的输出是4096,即 4KB。
20. 实现strcpy和swap#include "stdio.h"
void mstrcpy(char * dest,const char * src){
while(*src!=' '){
*dest++=*src++;
}
}
void swap(int *a,int *b){ // 5 6
*a=*a+*b;// a=11
*b=*a-*b;// b=11-6=5
*a=*a-*b;// a=11-5=6
}
int main(){
char buf[20]={0};
mstrcpy(buf,"123123123123");
printf("rn%srn",buf);
int a=10;
int b=20;
swap(&a,&b);
printf("rn%d,%drn",a,b);
return 0;
}



