C++开发基础知识(2)

本文详细解读了C/C++中的volatile关键字,介绍了其在Java虚拟机中的作用,以及在内存布局、中断服务、多任务环境中的使用场景。还涵盖了内存管理、函数调用约定、进程与线程的区别,以及volatile关键字在避免优化、保证可见性和原子性的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

volatile关键字一般出现在Java中,在C/C++中它的涵义是

volatile是java虚拟机提供的轻量级的同步机制,有三大特性:可见性,不保证原子性,禁止指令重排。
在C/C++中,volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
volatile的字面含义是易变的,它有下面的作用:
1 不会在两个操作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环境下,变量可能被其他的程序改变,编译器自己无法知道,volatile就是告诉编译器这种情况。
2 不做常量合并、常量传播等优化。
3 对volatile变量的读写不会被优化掉。
C语言关键字volatile
C语言关键字volatile表明某个变量的值可能在外部被改变,因此对这些变量的存取不能缓存到寄存器,每次使用时需要重新存取。该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程,
volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;

C/C++内存布局

一、首先进程地址空间的 1G 内核空间是给操作系统使用的,我们用户是没有操作权限的。
二、剩下的 3G 内存空间中,分为了栈区、内存映射段、堆区、数据段、bss段、代码段

栈区一般存放:函数体的局部变量、函数调用期间的所有参数压栈、函数的返回值
注意栈区这段内存是由操作系统自己维护的,所以函数结束,在栈上的空间会由操作系统自己回收。

贪心与动态规划的区别

看是否用到之前的最优解,如果用到就是动态规划,否则就是贪心,贪心无法解决动态规划的问题,但是动态规划能解决贪心的问题
我们在得到当前最优解的时候,需不需要使用前面的阶段中的得到的值,如果不需要,那就是贪心;如果需要,就是动归。

引用类型和值类型分别有哪些

值类型变量声明后,不管是否已经赋值,编译器为其分配内存。此时该值存储于栈上;int i=10;执行之前为默认一个值0。
引用类型定义时在栈上开辟一个空间用来存放其在堆上的地址,当赋值或者实例化时候就会在堆上开辟一个空间,然后把堆中的地址存放在栈中,这时候栈就存放了其地址。

值类型包括:结构体(数值类型,bool型,用户定义的结构体),枚举,可空类型。
引用类型包括:数组,用户定义的类、接口、委托,object,字符串。
成员变量值类型的默认值:
int类型的默认值是0
String类型的默认值是null
double类型的默认值是0.0d
Integer类型的默认值是null
Long类型的默认值是null
long类型的默认值是0L
float类型的默认值是0.0f
char类型的默认值是\u0000
byte类型的默认值是(byte)0
short类型的默认值是(short)0

匿名管道与命名管道的区别

匿名管道(pipe)
①只提供单向通信,也就是说,两个进程都能访问这个文件,假设进程1往文件内写东西,那么进程2 就只能读取文件的内容。
②只能用于具有血缘关系的进程间通信,通常用于父子进程建通信。
③管道是基于字节流来通信的
④依赖于文件系统,它的生命周期随进程的结束结束(随进程)
⑤其本身自带同步互斥效果
命名管道(named pipe 或 FIFO)。
1、与管道的区别:提供了一个路径名与之关联,以FIFO文件的形式存储于文件系统中,能够实现任何两个进程之间通信。而匿名管道对于文件系统是不可见的,它仅限于在父子进程之间的通信。
2、FIFO是一个设备文件,在文件系统中以文件名的形式存在,因此即使进程与创建FIFO的进程不存在血缘关系也依然可以通信,前提是可以访问该路径。
3、FIFO(first input first output)总是遵循先进先出的原则,即第一个进来的数据会第一个被读走。

void与void*

void关键字的使用规则:

  1. 如果函数没有返回值,那么应声明为void类型;
  2. 如果函数无参数,那么应声明其参数为void;
  3. 如果函数的参数可以是任意类型指针,那么应声明其参数为void * ;
  4. void不能代表一个真实的变量;
    void真正发挥的作用在于:
    (1) 对函数返回的限定;
    (2) 对函数参数的限定。

函数调用约定

函数调用约定: 就是对函数调用的一个约束和规定(规范),描述了函数参数是怎么传递和由谁清除堆栈的。它决定以下三个方面:
1. 函数参数传递的方式(是否采用寄存器传递参数、采用哪个寄存器传递参数、参数压桟的顺序等)
2. 函数调用结束后的栈指针由谁恢复(被调用的函数恢复还是调用者恢复)
3. 函数修饰名的产生方法
其中__cdecl是C/C++的默认调用约定。最常见的调用约定由__cdecl、__stdcall、__fastcall,其中使用最广泛的是__cdecl和__stdcall。还有一些不常用的调用约定,如__pascal、__thiscall等。

__cdecl调用方式规定:
1. 采用桟传递参数,参数从右向左依次入栈
2. 由调用者负责恢复栈顶指针
3. 编译器在编译时会在函数名前加上一个下划线前缀生成修饰名,格式为_function。如函数int Add(int a, int b)的修饰名是_Add。
参数的压栈顺序和栈的清理我们知道就行了,因为这是编译器的决定的,我们改变不了的。需要注意的是:调用参数个数可变的函数只能采用这种方式(如printf)。

__stdcall调用方式规定:
1. 采用桟传递参数,参数从右向左依次入栈
2. 由被调用者负责恢复栈顶指针
3. 在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_function@number。如函数int Add(int a, int b)的修饰名是_Add@8。

__fastcall调用方式规定:
1. 函数的第一个和第二个(从左向右)32字节参数(或者尺寸更小的)通过ecx和edx传递(寄存器传递),其他参数通过桟传递。从第三个参数(如果有的话)开始从右向左的顺序压栈;
2. 由被调用者恢复栈顶指针;
3. 在函数名之前加上"@",在函数名后面也加上“@”和参数字节数,例如@function@8;

__thiscall调用方式规定:
1. 采用桟传递参数,参数从右向左入栈。
2. 如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈;
3. 对参数个数不定的,调用者清理堆栈,否则由被调函数清理堆栈
参考:https://blog.csdn.net/weixin_39640298/article/details/84455481

进程与线程的区别

线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。

根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的

影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值