棋牌网站哪里做,网站可行性,怎么做本地婚姻介绍网站,网站正在建设中提示页面目录
1、C里指针和数组的区别
2、C中空指针请使用nullptr不要使用NULL
3、http/https区别和头部结构#xff1f;
4、有了mac地址为什么还要ip地址#xff1f;ip地址的作用
5、有了路由器为什么还要交换机#xff1f;
6、面向对象三大特性
7、友元函数
8、大端小端
…目录
1、C里指针和数组的区别
2、C中空指针请使用nullptr不要使用NULL
3、http/https区别和头部结构
4、有了mac地址为什么还要ip地址ip地址的作用
5、有了路由器为什么还要交换机
6、面向对象三大特性
7、友元函数
8、大端小端
9、野指针
10、static
11、指针*和引用的区别用sizeof时有什么不同为什么需要引用
12、静态变量什么时候初始化
13、new和malloc区别原理
14、内存泄漏、如果出现怎么排查和定位
15、const以及函数后加const
16、操作系统里堆和栈的区别
17、基类和派生类中构造函数和析构函数的顺序
18、strcpy和memcpy的区别谁的性能好
19、malloc和new的区别
20、静态多态和动态多态
21、内存分区
22、文件编译过程
23、虚拟内存
24、浏览器输入网址后执行的过程
25、进程和线程区别
26、死锁怎么产生的
27、设计模式
28、右值引用
29、MVC
30、分布式项目中客户端与服务端通信流程
31、tcp相比于udp的区别
32、RAII
33、分布式系统 1、C里指针和数组的区别
先来看一个经典问题char s1[]hello;
char *s2 hello;
1、s1的值是放在栈上的值是可以修改的而hello是一个字符串常量放在静态存储区是不能修改的。
2、内存大小不一样
#includestdio.hint main(){char s1[]hello;char *s2hello;puts(s1);puts(s2);printf(%ld %ld\n,sizeof(s1),sizeof(s2));return 0;
} 3、无法返回局部变量的地址栈上的值随着函数调用结束内存已经回收了 上面这种编译器会报警下面则不会 2、C中空指针请使用nullptr不要使用NULL C中NULL定义就是整数字面量0 对于C函数由于存在重载使用NULL而不是nullptr可能导致函数走错重载。 C中定义NULL为void* 0确实是代表空指针。使用时隐式转换成对应的需要类型的空指针。 C中void指针不能隐式转换成其他指针所以无法按照C那样定义。 C中保留NULL可以兼容一些C style的代码对于这些库不会使用到函数重载不会产生对应的问题。但对于纯C程序请使用nullptr表示空指针。 3、http/https区别和头部结构 HTTP超文本传输协议被用于在web浏览器和网站服务器之间传递消息以明文的方式发送内容不提供任何方式的数据加密传输端口为80特点是简单快捷灵活无状态每次请求都是独立的上一次请求和下一次请求互不相干。比如登录某个网站后本来不需要再登陆不知道上次请求已经登陆过了 HTTPSHTTPSSL/TLS 基于TLS/SSL协议加密进行引入了会话保持session和cookie状态记录-登录验证TCP传输拿到的是密文传输端口为443SSL/TLS协议依靠证书来验证服务器的身份。 4、有了mac地址为什么还要ip地址ip地址的作用 IP包裹地址一个为互联网的每一个网络和每一台主机分配的逻辑地址 Mac收件人信息媒体访问控制地址局域网地址以太网地址物理地址是一个用来确认网上设备位置的地址 Mac地址用于标示一个网卡一台设备若有一个或多个网卡则每个网卡都需要并会有一个唯一的Mac地址表明身份 只有Mac地址可以传输数据只要同处于一个局域网内 ISP互联网服务提供商。 5、有了路由器为什么还要交换机 路由器与外部通讯 交换机提供内网通讯 不是每个网络需要路由器比如企业学习医院等内部通信需要大量的接入设备只需要交换机一台交换机可以接入几十台设备而仅需一个路由器提供对外访问更大型的网络里需要对内部网进行划分若干小内网实现内部小网之间互相访问可采用带路由功能的交换机即三层交换机 路由器侧重点是共享交换机功能不强侧重点是多口构成局域网 如果有50台computer不得不用交换机,小路由器便宜但代理能力更弱只能代理几台若加很多口又没有能力代理没用但口多代理强的路由器很贵如只要交换机加了路由价也贵了功能也多余的. 6、面向对象三大特性 1封装性将客观事物抽象成类每个类对自身的数据和方法实行protection 2继承性广义的继承有三种实现形式实现继承使用基类的属性和方法而无需额外编码的能力、可视继承子窗体使用父窗体的外观和实现代码、接口继承仅使用属性和方法实现滞后到子类实现。 3多态性是将父类对象设置成为和一个或更多它的子对象相等的技术。用子类对象给父类对象赋值之后父类对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。 封装是实现面向对象程序设计的第一步将数据或函数等集合在一个个的单元类中。封装的意义就是保护或防止数据被无意破坏。
继承主要用来实现重用代码节省开发时间子类可以继承父类。
多态是指同一操作作用于不同的对象可以有不同的解释产生不同的执行结果。在运行时可以通过指向基类的指针来调用实现派生类中的方法。
继承是面向对象编程中实现代码复用的重要手段。通过继承子类可以继承父类的属性和方法并可以添加或覆盖父类的方法。这使得子类能够共享父类的代码并扩展或修改其功能。 public继承 protected继承 private继承 基类的public 派生出public 派生出protected 派生出private 基类的protected 派生出protected 派生出protected 派生出private 基类的private 在派生类不可见 在派生类不可见 在派生类不可见
private基类无论以什么方式继承到派生类中都是不可见的
使用class时默认是private使用struct默认是public
封装是面向对象编程的核心概念之一它通过将数据和操作数据的方法绑定到一个对象中隐藏对象的内部状态和实现细节只对外提供公共接口。这样可以提高代码的安全性和可维护性。
封装将类的实现细节隐藏暴露一个简洁。清晰的接口提高代码的可重用性、实用性、耦合度、可维护性
访问控制权限
一个类的public变量、函数可以通过类的实例变量访问
protected无法通过变量访问但可以通过类的友元函数、友元类访问
private同protected
多态多种形态去完成某个行为当不同对象去完成时会产生不同的状态
买票学生半价、普通人全价
实现重写/覆盖:子类有一个跟父类完全相同的虚函数子类的虚函数重写了基类的虚函数
子类父类都要这个虚函数
多态是面向对象编程中实现接口统一和灵活性的关键特性。通过多态不同的对象可以对同一消息作出不同的响应。这使程序在运行时能够根据对象的实际类型来执行相应的操作提高了代码的灵活性和可扩展性。分为静态和动态两种
静态编译时重载、泛型编程、重载运算符、模板编译时自动绑定好
动态运行时虚函数、virtual、回调函数
虚函数有一个虚函数表虚函数表指针一般有四个字节虚表指针在内存哪个分区取决于在堆上创建还是栈上
虚函数和纯虚函数通过使用指向基类对象的指针或引用调用虚函数 多态分为两种一种是编译时多态比如函数重载和模板编译器在编译代码时会根据不同的代码匹配相应的函数。另一种是运行时多态主要通过虚函数实现。在定义类时定义一个虚函数当在子类中使用时只需要重写这个虚函数。 重写虚函数一般先写函数的返回值、函数名和形参然后在形参后面加上override关键字。如果是纯虚函数必须重写才能使用。如果不是纯虚函数override关键字可以不加。
C在运行时虚函数通过虚表vtable实现。当一个对象调用虚函数时程序会通过该对象的虚表指针找到相应的函数地址并进行调用。
虚函数重写使用override关键字重写虚函数。纯虚函数必须重写非纯虚函数可以不加override。
7、友元函数 友元函数是 一个特性它允许一个或多个非成员函数访问一个类的私有和保护成员。友元函数不是类的成员函数但它可以访问类的所有成员包括私有成员。这种机制提供了一种突破数据封装和隐藏的方式使得非成员函数能够直接操作类的内部数据。然而使用友元函数需要谨慎因为它可能会破坏封装性并增加代码的复杂性。 8、大端小端
大端小端是计算机存储数据的一种字节序方式。
大端模式Big-Endian是指高位字节存储在内存的低地址处而低位字节存储在内存的高地址处
小端模式Little-Endian则相反低位字节存储在内存的低地址处高位字节存储在内存的高地址处。这两种模式在跨平台编程和网络通信中需要特别注意。 如果数据类型占用的内存空间大于1字节CPU把数据存放在内存中的方式有两种
1大端序Big Endian低位字节存放在高位高位字节存放在低位。
2小端序Little Endia低位字节存放在低位高位字节存放在高位
大小端含义
大端:低地址存放数据的高位,高地址存放数据的低位. 即CPU对操作数的存放顺序为高字节到低字节.
如存放数据0x123456:
数据: 0x56 | 0x34 | 0x12
地址: 0x01 | 0x02 | 0x03
小端:低地址存放数据的低位,高地址存放数据的高位. 即CPU对操作数的存放顺序为低字节到高字节.
如存放数据0x123456:
数据: 0x12 | 0x34 | 0x56
地址: 0x01 | 0x02 | 0x03
判断大小端
1.联合体判断:因为联合体的大小为联合体中所有数据当中类型最大那个类型的大小,则通过char和int可以判断.
#includeiostream
using namespace std;
union A{
char a;
int b;
/*char 占一个字节,int占四个字节
所以联合体的大小为四个字节,且a b公用一块内存
当对b赋值为1时,则b0x00 00 00 01;
当为大端模式时:读取a得到的值为0;
当为小端模式时:读取a得到的值为1;
*/};int main(){
A U;
U.b1;
if(U.a){
cout小端模式endl;
}
else{
cout大端模式endl;
}
return 0;
}
通过对四字节的 int* 强转为一字节的 char*
#includeiostream
using namespace std;
int main()
{
int i 1;
//i为i的地址,此时为int*类型
//(char*)将int*强转为char*
//*取该char*的内容
if (*(char*)i) {
cout小端模式endl;
}
else{
cout大端模式endl;
}
return 0;
}
9、野指针 野指针是指指向已经被释放的内存空间的指针或者是一个未被初始化的指针。使用野指针可能会导致程序崩溃或数据损坏。为了避免野指针问题需要在使用指针前进行初始化并在释放内存后将指针置为nullptr。 10、static 修饰静态局部变量会改变局部变量的存储位置从而使得局部变量的生命周期变长延长至程序结束才销毁普通的局部变量创建后是放在栈区这种局部变量进入作用域时创建出了作用域就销毁。static修饰局部变量只改变生命周期不改变作用域 修饰全局变量会改变全局变量的链接属性使全局变量无法被其他文件调用作用域变小 全局变量程序所有源文件对象及函数都可以调用生命周期贯穿整个程序想被另一个文件使用时需要进行外部声明extern 修饰函数改变函数链接属性从而使作用域变小函数本身有外部链接属性被修饰后成了内部链接属性 11、指针*和引用的区别用sizeof时有什么不同为什么需要引用 指针内存地址指针变量是用来存放内存地址的变量 引用给已存在的变量取一个别名编译器不会给引用变量开辟内存空间共用一块内存空间主要作用是修饰函数的形参和返回值 在C中函数和返回值的传递方式有三种值传递、指针传递、引用传递引用具有指针的效率有又具有变量使用的方便性 区别 引用给予程序元素完成其功能的最小权限指针能无约束地操作内存中的任何东西非常危险 引用定义必须初始化指针没有但尽量初始化防止野指针 引用在初始化后不能再引用其他实体指针可以 没有null引用但是有nullptr指针 在sizeof中含义不同引用结果为引用类型大小但指针是地址空间所占字节个数32位平台占4个字节 有多级指针但没有多级引用 12、静态变量什么时候初始化
初始化只有一次但可以多次复制在主程序之前编译器已经为其分配好了内存
静态变量和全局变量一样都存放在全局区域数据区
如果是cint a1;int ba; 是错误的在编译时才初始化
Cint a1;static int ba;
由于C引入对象后要进行初始化必须执行相应的构造函数和析构函数经常会进行特定操作所以C为全局或静态对象时有首次用到时才会进行构造C中内置类型比如int、double、char都升级成了类在对象第一次使用时初始化.
13、new和malloc区别原理
new、delete是关键字需要编译器支持
malloc、free是库函数需要引入相应头文件
malloc申请空间时要填入申请内存大小int *m(int*)malloc(4);堆空间
new根据类型分配内存 int *anew int(0); 自由存储区
c内存分为堆、栈、自由存储区、全局/静态存储区、常量存储区
堆是操作系统维护的一块特殊内存提供了动态分配功能当运行程序调用malloc时会从中分配调用free归还内存
自由存储区是c中动态分配和释放对象的概念
通过new分配的内存区域可以称为自由存储区自由存储区可以是堆、全局/静态存储区等
new返回对象类型的指针类型与对象匹配
malloc内存分配成功返回void *需要通过强制类型转换将void*转换成所需分配失败返回null
new失败会抛出异常
malloc可以通过realloc扩张new没有 1new是C中的操作符malloc是C中的一个函数。
2new不止分配内存而且会调用类的构造函数同理delete调用类的析构函数而malloc只分配内存不会进行初始化类成员的工作同样free也不会调用析构函数。
3内存泄露对于malloc或者new都可以检查出来区别在于new可以指明是文件的哪一行而malloc没有这些信息。
4new可以认为是malloc加构造函数的执行new出来的指针是直接带类型信息的而malloc返回的值都是void指针
14、内存泄漏、如果出现怎么排查和定位
分配的内存没有被正确释放导致内存被占用过多主要与动态内存分配有关
int *pnew int;
delete p;
对同一个指针重新分配内存int *pnew int; pnew int;
导致程序运行效率下降、程序出现安全漏洞、内存资源枯竭
15、const以及函数后加const
const int a 常整型数
int const a 常整型数
const int *a 指向常整型数的指针即a的值可以变*a不可变
int* const a 常指针a的值不能变*a可变
int const *a const 指向常整型数的常指针 const修饰函数参数只能修饰输入作用的参数如果输入参数为指针加上const起到保护指针意外修改的作用
const int fun();没有必要只是个临时值最终这个值会复制给接受它的变量
const修饰成员函数为了保护成员变量要求const函数不能修改成员变量否则编译报错
const对象只能访问const成员函数非const对象可以访问任何成员函数包括const成员函数
const成员函数可以分为所有成员变量但只能访问const的成员函数
const成员函数不能修改任何成员变量除非变量用mutable修饰
在const成员函数中成员变量都变成const属性无法再次修改
16、操作系统里堆和栈的区别
堆由程序员分配释放若不释放程序结束时可能由OS回收堆的内部地址生长方向与栈相反由低到高
栈由操作系统自动分配释放存放函数的参数值局部变量栈的内部地址是由高到低分配的因此后定义的变量在栈中的地址低于先定义的变量
空间大小栈的值是固定的由编译器不同而不同一般为2M较小堆理论上可以分配虚拟内存大小的空间堆区的内存空间是由链表组织的是不连续的
17、基类和派生类中构造函数和析构函数的顺序
构造先基类再派生类
析构先派生类再基类
多个基类的调用跟基类继承的顺序有关
18、strcpy和memcpy的区别谁的性能好 strcpy和memcpy都是C语言中常用的字符串复制函数
主要区别
复制内容
strcpy只能复制以\0结尾的字符串。
memcpy可以复制任意内容包括字符数组、整型、结构体、类等只要指定了正确的长度。
复制方法
strcpy不需要指定长度遇到字符串结束符\0时停止复制。如果目标空间不够大可能会导致缓冲区溢出。
memcpy根据其第三个参数即要复制的字节数来决定复制的长度不会自动停止。因此需要确保目标空间足够大以容纳要复制的数据。
参数类型
strcpy参数是字符指针char*。
memcpy参数是void指针void*提供了更大的灵活性可以复制任何类型的数据。
安全性
strcpy由于不检查目标空间是否足够大可能会导致缓冲区溢出存在安全隐患。
memcpy在正确使用时即确保目标空间足够大相对更安全。但如果不小心指定了错误的长度也可能导致内存问题。
性能表现
对于较短的字符串strcpy可能需要额外的处理来查找字符串结束符\0这可能会使其比memcpy稍微慢一些。然而这种差异通常非常小并且在大多数情况下可以忽略不计。
对于较长的字符串或需要复制非字符串数据时memcpy的性能可能会更好因为它不需要处理字符串结束符并且可以直接根据指定的长度进行复制。
在实际应用中性能的差异还受到编译器优化、内存布局、CPU缓存等多种因素的影响。因此很难给出一个绝对的结论说哪个函数性能更好。通常选择哪个函数应该基于具体的使用场景和需求。
总结
如果需要复制以\0结尾的字符串并且目标空间足够大以容纳整个字符串包括结束符则可以使用strcpy。但需要注意避免缓冲区溢出的问题。
如果需要复制任意类型的数据或指定长度的字符数组并且希望避免字符串结束符的处理则可以使用memcpy。但需要确保目标空间足够大以容纳要复制的数据。
19、malloc和new的区别
malloc函数用于在堆上分配指定字节数的内存并返回一个指向该内存的指针类型为void*通常需要强制类型转换。
初始化malloc分配的内存是未初始化的即内存中的值是未定义的。
释放使用malloc分配的内存必须使用free函数来释放否则会导致内存泄漏。
灵活性malloc只负责分配内存不涉及对象的构造。因此对于类类型的对象仅使用malloc是不足够的还需要手动调用构造函数。
new运算符是C中特有的。它用于在堆上分配内存并构造对象。new运算符返回一个指向新创建对象的指针。
初始化使用new分配并构造的对象会被自动初始化。对于内置类型将调用默认构造函数如果适用或进行零初始化对于类类型将调用其构造函数。
释放使用new分配并构造的对象必须使用delete运算符来释放和销毁。delete运算符会首先调用对象的析构函数然后释放内存。
便利性new运算符结合了内存分配和对象构造两个步骤使代码更加简洁和易于管理。
malloc和new都用于在堆上动态分配内存但new还负责对象的构造。
malloc返回的是void*类型的指针需要手动进行类型转换而new返回的是具体类型的指针。
使用malloc分配的内存必须使用free释放而使用new分配并构造的对象必须使用delete释放和销毁。
new运算符在分配内存时会进行初始化对于类类型会调用构造函数而malloc不会。
在C中推荐使用new和delete进行动态内存管理因为它们更符合C的面向对象特性。然而在处理某些与C语言接口的代码或需要精确控制内存布局的场景中malloc和free仍然是有用的。
20、静态多态和动态多态 静态多态又称编译期多态是指在编译时就能确定对象的类型和方法调用的多态性。 实现方式 函数重载和运算符重载。在C中函数重载是指在同一个作用域内可以声明多个具有相同名字但参数列表不同的函数。运算符重载则是对已有的运算符进行重新定义使其能够用于用户自定义的类型。 特点
效率较高由于静态多态在编译时确定编译器可以进行优化提高程序运行效率。
适配性和松耦合性通过模板和特化等技术静态多态可以处理不同类型的数据实现代码的复用和扩展。
泛型设计静态多态为C带来了泛型设计的概念 二、动态多态是指在运行时才能确定对象的类型和方法调用的多态性。
实现方式 继承和虚函数来实现。一个基类中的成员函数可以被声明为虚函数这意味着该函数在派生类中可以被重写Override。当使用基类指针或引用来调用虚函数时程序会在运行时根据实际对象的类型来确定调用哪个函数。
特点
灵活性高动态多态允许程序在运行时根据对象的实际类型来选择合适的方法提高了程序的灵活性和可扩展性。
接口与实现分离通过虚函数和继承动态多态实现了接口与实现的分离使得代码更加清晰和易于维护。
处理异质对象集合动态多态可以处理同一继承体系下的异质对象集合实现多态性。
示例 在C中可以通过继承和虚函数实现动态多态。例如一个基类中包含一个虚函数派生类重写该虚函数。当使用基类指针指向派生类对象并调用虚函数时程序会调用派生类中的重写函数。
三、静态多态与动态多态的比较
本质区别
静态多态在编译时确定对象的类型和方法调用由模板具现完成。
动态多态在运行时确定对象的类型和方法调用由继承和虚函数实现。
接口形式
静态多态的接口是隐式的以有效表达式为中心多态通过模板具现在编译期完成。
动态多态的接口是显式的以函数签名为中心多态通过虚函数在运行期实现。
优缺点
静态多态的优点包括效率高、适配性强、支持泛型设计等缺点包括调试困难、编译耗时、代码膨胀等。
动态多态的优点包括灵活性高、接口与实现分离、处理异质对象集合等缺点包括运行期绑定导致一定的运行时开销、编译器无法对虚函数进行优化等。
综上所述静态多态和动态多态各有优缺点应根据具体的应用场景和需求来选择合适的多态实现方式。
21、内存分区
在C语言中程序的内存布局通常被划分为几个不同的区域每个区域都有其特定的用途和管理方式。以下是对C语言内存分区的详细解释
代码区Text Segment
代码区也被称为文本段或代码段。
这个区域存储了程序的机器指令即CPU要执行的代码。
代码区通常是只读的以防止程序意外地修改自己的指令。
当程序被加载到内存中时代码区的内容从可执行文件中复制而来。
全局/静态数据区Data Segment
全局/静态数据区包含了全局变量、静态变量和常量数据。
全局变量和静态变量在程序的整个生命周期内都存在而常量数据则用于存储字符串常量等不变的值。
这个区域在程序开始执行前就被初始化并在程序结束时被释放。
它分为已初始化和未初始化两个部分
已初始化数据区Data Segment存储已初始化的全局变量和静态变量。
未初始化数据区BSS Segment存储未初始化的全局变量和静态变量这些变量在程序开始执行前被自动初始化为零。
堆区Heap
堆区用于动态内存分配。
程序员可以使用如malloc、calloc、realloc等函数在堆上分配内存并使用free函数释放内存。
堆区的大小在程序运行时是可变的由程序员控制。
如果程序员忘记释放已分配的内存可能会导致内存泄漏。
栈区Stack
栈区用于存储局部变量和函数调用信息如函数参数、返回值地址、局部变量指针等。
栈的大小在程序编译时确定并在程序运行时由系统自动管理。
每当函数被调用时系统会在栈上为该函数的局部变量和调用信息分配空间当函数返回时这些空间会被自动释放。
栈的访问速度非常快因为栈内存通常是连续分配的。
这些内存分区共同构成了C语言程序的内存布局。程序员需要了解这些分区的特性和用途以便正确地管理内存和编写高效的C语言程序。同时也要注意避免内存泄漏、缓冲区溢出等常见的内存管理错误。
22、文件编译过程
文件编译的过程是将源代码转换为可执行文件的过程这个过程通常包括预处理、编译、汇编和链接四个阶段。
一、预处理Preprocessing
主要任务是对源代码进行初步的处理为后续的编译阶段做准备。
处理头文件通过#include指令将所需的头文件内容插入到源文件中形成一个整体的源代码文件。头文件通常包含函数声明、宏定义、类型定义等。
宏替换将所有定义的宏进行替换例如将所有出现的宏名替换为对应的宏定义内容。宏定义通常使用#define指令进行。
条件编译根据预处理指令如#ifdef、#ifndef、#if、#elif等的条件判断选择性地编译代码段。
删除注释删除所有的注释内容包括单行注释//和多行注释/* */。
预处理后的结果通常是一个中间文件这个文件包含了经过宏替换、条件编译等处理后的源代码。
二、编译Compilation
编译是编译过程中的核心阶段主要任务是将预处理后的源代码转换为汇编语言。
词法分析将源代码划分为一个个的标记token如关键字、标识符、运算符等。
语法分析根据语法规则将标记组合成语法树并检查代码是否符合语法规范。
语义分析对语法树进行语义检查包括类型检查、变量声明检查等。如果源代码有语法或语义错误编译器会报错并停止编译过程。
编译后的结果通常是汇编代码这些代码包含了程序的基本逻辑和运算指令。
三、汇编Assembly
汇编是将编译生成的汇编代码转换为机器指令的过程。
符号解析将变量和函数引用与其定义进行关联生成符号表。
生成机器码将汇编指令翻译成机器指令并生成目标文件通常是.o或.obj文件。目标文件包含了程序的可执行代码以及相关的调试信息。
四、链接Linking
链接是将多个目标文件和所需的库文件链接在一起生成最终的可执行文件的过程。
符号解析将各个目标文件中的符号引用与其定义进行关联解决符号引用问题。
重定位将目标文件中的地址引用转换为实际的内存地址。
合并代码和数据将各个目标文件中的代码和数据合并到一起生成最终的可执行文件。
链接过程中还会处理程序的静态库和动态库依赖关系确保程序在运行时所需的库文件已经被正确地加载。
总结
通过预处理、编译、汇编和链接这四个阶段源代码最终被转换为可执行文件并在计算机上运行。在实际的软件开发中这些阶段通常由编译器和链接器自动完成。了解文件编译的过程有助于程序员更好地理解程序的底层机制和运行原理从而更好地优化代码和提高编译效率。
23、虚拟内存
虚拟内存是计算机系统内存管理的一种技术它使得应用程序认为它拥有连续可用的内存一个连续完整的地址空间而实际上这部分内存是被分隔成多个物理内存碎片的有时部分数据还会暂时存储在外部磁盘存储器上在需要时进行数据交换。以下是对虚拟内存的详细解释
一、定义与概念
虚拟内存是操作系统用来扩展可用内存容量的一种技术。它通过将部分数据暂时存储在硬盘上使得超出物理内存限制的数据也能被有效使用。每个程序都使用虚拟地址空间来访问内存而非直接访问物理内存。虚拟内存管理器负责将虚拟地址转换为物理地址。
二、工作原理
虚拟内存的核心在于分页Paging技术。操作系统将内存划分为固定大小的块称为页面Page每一页通常为4KB。在实际运行时操作系统会将所需的页面从虚拟内存调入物理内存。当某个页面不再需要时它会被换出Swapped Out到虚拟内存。以下是虚拟内存工作原理的详细步骤
地址映射每个进程都有自己的页面表用于记录虚拟地址和物理地址的映射关系。当进程访问内存时CPU会使用页面表来转换虚拟地址为物理地址。
页面调度当程序需要访问一个页面时虚拟内存管理器会检查该页面是否已经在物理内存中。如果已经在物理内存中则直接访问如果不在则发生页面错误Page Fault。
页面错误当进程访问的页面不在物理内存中时会发生页面错误。操作系统会暂停进程将所需的页面从虚拟内存调入物理内存通常是从硬盘上的页面文件中读取然后恢复进程的执行。
页面置换当物理内存已满时操作系统会选择不常用的页面进行置换将其换出到虚拟内存即硬盘上的页面文件中从而腾出空间给新的页面。页面置换算法有多种如最近最少使用LRU等。
三、优点与缺点
优点
扩展内存空间虚拟内存使得计算机能够运行比物理内存更大的应用程序有效扩展了系统的内存容量。
提高多任务处理能力通过虚拟内存操作系统可以在有限的物理内存上运行多个程序提高了系统的多任务处理能力。
提高内存利用率虚拟内存允许程序使用比物理内存更多的内存而不会因此而崩溃。同时通过页面置换算法可以更有效地利用物理内存空间。
简化内存管理虚拟内存为程序员提供了一个更大的、连续的地址空间简化了内存管理的工作。
缺点
占用一定的物理硬盘空间虚拟内存需要在硬盘上存储页面文件因此会占用一定的物理硬盘空间。
加大了对硬盘的读写由于页面置换和页面调度等操作虚拟内存会加大对硬盘的读写频率可能会影响硬盘的寿命和性能。
设置不当会影响整机稳定性与速度如果虚拟内存设置不当如页面文件大小设置不合理、虚拟内存与系统设在同一分区内等可能会影响整机的稳定性和速度。
四、应用场景
大型数据处理在处理大数据集或运行大型应用程序时虚拟内存可以提供更大的内存空间避免内存不足的问题。
多任务操作虚拟内存允许同时运行多个应用程序提高了多任务处理能力适用于需要同时运行多个程序的场景。
内存保护通过虚拟内存提供的内存保护机制可以防止不同进程之间的内存互相干扰从而提高系统稳定性和安全性。
五、配置与优化
根据内存大小和电脑用途设定虚拟内存的设定主要根据电脑的内存大小和用途来设定。一般来说可以让操作系统自动分配管理虚拟内存它能根据实际内存的使用情况动态调整虚拟内存的大小。
避免与系统设在同一分区内为了避免系统在此分区内进行频繁的读写操作而影响系统速度最好将虚拟内存设置在其它分区中磁盘剩余空间较大而又不常用的盘中如D、E盘。
合理配置虚拟内存大小一般默认的虚拟内存大小是取一个范围值但最好给它一个固定值以减少磁盘碎片的产生但需要注意的是固态硬盘不会产生磁盘碎片。具体数值可以根据物理内存大小来定。
选择速度较快的硬盘或SSD为了提高虚拟内存的性能可以选择速度较快的硬盘或固态硬盘SSD来存储页面文件。
综上所述虚拟内存是计算机系统内存管理的一种重要技术它通过分页技术和页面置换算法实现了内存扩展和多任务处理等功能。虽然虚拟内存有一定的缺点和限制但通过合理的配置和优化可以充分发挥其优势并提高系统的性能和稳定性。
24、浏览器输入网址后执行的过程
当在浏览器中输入网址并按下回车键后 DNS解析浏览器首先将输入的网址发送给DNS服务器以获取该网址对应的IP地址。DNS服务器会查询其数据库在找到匹配的域名时返回对应的IP地址给浏览器。 TCP连接建立浏览器使用获取到的IP地址与服务器建立TCP连接。这涉及到使用TCP三次握手的过程确保客户端与服务器之间的可靠连接。 发起HTTP请求一旦建立了TCP连接浏览器会发送HTTP请求到服务器。请求包含请求方法例如GET、POST、请求的URL、HTTP版本以及其他可能的请求头信息如用户代理、Cookie等。 服务器处理请求服务器收到HTTP请求后会根据请求的URL和其他请求信息来处理请求。服务器可能会读取请求中的参数查询数据库执行相应的逻辑处理并生成HTTP响应。 HTTP响应服务器生成完整的HTTP响应后将其返回给浏览器。响应包括一个状态码表示请求的结果例如200表示成功404表示资源未找到等响应的内容以及其他响应头信息如Content-Type、Content-Length等。 接收和解析响应浏览器接收到服务器的HTTP响应后开始解析响应。它会检查状态码根据响应头中的Content-Type确定响应内容的类型并将响应的内容保存下来。 渲染页面如果响应的内容是HTML页面浏览器会开始解析HTML并构建DOM树。然后将CSS文件加载和解析为样式规则并将其应用于DOM树生成渲染树。最后浏览器使用渲染树将页面内容绘制到用户的屏幕上。 关闭TCP连接一旦页面完全加载并渲染完成浏览器会关闭与服务器之间的TCP连接。但是如果页面中存在其他的资源如图片、脚本、样式表等浏览器可能会继续发送HTTP请求获取这些资源。
25、进程和线程区别
进程是操作系统资源分配的最小单元一个进程拥有的资源有字节的堆、栈、虚存空间页表等
可以看作是一个类或PCB进程控制块的结构体,进程是操作系统堆一个正在运行的程序的一种抽象可以把进程看作程序运行的一次运行过程
程序是在一个静态磁盘上的一个可执行文件
进程是将可执行文件加载到系统中加载就是将信息放在内存中分配资源并执行指令
进程本质PCB代表一个实实在在运行着的程序也就是进程
包括PID:进程ID
进程状态新建状态就绪运行阻塞销毁
优先级决定进程的执行顺序
记账信息记录CPU调用次数和执行间隔
上下文信息保存本次执行状态
一组内存指定进程需要使用的资源 线程被包含在进程中是进程中实际运行的单位
一个进程可以并发多个线程每个线程执行不同的任务是操作系统进行运算调度的最小单元进程中包含了线程线程属于进程
每个进程有自己的内存和资源一个进程中的线程会共享这些内存和资源
26、进程间的通信方式线程的通讯方式呢怎么实现
管道半双工通信
从内核中划出一片空间用作缓存一个进程往这片缓存中写入数据另一个进程读取这个缓存中的数据分为匿名管道和命名管道匿名管道是半双工通信数据只能在管道中单向流动如果想要进行双向通信则需要建立两条管道且一般只用于父子进程之间的通信
一个进程采用pipe函数创建管道该函数返回两个文件描述符一个是写描述符一个是读描述符
通过fork创建一个子进程然后实现父子进程之间的通信 信号是一种软中断产生方式有两种一种为硬件产生即采用终端设备比如键盘来产生信号另一种是通过调用系统函数给一个进程发送信号
进程收到信号之后并不会立刻执行而是在cou由内核态转变为用户态之前会检查是否有未处理的信号 消息队列进程给进程发送数据时只需要即将数据放在消息队列中便可以立即返回而不需要阻塞
消息队列是保存在内核中的消息链表通信双方会约定好收发数据的格式而不是无格式的字节流 共享内存可以理解为操作系统内核所划分出的一段物理地址空间由于每一个进程都有自己的虚拟地址空间且通过页表完成地址的映射所以可以通过借助页表完成进程中的一段虚拟地址空间和共享内存的映射
信号量当使用共享内存的通信方式如果有多个进程同时往共享内存写入数据有可能先写的进程的内容被其他进程覆盖了因此需要一种保护机制信号量本质是一个整型的计数器用于实现进程间的互斥和同步
信号量代表资源的数量操作信号量的方式有两种
P操作将信号量减一相减后信号量如果小于0则表示资源已经被占用了进程需要阻塞等待如果大于等于0则说明还有资源可用进程可以正常执行
V操作将信号量加一相加后如果小于等于0则表明当前有进程阻塞于是将该进程唤醒如果大于0则表明当前没有阻塞的进程
26、死锁怎么产生的
两个或多个并发进程中如果每个进程持有某种资源而又都等待着别的进程释放它或它们现在保持着的资源否则就不能向前推进此时每个进程都占用了一定的资源但又都不能向前推进称这一组进程产生了死锁
产生原因系统资源不足进程推进顺序非法
必要条件
互斥条件---涉及的资源是非共享的
不剥夺条件---进程所获得的资源在未使用完毕之前不能被其他进程强行夺走
部分分配---进程每次申请它所需的一部分资源在等待新资源的同时继续占用已分配到的资源
环路条件
线程状态它们是怎么转换的
新建状态新创建了一个线程对象
就绪状态线程对象创建后只等待获取cpu的使用权
运行状态就绪状态的线程获得了cpu执行程序代码
阻塞状态线程因某种原因放弃cpu使用权暂时停止运行直到线程进入就绪状态才有机会转到运行状态
死亡状态线程执行完了或因异常退出了run方法该线程结束生命周期
27、设计模式
工厂模式定义创建对象的接口封装对象的创建使得具体化类的工作延迟到了子类中
Return new ConcreteProduct();
仅局限于一类的类
AbstactFactory模式抽象工厂模式
28、右值引用
左值可以取地址可以修改可以放在等号两边右值不可以取只能放在右边没有名称
区分放在等号左边的是左值右边的是右值
Int a3;//a是左值3是右值
Int ba;//ab都是左值
Int ba;//右值引用左值不行
右值引用
右值引用必须初始化int a10
不能用左值初始化
当对右值加上引用后可以修改值也可以修改地址从功能上升为左值右值引用的本质是不用拷贝的左值
引用的目的传递参数有两种方式值传递和引用传递
左值引用函数传参函数返回值相比于值传递减少了拷贝次数
当返回值为右值引用时会把返回的临时变量中的内存据为己有仍保持了有效性避免拷贝
29、MVC MVC模式即Model-View-Controller模式是一种经典的软件设计模式。
MVC模式旨在将应用程序的输入、处理和输出分开使得数据Model、视图View和控制逻辑Controller相互独立从而提高代码的可扩展性、可复用性、可维护性以及灵活性。
二、组成部分
模型Model
是应用程序中的数据部分表示应用程序中的状态和行为。
负责处理应用程序的数据逻辑和业务规则。
可以是一个或多个JavaBean对象它们封装了数据的属性和对这些数据的方法操作。
视图View
是用户的操作界面负责显示应用程序的用户界面。
对于Web应用来说视图可以是JSP页面、HTML页面或其他类型的用户界面。
视图只负责数据的显示和采集而不包含任何业务逻辑或数据处理。
控制器Controller
处理从视图层发送的请求并选取模型层的业务模型完成响应的业务实现。
控制器接收用户的输入并将其转化为对模型或视图的操作。
在Web应用中控制器通常是一个Servlet对象它接收用户的请求并根据请求来调用相应的模型和视图。
三、工作原理
用户通过视图层与应用程序进行交互发送请求。
控制器接收用户的请求并根据请求的类型和参数来选择相应的模型和视图。
控制器调用模型层来处理用户请求的数据逻辑和业务规则。
模型层处理完数据后将结果返回给控制器。
控制器选择相应的视图来显示处理结果。
视图从模型中获取数据并将其展示给用户。
四、优点
模块化MVC模式将应用程序划分为三个独立的模块提高了代码的可维护性和可扩展性。
灵活性由于视图、模型和控制器的相互独立可以方便地对其中任何一部分进行更改、替换或扩展而不会影响到其他部分。
可重用性模型可以被多个视图共享提高了代码的重用性。
分离关注点MVC模式将用户界面的设计、数据逻辑的处理和业务流程的控制分离开来使得开发人员可以更加专注于各自的领域。
五、应用场景
MVC模式广泛应用于Web应用程序的开发中特别是在Java Web开发中如使用Struts、Spring MVC等框架。此外MVC模式还可以应用于桌面应用程序、移动应用程序以及游戏开发等领域。
综上所述MVC模式是一种强大的软件设计模式它通过分离应用程序的输入、处理和输出使得代码更加清晰、可维护和可扩展。在实际应用中可以根据具体的应用场景和需求来选择是否使用MVC模式以及如何使用它。
30、分布式项目中客户端与服务端通信流程
一、建立连接
首先需要建立连接。这一步骤通常通过TCP/IP协议完成包括三次握手以确保双方能够稳定、可靠地通信。
二、发送请求
客户端准备请求
客户端根据业务需求准备要发送的请求数据。
请求数据通常包括请求头如方法、路径、协议版本等和请求体如请求参数、业务数据等。
序列化请求数据
为了在网络中传输请求数据需要被序列化为二进制格式。
序列化过程将数据结构转换为一系列字节以便通过网络发送。
发送请求
客户端通过已建立的连接将序列化后的请求数据发送给服务端。
这一步骤通常使用Socket编程或类似机制完成。
三、接收响应
服务端接收请求
服务端通过监听端口接收来自客户端的请求数据。
接收到的数据被反序列化为原始数据结构以便后续处理。
处理请求
服务端根据请求的内容和方法执行相应的业务逻辑。
这一步骤可能涉及数据库查询、计算、文件操作等。
准备响应
服务端将处理结果封装为响应数据。
响应数据通常包括状态码、响应头和响应体如处理结果、错误信息等。
序列化响应数据
为了将响应数据发送给客户端服务端需要将其序列化为二进制格式。
发送响应
服务端通过已建立的连接将序列化后的响应数据发送给客户端。
四、处理响应
客户端接收响应
客户端通过已建立的连接接收来自服务端的响应数据。
反序列化响应数据
客户端将接收到的二进制数据反序列化为原始数据结构。
处理响应结果
客户端根据响应数据的内容执行相应的业务逻辑。
这一步骤可能涉及更新UI、存储数据、处理错误等。
五、断开连接
在完成通信后客户端与服务端通常会断开连接以释放资源。这一步骤通常通过四次挥手等过程完成。
六、通信过程中的注意事项
异常处理
在通信过程中可能会出现网络故障、超时、数据错误等异常情况。
客户端和服务端需要设计合理的异常处理机制以确保系统的稳定性和可靠性。
安全性
分布式系统中的通信通常涉及敏感数据的传输。
因此需要采用加密、签名、身份验证等安全措施来保护通信过程和数据安全。
性能优化
为了提高通信效率可以采用压缩、缓存、异步通信等技术手段来优化通信过程。
综上所述分布式项目中客户端与服务端之间的通信流程是一个复杂而重要的环节。通过合理的设计和实现可以确保系统的稳定性、可靠性和高效性。
31、tcp相比于udp的区别
TCP传输控制协议和UDP用户数据报协议都是运输层第四层的协议
一、面向连接与无连接
TCP面向连接的协议。在数据传输之前需要通过三次握手建立连接确保通信双方准备就绪。数据传输结束后需四次挥手释放连接。这种面向连接的特性使得TCP能够提供可靠的通信服务。
UDP无连接的协议。在数据传输之前不需要建立连接直接发送数据包。每个UDP数据报都是独立的不会保存连接状态。这种无连接的特性使得UDP的传输效率更高但可能牺牲一定的可靠性。
二、可靠性
TCP提供可靠的数据传输服务。通过序列号、确认应答、重传机制等手段确保数据能够按照发送的顺序正确、无差错地到达接收方。如果数据包在传输过程中丢失或损坏TCP会触发重传机制直到数据成功传输为止。
UDP不保证数据的可靠传输。没有重传机制和顺序控制机制发出的数据包一旦发生丢失就无法恢复也无法保证接收方接收到数据的顺序一定是发送方发送的顺序。因此UDP的传输可靠性较低但传输速度更快。
三、拥塞控制
TCP具有拥塞控制机制。可以根据网络的拥塞程度动态调整发送数据的速率避免网络拥塞导致数据丢失和延迟增加。TCP的拥塞控制机制包括慢启动、拥塞避免、快重传和快恢复等算法。
UDP没有内置的拥塞控制机制。它会将所有数据发送到网络上无论网络状态如何。这可能导致网络拥塞但在某些实时性要求高的应用场景中UDP的这种特性反而能够提高其传输效率。
四、传输效率
TCP由于需要建立连接、进行确认应答和重传等操作TCP的传输效率相对较低。但这也保证了其数据传输的可靠性和稳定性。
UDP没有建立连接的过程也不需要确认应答和重传等操作因此UDP的传输效率更高。这使得UDP在实时性要求高的应用场景中更具优势。
五、使用场景
TCP适用于需要可靠数据传输的场景如文件传输、电子邮件等。在这些场景中数据的完整性和顺序性至关重要。
UDP适用于实时性要求高、但对数据可靠性要求不高的场景如实时视频、音频传输、在线游戏等。在这些场景中数据的实时性更为重要而少量数据的丢失或顺序错乱是可以容忍的。 5、为什么项目中采用的是tcp而不是udp 广泛应用
TCP被广泛使用在许多重要的应用层协议中如HTTP、HTTPS、FTP和SMTP等。这些协议的成熟和普遍使用使得TCP成为了互联网的基石。
相比之下UDP虽然在一些特定场景下如实时音视频传输、在线游戏和广播应用具有优势但在需要确保数据完整性和可靠性的场景中TCP仍然是首选。
开发者偏好和用户期望
许多开发者选择TCP是因为它更容易管理和调试连接的可靠性而不必担心手动处理重传和顺序问题。
在许多情况下用户更关心数据的可靠传输而不是速度。例如在电子邮件和文件下载中数据完整性和可靠性是关键因素。
综上所述虽然UDP在速度上具有优势但由于TCP在可靠性、流量控制、拥塞控制以及广泛应用方面的优势使得TCP在许多项目中仍然是主流选择。
32、RAII
RAIIResource Acquisition Is Initialization设计思想即“资源获取即初始化”是C中的一种管理资源、避免泄漏的惯用法。它的核心思想是将资源的管理如动态内存的分配和释放、文件句柄的打开和关闭等与对象的生命周期紧密绑定。具体来说资源在对象的构造函数中被分配或获取并初始化而在对象的析构函数中被释放或回收。这种方式确保了资源总是被正确管理即使在发生异常的情况下也能保证资源被正确释放从而避免了资源泄漏等问题。
一、资源分配与初始化
在对象的构造函数中通过适当的操作如new操作符、文件打开函数等分配或获取所需的资源。
将这些资源保存在对象的成员变量中以便在对象的其他成员函数中使用。
二、资源释放与析构
在对象的析构函数中通过适当的操作如delete操作符、文件关闭函数等释放或回收在构造函数中分配或获取的资源。
析构函数会在对象生命周期结束时被自动调用从而确保资源总是被正确释放。
三、异常安全性
由于RAII利用了C中对象自动调用析构函数的特性即使在发生异常的情况下也能保证资源被释放。
这使得使用RAII管理资源的代码更加健壮和可靠。
四、智能指针与RAII
智能指针是RAII思想的一个重要应用。智能指针类封装了常规指针并在内部使用引用计数或其他机制来自动管理内存的分配和释放。
当智能指针对象不再被使用时如超出作用域、被显式删除或被赋予新值等其所指向的内存会被自动释放从而避免了内存泄漏问题。
五、RAII的优势
简化资源管理通过将对资源的管理封装在对象中简化了资源管理的代码。
提高代码可读性使用RAII可以使代码更加清晰和易于理解因为资源的分配和释放都在对象的构造函数和析构函数中完成。
增强异常安全性即使在发生异常的情况下也能保证资源被正确释放。
六、RAII的应用场景
RAII广泛应用于C等支持对象导向编程的语言中用于管理各种资源如动态内存、文件句柄、网络连接等。
在实际开发中可以使用标准库中的智能指针如std::unique_ptr、std::shared_ptr等来方便地实现RAII。
综上所述RAII设计思想是一种非常有效的资源管理方式它通过将资源的管理与对象的生命周期紧密绑定确保了资源的正确分配和释放。
遵循RAII原则RAIIResource Acquisition Is Initialization是一种管理资源的技术它要求资源如内存、文件句柄等的获取和释放与对象的生命周期绑定。在对象的构造函数中分配资源在析构函数中释放资源。
33、分布式系统
C中实现分布式系统是一项复杂的任务通常涉及网络通信、数据同步、并发处理等多个方面。
1. 网络通信
分布式系统的核心在于节点之间的通信。
Boost.Asio一个跨平台的C库用于网络和底层I/O编程。支持TCP、UDP、串口等通信方式。
ZeroMQ一个高性能的异步消息库支持多种传输协议如TCP、IP多播、in-process等。
gRPC高性能、开源和通用的RPC框架
2. 数据同步和一致性
确保分布式系统中各个节点之间的数据一致性是关键。
分布式锁使用Redis或ZooKeeper实现分布式锁确保在多个节点之间对共享资源的互斥访问。
分布式事务如Google的Spanner和Percona的XtraDB Cluster等数据库系统提供了分布式事务的支持。
一致性算法如Paxos、Raft等用于确保分布式系统中的日志或配置数据的一致性。
3. 并发处理
C11及更高版本提供了多线程支持但处理分布式系统中的并发任务通常需要更复杂的机制。
C标准库中的线程和同步原语如std::thread、std::mutex、std::condition_variable等。
任务队列使用线程池和任务队列来管理并发任务如Intel TBBThreading Building Blocks或Boost.Thread。
4. 序列化与反序列化
在分布式系统中数据需要在不同节点之间传输通常需要进行序列化和反序列化。
Protocol BuffersGoogle开发的一种与语言无关、平台无关的可扩展机制用于序列化结构化数据。
Boost.Serialization一个C库用于序列化和反序列化C对象。
JSON/XML简单的文本格式便于调试和跨语言使用。
5. 集群管理与部署
管理和部署分布式系统的集群节点也是一个重要方面。
Kubernetes一个开源的容器编排和管理平台支持自动化部署、扩展和管理容器化应用程序。
Docker一个开源的应用容器引擎让开发者可以打包他们的应用以及依赖包到一个可移植的容器中然后发布到任何流行的Linux机器上也可以实现虚拟化。
34、jsoncpp
JsonCpp是一个用于解析和生成JSON数据的C库它提供了DOM风格的API来处理JSON数据可以将整个JSON文档加载到内存中然后像操作树结构一样操作它。
JsonCpp的序列化过程通常涉及创建一个Json::Value对象然后使用它的方法来构建JSON结构最后使用Json::StreamWriterBuilder将其转换为字符串。
二、工作模式
基于对象的模型将JSON结构映射到C对象适合于需要深度访问JSON树的情况。在这种模式下你可以像操作C对象一样操作JSON数据这使得数据访问和操作更加直观和方便。
基于值的模型更适用于简单的读写操作。在这种模式下JsonCpp内部实现了自动内存管理使用更为简便。你可以直接使用JsonCpp提供的Json::Value类型来存储和操作JSON数据。
三、使用方法
使用API
使用Json::Reader类来解析JSON字符串。
使用Json::Value类来表示和操作JSON数据。
使用Json::StreamWriterBuilder类来将Json::Value对象序列化为JSON字符串。
四、应用场景
配置文件读取JsonCpp可以方便地用于读取或保存应用程序的配置信息因为JSON格式清晰易读相比XML更简洁。
网络通信在网络API调用中JsonCpp常用于解析返回的JSON数据或者将C对象转换为JSON字符串发送给服务器。
数据分析在数据分析领域JsonCpp可以作为数据导入导出工具帮助你将JSON数据轻松转换为C中的结构化数据。
日志记录生成结构化的JSON格式日志便于后续进行自动化处理和分析。