cc++2.0

LSD2.0版本,有些地方重复,嵌入式软件c/c++,2023,10,30

C语言

关键字

typedef和define有什么区别

  1. 用法不同:typedef用来定义一种数据类型的别名,增强程序的可读性。define主要用来定义常量,以及书写复杂,使用频繁的宏。

  2. 执行时间不同:typedef是编译过程的一部分,有类型检查的功能。define是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。

  3. 作用域不同:typedef有作用域限定。define不受作用域约束,只要是在define声明后的引用都是正确的。

  4. 对指针的操作不同:typedef和define定义的指针时有很大的区别。

*注意:typedef定义是语句,因为句尾要加上分号。而define不是语句,千万不能在句尾加分号。*

c++,inline内联函数和宏定义区别:

  1. 内联函数在编译时展开,而宏在预编译时展开。

  2. 在编译的时候,内联函数直接被嵌入到目标代码中去,而宏只是一个简单的文本替换。

  3. 内联函数可以进行如类型检查、语句是否正确等编译功能,宏不具有这样的功能。

  4. 宏不是函数,而inline是函数。

宏与函数的区别

宏是一种预处理器指令,它在编译时会被替换为相应的代码,而函数是一段独立的可执行代码,需要在运行时调用才能执行。

运行速度上宏要比函数的运行速度快, 函数需要调用并且返回值这个过程,而宏却不需要。

对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?

c用宏定义,c++用inline

const是什么

  • 值不可修改:一旦常量被赋值后,其值将保持不变,不能再对其进行修改。

  • 作用域限制:常量的作用域通常被限制在声明时所在的作用域内部

  • 编译时确定:常量的值在编译时就已确定,并在运行时保持不变

const用来定义一个只读(不能修改其值)的变量或对象。主要优点:便于类型检查、同宏定义一样可以方便地进行参数的修改和调整、节省空间,避免不必要的内存分配、可为函数重载提供参考。


const 应用表现在:常量定义;类的成员变量为常量;类的成员函数为常量(表示该函数不能修改成员变量的值);函数传递的时候参数为常量。

*说明:const修饰函数参数,是一种编程规范的要求,便于阅读,一看即知这个参数不能被改变,实现时不易出错。*

const有什么用途?(请至少说明两种)

(1)可以定义 const 常量

(2)const 可以修饰函数的参数、返回值,甚至函数的定义体。被 const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

static 有什么作用

static 在 C 中主要用于定义全局静态变量、定义局部静态变量、定义静态函数。在 C++中新增了两种作用:定义静态成员变量、静态成员函数。

注意:因为 static 定义的变量分配在静态区,所以其定义的变量的默认值为 0,普通变量的默认值 为随机数,在定义指针变量时要特别注意。

static的作用

  • 限制变量和函数的作用域,防止被其他文件的代码访问。

  • 延长变量的生命周期,在程序刚开始运行时就完成初始化,直到程序运行结束才释放。

  • 修饰代静态码块:静态代码块只会在程序开始时执行一次。控制变量的初始化和生命周期。

  • 声明静态成员变量,使其属于类本身而不是对象,可以让多个对象共享同一份内存。

static全局变量与普通的全局变量有什么区别?

static全局变量只初使化一次,防止在其他文件单元中被引用;(c++)

全局变量能被其他文件的关键字extern所引用;

extern有什么作用

声明一个在其他文件中定义的外部变量或函数,提示编译器遇到此变量和函数时到其它模块中寻找其定义。允许在当前文件中使用这些外部变量或函数而不需要重新定义。

volatile、inline是什么含义,有什么用(使用场合)?

volatile: 强制编译器每次从内存中取得该变量的值,而不是从被优化后的寄存器/缓存中读取。

inline: inline关键字仅仅是建议编译器做内联展开处理,即是将函数直接嵌入调用程序的主体,省去了调用/返回指令。


内联函数使用的场合:对于简短的函数并且调用次数比较多的情况,适合使用内联函数。

在以下几种情况下需要使用 volatile 修饰符:

1). 并行设备的硬件寄存器(如:状态寄存器)

2). 一个中断服务子程序中会访问到的非自动变量

3). 多线程应用中被几个任务共享的变量,用于确保变量的值在多个线程之间可见。

volatile声明的作用

volatile声明的变量是指可能会被意想不到地改变的变量,这样编译器就不会轻易优化该变量。

它主要用于多线程编程中,用来保证共享变量的内存可见性。(注:指针也可用volatile)

三个常见场景

  • 多线程中的共享变量

  • 中断程序中访问到的非自动变量

  • 并行设备的硬件寄存器

函数

sizeof 操作符/关键字和strlen库函数的区别

  1. sizeof操作符计算的是数据类型或变量所占用的内存空间大小,包括结尾的空字符。

  2. strlen: 计算字符串的长度,不包括结尾的空字符。

  3. 编译器在编译时就计算出了 sizeof 的结果。而 strlen 函数必须在运行时才能计算出来。


char str[] = “world”; cout<<sizeof(str)<<”:”; //6

void *pp = malloc(10); cout<<sizeof(pp)<<endl;//8


int a = 10; char *str = "hello world";

// sizeof 计算变量或类型所占内存大小 printf("sizeof(a) = %d\n", sizeof(a)); // 4 printf("sizeof(str) = %d\n", sizeof(str)); // 4

// strlen 计算字符串中字符的个数 printf("strlen(str) = %d\n", strlen(str)); // 12

malloc函数和new操作符有啥区别

  1. 他们的作用都是申请内存空间

  2. 使用new申请内存分配时不需要指定内存的大小,编译器会根据类型自行计算;new,delete是操作符,使用范围在c++;

  3. 使用malloc申请内存分配时则需要指出所需内存的大小。malloc,free是函数,使用范围是c;

  4. new分配内存调用对象的构造函数,delete释放内存调用对象的析构函数;

  5. malloc仅分配内存,free仅释放内存,并不执行构造和析构函数;

  6. new,delete返回的是某种类型的指针(更具体了->进阶);malloc,free返回的是void型指针;

strcpy,sprintf,mencpy的区别

这些都用于拷贝,针对不同的对象,选择合适的函数实现拷贝功能

1操作对象的不同,strcpy的两个操作对象都是字符串,sprintf的操作员对象可以是多种数据类型,目的操作对象是字符串,memcpy的2个对象是两个任意可操作的内存地址,并不限于何种数据类型。


执行效率不同mencpy最高,strcpy其次,sprintf最低

  • strcpy主要实现字符串变量间的拷贝

  • sprintf主要实现其他数据类型到字符串的转化

  • memcpy是内存块间的拷贝

变量

全局变量和局部变量有什么区别?

全局变量:

  • 在函数外部声明的变量,整个程序都可以访问。

  • 声明时会被默认初始化,可以在任何函数中使用。

  • 生命周期长,整个程序执行期间都存在。

  • 全局变量存储在全局数据区(data)中

局部变量:

  • 在函数内部或代码块内部声明的变量,只能在所属的函数或代码块中访问。

  • 声明时没有默认初始化,需要手动赋值才能使用。

  • 生命周期短,只在所属的函数或代码块的执行期间存在。

  • 局部变量存储在栈区(stack)


保存位置不同

全局变量保存在内存的全局存储区中,占用静态的存储单元;局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。


作用域不同

局部变量的作用域仅限于定义它的函数内,全局变量的作用域是整个程序。

生命周期不同

局部变量在函数调用时分配,函数返回时释放。全局变量在程序开始时分配,程序结束时释放。

可见性不同

局部变量仅在定义它的函数内可见。全局变量在程序的任何位置可见。

static全局变量与普通的全局变量有什么区别?

static全局变量只初使化一次,防止在其他文件单元中被引用;

全局变量能被其他文件的关键字extern所引用;

全局变量和局部变量分别保存在哪个位置(栈堆),函数内static定义的变量是存放在哪里的?栈堆有什么区别?

全局变量在静态存储区/全局数据区

局部变量在栈区

static定义的变量也是静态存储区

指针

指针是一个地址,而指针变量是存放变量的地址的变量。

指针是某种数据类型的变量,存储指向变量的地址;

指针的作用是

指针的作用是用来存储内存地址,通过指针可以访问和修改内存中的数据(指向变量的值)。


指针在编程中非常有用,它可以用于动态内存分配、传递函数参数、实现数据结构等。但是指针也需要谨慎使用,因为如果指针没有正确初始化或者指向了无效的内存地址,就可能导致程序崩溃或者出现未定义的行为。因此,在使用指针时需要注意对指针进行正确的初始化和操作。

如何避免野指针

产生原因:

  1. 指针变量声明时没有被初始化,解决办法是声明时初始化,可以是指向具体的地址,可以是指向NULL;

  2. 指针p被释放内存后(free或delete),没有置为NULL,解决办法是指针指向的内存空间,被释放后指针应该指向NULL;

  3. 指针操作超越了变量的作用范围,解决办法是在变量的作用域结束前,释放掉变量的地址空间并且让指针指向NULL;

引用和指针有啥区别

  1. 引用必须被初始化,却不分配存储空间;指针声明时可以不被初始化,在初始化的时候需要分配存储空间。

  2. 引用初始化后不能被改变,指针可以改变所指向的对象。

  3. 不存在指向null的引用,但存在指向null的指针。

  4. 作为参数传递时,指针需要被解引用*才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象;


2.使用 sizeof 计算一个指针的大小是 32位系统下大小为4,64位系统下大小为8,而引用则是被引用对象的大小;

5.可以有 const 指针,但是没有 const 引用;

7.指针可以有多级指针(**p),而引用止于一级;

简述指针常量与常量指针区别

指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其他地方不能改变。常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值。

指针常量强调的是指针的不可改变性,而常量指针强调的是指针对其所指对象的不可改变性。

*注意:无论是指针常量还是常量指针,其最大的用途就是作为函数的形式参数,保证实参在被调用函数中的不可改变特性。*


可以将指针常量记忆为“指针不变,常量可变”,表示指针本身不可变,但所指向的值可变;将常量指针记忆为“常量不变,指针可变”,表示所指向的值不可变,但指针本身可变。

数组名和指针的区别

数组名:

  • 是一个常量指针,指向数组的首元素。

  • 大小固定为整个数组的大小。

  • 无法被改变或重新赋值。

  • 无法进行指针运算。

指针:

  • 是一个变量,存储一个内存地址。

  • 大小固定为指针类型的大小。

  • 可以指向任意类型的对象。

  • 可以被改变或重新赋值。

  • 可以进行指针运算,如加法、减法等。


请写出以下代码的打印结果:

void main(void)

{

charstr[13]="Hello world!";

char*pStr="Hello world!";

cout<<sizeof(str)<<endl; //13

cout<<sizeof(pStr)<<endl;//4

cout<<strlen(str)<<endl; //12

cout<<strlen(pStr)<<endl;//12

return;

}

注意:一定要记得数组名并不是真正意义上的指针,它的内涵要比指针丰富的多。但是当数组名当 做参数传递给函数后,其失去原来的含义,变作普通的指针。另外要注意 sizeof 不是函数,只是操作符。

内存分配

栈堆区别Heap与stack的差别。

1、空间分配区别;2、缓存方式区别;3、数据结构区别

【标准答案】Heap是堆,stack是栈。 Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。 Stack空间有限,Heap是很大的自由存储区 C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。 程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行

堆栈溢出一般是由什么原因导致的?

没有回收垃圾资源。

编码实现某一变量某位清零或置1(笔试题)

#define BIT2(0x1<<2)

static int a;

void set_bit2_1(void){ // 设置a的bit2为1,|

a |=BIT2;

}

void set_bit2_0(void){ // 清除a的bit2为0,解锁~,&

a &=~BIT2;

}

int main(int argc, char ** argv)函数中,参数argc和argv分别代表什么意思?

在C语言中,主函数int main(int argc, char **argv)用来作为程序的入口,argcargv是其参数。

  • argc是整型参数,表示命令行参数的个数。它记录了程序在运行时附带的命令行参数的数量,至少为1,因为程序自身的名称也算一个参数。

  • argv是字符指针数组,用来存储命令行参数的字符串。每个元素指向一个以null结尾的字符串,表示一个命令行参数。

  • argv[0]指向程序的名称,argv[1]指向第一个参数,以此类推,argv[argc-1]指向最后一个参数。


举个例子,假设我们在命令行中执行以下命令:

./program arg1 arg2 arg3

那么argc的值为4,argv的值如下所示:

argv[0] = "./program"
argv[1] = "arg1"
argv[2] = "arg2"
argv[3] = "arg3"
argv[4] = NULL

编译.c

要编译一个C语言源代码文件(.c文件),可以按照以下步骤进行:

  1. 确保已经安装了C编译器:在终端或命令行界面中输入gcc --version,如果显示了gcc的版本信息,则表示已经安装了C编译器。如果没有安装,可以根据操作系统的不同,安装相应的C编译器,例如在Ubuntu上可以使用sudo apt install build-essential命令安装gcc。

  2. 在文本编辑器中编写C源代码:使用文本编辑器(例如Notepad++、Sublime Text、Visual Studio Code等)打开一个空白文件,并编写C语言源代码。例如,创建一个名为hello.c的文件,并在其中编写以下代码:

#include <stdio.h>
​
int main() {
    printf("Hello, World!\n");
    return 0;
}
  1. 保存源代码文件:将编写好的C源代码保存为.c文件,例如保存为hello.c

  2. 打开终端或命令行界面:打开终端或命令行界面,并进入保存了源代码文件的目录。可以使用cd命令切换目录。

  3. 执行编译命令:在终端或命令行界面中执行以下命令来编译C源代码文件:

gcc -o hello hello.c

其中,gcc是C编译器的命令,-o hello指定编译后生成的可执行文件的名称为hellohello.c是要编译的源代码文件。

  1. 检查编译结果:如果编译成功,终端或命令行界面中不会显示错误信息。如果有错误信息,需要检查源代码是否存在语法错误或其他问题。

  2. 运行可执行文件:在终端或命令行界面中执行以下命令来运行生成的可执行文件:

./hello

如果一切正常,应该会在终端或命令行界面中看到输出结果Hello, World!

注意:以上步骤是在Linux或类Unix系统中的操作,如果是在Windows系统中,可以使用MinGW或Cygwin等工具来提供类似的编译环境和命令。

内存分布模型

1

上图是比较经典的内存分布的模型图,下面将对上图中的不同的组成部分进行详细解释(从低地址到高地址)注:必须知道组成结构但是具体的含义只需要理解。

  1. 代码段:存放程序的机器指令(即二进制代码)。通常是只读的,因为程序的指令在执行过程中不应该被修改。

  2. 数据段:存放已初始化的全局变量和静态变量。这些变量在程序开始运行时已经赋予了初始值。

  3. BSS 段:存放未初始化的全局变量和静态变量。它们在程序开始运行时会自动初始化为0或者空指针。

  4. 堆区:动态分配的内存空间,用于存放程序运行时动态申请的内存。(程序员可以通过函数(如malloc、calloc等)或者操作系统提供的接口来申请和释放堆内存,堆从低地址向高地址增长。)

  5. 栈区存放函数的局部变量、函数参数值以及函数调用和返回时的相关信息。栈区是按照“先进后出”的原则进行管理,内存的分配和释放是自动进行的,栈从高地址向低地址增长。是一块连续的空间。

  6. 共享区:也称为文件映射或共享内存,用于实现不同进程之间的内存共享。

调用动态/静态链接库的两种方式。

第一种是静态链接方式,在这种方式下,静态链接库中的所有数据代码都将拷贝到调用程序的代码空间中去,此时它和调用程序本身的函数没有什么区别;

第二种是动态链接方式,在这种方式下,动态链接库中的数据代码是在需要的时候才拷贝到内存中去的;

排序算法

冒泡排序

  1. 冒泡排序是一种简单的排序算法,它通过重复地遍历要排序的列表,比较相邻元素并将它们按照升序或降序进行交换。

  2. 冒泡排序的基本思想是外层循环控制遍历次数,内层循环用于比较相邻元素并进行交换。通过多次遍历,最大的元素逐渐“冒泡”到列表的末尾。


  3. 冒泡排序的时间复杂度为O(n^2),其中n是待排序列表的长度。尽管冒泡排序算法简单,但对于大规模的数据排序效率较低,通常在实际应用中不推荐使用。

快速排序

在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。


快速排序是一种常用的排序算法,它通过将一个列表分割成较小的子列表来实现排序。具体步骤如下:

  1. 选择一个基准元素:从列表中选择一个元素作为基准(通常选择第一个或最后一个元素)。

  2. 分割:将列表中小于基准元素的元素移到基准元素的左边,将大于基准元素的元素移到基准元素的右边。此过程称为分割。

  3. 递归排序子列表:对基准元素左边的子列表和右边的子列表重复上述步骤,直到子列表的大小为1或为空。

  4. 合并:将排序好的子列表合并起来,得到最终的排序结果。

快速排序的关键在于分割步骤,它通过不断比较和交换元素的方式将列表分成两个部分。在每次分割之后,基准元素会被放置在最终排序位置的正确位置上。这样,在递归排序子列表时,只需要考虑基准元素左边和右边的子列表。

快速排序的时间复杂度为O(nlogn),其中n是待排序列表的长度。它是一种高效的排序算法,在实际应用中被广泛使用。

选择排序

选择排序是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

二分搜索算法

二分查找算法(Binary Search)是一种高效的查找算法,用于在有序数组或有序列表中查找指定元素的位置。

二分查找的基本思想是将查找区间不断缩小为原来的一半,直到找到目标元素或确定目标元素不存在为止。具体步骤如下:

  1. 初始化左边界left为0,右边界right为数组长度减1。

  2. 当左边界小于等于右边界时,执行以下步骤: a. 计算中间位置mid,即mid = (left + right) / 2。 b. 如果中间位置的元素等于目标元素,则返回mid。 c. 如果中间位置的元素大于目标元素,则将右边界更新为mid-1。 d. 如果中间位置的元素小于目标元素,则将左边界更新为mid+1。

  3. 如果循环结束时仍未找到目标元素,则返回-1表示目标元素不存在。

二分查找的时间复杂度为O(log n),其中n为数组或列表的长度。由于每次查找都将查找区间缩小为原来的一半,因此二分查找的效率非常高。然而,二分查找的前提是数组或列表必须是有序的,否则无法使用该算法进行查找。

数据结构

数据结构涉及到如何组织和管理数据。常见的数据结构包括数组、链表、栈、队列、树、图等。每种数据结构都有其特定的特点和适用场景。

链表

  1. 首先,定义链表的节点结构。节点结构通常包含两个部分:数据和指向下一个节点的指针。

  2. 创建链表的头节点。头节点是链表的起始节点,它不包含数据,只有指向第一个真实节点的指针。

  3. 插入节点。要在链表中插入一个新节点,需要先创建一个新节点,并将它的数据赋值为要插入的数据。然后,将新节点的指针指向原来的下一个节点,再将前一个节点的指针指向新节点。

  4. 删除节点。要从链表中删除一个节点,需要找到要删除的节点的前一个节点,将前一个节点的指针指向要删除节点的下一个节点,然后释放要删除的节点的内存空间。

  5. 遍历链表。从链表的头节点开始,依次访问每个节点的数据,直到链表的尾节点。

  6. 查找节点。从链表的头节点开始,依次比较每个节点的数据,直到找到匹配的节点或遍历到链表的尾节点。

  7. 获取链表长度。从链表的头节点开始,依次遍历每个节点,统计节点的个数。

需要注意的是,在链表的插入和删除操作中,需要特别注意指针的处理,以避免出现空指针或内存泄漏等问题。

具体的链表实现方式可能因编程语言而异,你可以根据自己的需要选择合适的编程语言,并查阅相关的文档和教程来了解链表的具体实现细节。

栈和队列的区别

栈和队列的主要区别在于存储方式和访问,操作方式。栈是后进先出的,只能在栈顶进行操作;队列是先进先出的,可以在队首和队尾进行操作。

操作:

  • 栈的主要操作是压栈(push)和弹栈(pop),即向栈顶添加元素和从栈顶移除元素。

  • 队列的主要操作是入队(enqueue)和出队(dequeue),即向队尾添加元素和从队首移除元素。

  • 杀头

访问方式:

  • 栈只能访问并操作栈顶的元素,不能直接访问其他位置的元素。

  • 队列可以访问和操作队首和队尾的元素。

    1都是线形存储结构

    2插入和删除数据的操作方式不同;队列先进先出;栈先进后出

链表和数组的区别

  1. 数组是一片连续的空间,声明时要确定大小;

  2. 链表是一片不需连续的动态空间,长度可变,由结点组成,每个结点要保存数据和指向下一个节点指针(这个节点是由数据域和指针域组成的);

  3. 数组的线形查找速度块,查找操作直接使用地址偏移;链表需要按顺序检索结点,效率低;

  4. 链表可快速插入和删除结点,数组则可能需要大量的数据移动;

  5. 链表不存在越界问题;数组存在越界问题;

  6. 总的来说,数组便于查询,链表便于插入或删除。

有一个单向链表,如何找到其中的中间项(描述思路即可)。

1定义2个指针,快慢指针;

判断链表为空?是则返回null;

2快慢指针都指向头结点;

3使用循环,结束条件是快指针指向空或快指针的下一个节点为空;

4循环体内,当慢指针移动一个节点时,快指针移动2个节点

当循环结束时,返回慢指针指向的节点;

简述TCP与UDP协议的区别。

  1. TCP 是面向连接的,UDP 是无连接的,即发送数据前不需要先建立链接。

  2. TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,不保证可靠交付。

  3. TCP 是面向字节流,UDP 面向报文,并且没有拥塞控制,网络出现拥塞不会使得发送速率降低

  4. TCP 只能是 1 对 1 的,UDP 支持 1 对 1,1 对多。

三次握手

  1. 客户端发送一个同步包给服务器,请求建立连接。

  2. 服务器收到同步包后,回复一个同步和确认包给客户端,表示确认收到请求,并同意建立连接。

  3. 客户端收到服务器的同步和确认包后,再发送一个确认包给服务器,表示收到确认,并告知服务器可以开始传输数据。


通过这三次通信,客户端和服务器建立了可靠的连接,可以进行数据传输。这种三次握手的机制可以确保连接的可靠性和稳定性。

四次挥手

xx

变量a的定义(笔试题)

一个指向指针的的指针,它指向的指针是指向一个整型数

int **a;

一个指向有10个整型数数组的指针

Xint (*a[10]);X

int (*a)[10];

一个指向函数的指针,该函数有一个整型参数并返回一个整型数

int (*a)(int)

—个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

int (*a[10])(int)

常见的变量定义

  • int a;:定义了一个变量 a,它的类型是 int

  • int *a;:定义了一个指针 a,它指向 int 类型的变量。

  • int **a;:定义了一个指针 a,它指向一个指向 int 类型的指针。

  • int a[10];:定义了一个数组 a,该数组有 10 个元素,每个元素是 int 类型。

  • int *a[10];:定义了一个数组 a,该数组有 10 个元素,每个元素是 int 类型的指针。

  • int (*a)[10];:定义了一个指针 a,该指针指向一个数组,该数组有 10 个元素,每个元素是 int 类型。

  • int (*a)(int);:定义了一个指针 a,该指针指向一个参数是 int,返回值是 int 的函数。

  • int (*a[10])(int);:定义了一个数组 a,该数组的元素是一个指向参数是 int,返回值是 int 的函数指针。

C++

1.重载:一个函数名多用

2.引用:为一个变量起别名

3.内置(inline)函数:不执行调用,而是在编译时直接把代码接入到调用点处,节省运行时 间,但增加了目标程序长度。 4.多态性:由继承产生的的派生类,基对象对于同一消息,会做出不同的反应。

5.struct 定义类和 class 定义类的区别:前者默认 public,后者默认 private

6.构造函数:用来处理对象的初始化,名字必须与类名同名,不具有任何类型,不返回任何 值。在建立类对象时会自动调用构造函数。

7.虚基类:在继承间接共同基类时只保留一份成员。

8.虚函数:在基类声明函数是虚拟的,并不是实际存在德尔函数,然后在派生类中才正式定 义此函数。

9.纯虚函数有以下特点:纯虚函数仅仅是用来为基类的派生类中的函数保留位置;纯虚函数 在基类中没有定义,它们被初始化为 0;当虚函数变成纯虚函数时,任何派生类都必须给出 它自己的定义。否则编译出错。

10.函数模板:建立一个通用的函数,其函数类型和形参类型不具体指定,用一个虚拟的类 型来代替

11.面向对象程序设计有四个主要特点:抽象,封装,继承和多态性。

12.静态成员函数与非静态成员函数的区别:前者没有 this 指针,后者有 this 指针。 静态成员函数只要用来访问静态数据成员,而不访问非静态成员。

13.转换构造函数:讲一个其他类型的数据转换成一个类的对象,只有一个形参。

14.类型转换函数:将一个类的对象转换成另一个类型的数据,只能作为成员函数。有 operator 关键字,无形参

C++面向对象的三大特性是封装、继承和多态。

  1. 封装:封装是指将数据(属性)和操作数据的函数(方法)封装在一起,形成一个类。通过封装,可以将数据和方法进行隐藏,只对外提供必要(获取数据)的接口。这样可以保护数据的安全,并且提高代码的可维护性和复用性。

  2. 继承:继承是指一个类可以派生出子类,子类可以继承父类的属性和方法。通过继承,子类可以重用父类的代码,同时还可以添加新的属性和方法,实现代码的扩展和复用。

  3. 多态:多态是指同一个方法可以在不同的对象上具有不同的行为。多态可以通过继承和接口实现。在C++中,动态多态可以通过虚函数实现。通过多态,可以实现同一个方法在不同的对象上表现出不同的行为。这样可以提高代码的灵活性和可扩展性。

拷贝构造函数和赋值运算符的区别

1,拷贝构造函数产生新的类对象,而赋值运算符不能。

2,拷贝构造函数产生新的类对象,在初始化前不用校检源对象和新建的对象是否相同;复制运算符需要这个操作,另外需要将原来的对象中的内存分配给释放掉。

成员函数的重写(覆盖),重载和隐藏的区别

范围:重写/被重写的函数都在两个类中;重载/被重载的函数都在1个类中。

参数:重写/被重写的函数的参数列表一定相同;重载/被重载的函数参数列表一定不同。

virtual 的区别:重写的基类中被重写的函数必须要有 virtual 修饰,而重载函数和被重载函数可以被virtual 修饰,也可以没有。


覆盖和重载的方法都可以不同名

final修饰的方法不能被覆盖

函数的三种传参方式

  1. 值传递,将参数的值复制一份到函数内部的参数,在函数内部,对形参的修改无法修改实参的内容。

  2. 地址/指针传递,通过指针传递参数,函数接收指针作为形参,可以通过指针来访问和修改原始参数的值。

  3. 引用传递,引用给实参取一个别名,通过引用传递参数,使得函数内部可以直接访问和修改原始参数的值。节省内存空间,提高操作效率,危险操作。


如果函数内部不需要修改实参的值,可以使用值传递;

如果需要修改实参的值,可以使用地址传递或引用传递。

在引用传递的情况下,如果希望函数内部不修改实参的值,可以使用const关键字来声明形参,表示形参是只读的。

class 和 struct 的区别?

在C++中,struct与class唯一的区别就是在于 默认的访问权限不同。

区别:

struct 默认权限为公共权限

class 默认权限为私有权限


除了默认的成员访问权限之外,class 和 struct 在其他方面基本上是相同的。它们都可以包含成员变量和成员函数,并且都可以使用继承、多态等面向对象的特性。

因此,一般来说,如果定义的是一种包含私有成员的复杂数据类型,通常会选择使用 class。

而如果定义的是一种简单的数据结构,只包含公共成员,并且没有复杂的逻辑和功能,通常会选择使用 struct。

常引用有什么作用

常引用的引入主要是为了避免使用变量的引用时,在不知情的情况下改变变量的值。

常引用主要用于定义一个普通变量的只读属性的别名、作为函数的传入形参,避免实参在调用函数中被意外的改变。

*说明:很多情况下,需要用常引用做形参,被引用对象等效于常对象,不能在函数中改变实参的值,这样的好处是有较高的**易读性和较小的出错率**。*

构造函数能否为虚函数

构造函数不能是虚函数。而且不能在构造函数中调用虚函数,因为那样实际执行的是父类的对应函数,因为自己还没有构造好。

析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。析构函数也可以是纯虚函数,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。

*说明:虚函数的动态绑定特性是实现重载的关键技术,**动态绑定根据实际的调用情况查询相应类的虚函数表**,调用相应的虚函数。*

虚函数是什么

虚函数是C++中的一个重要概念,它允许在基类中定义一个函数,并在派生类中进行重写。通过使用虚函数,可以实现多态性,即在运行时根据对象的实际类型来调用相应的函数。

在C++中,将函数声明为虚函数需要在函数声明前面加上关键字"virtual"。

函数重载

  • 实现方法:

    • 1.函数名相同

    • 2.函数的参数不同 (个数不同,类型不同,位置不同)

    • 3.返回值类型不同无法实现函数重载

构造函数

  • 构造函数 作用:初始化类中的数据成员(属性) 特点: 1.函数没有返回值 2.函数名与类名相同 3.函数无需调用,在创建类的对象时自动调用 4.构造函数必须要设计在公共区 (PS:放入私有区,将无法创建对象) 5.构造函数也可以重载

析构函数

构造函数会在对象创建时自动调用,而析构函数会在对象销毁时自动调用。

析构函数名与类名不同不是析构函数的特征,而是析构函数的命名规则。为了便于区分。

析构函数的名称与类名相同,但是前面要加上一个波浪号(~)作为标识符,例如 ~MyClass()。

析构函数没有返回值,也不需要参数,

它会在对象销毁时自动调用,无法手动调用。


析构函数的特征是:

  • 在对象被销毁时自动调用,用于释放对象占用的内存和资源。

  • 只有一个,不能被重载。

  • 没有返回值,也不能带有任何参数。

重载-覆盖(重写)

重载是在同一个作用域中通过函数名和参数列表的不同来定义多个函数,而覆盖是子类重新定义父类中的虚函数,使得子类对象在调用该函数时执行子类中的实现。

进程 线程

列出你知道的Linux和Windows线程同步方法。

在Linux中线程同步的常见方法: 在WIN32中,同步机制主要有以下几种:

(1)互斥锁; (1)事件(Event);

(2)条件变量; (2)信号量(semaphore);

(3)读写锁; (3)互斥量(mutex);

(4)信号量。 (4)临界区(Critical section)。

进程间通信的方式有哪些?

管道、消息队列 、共享内存、信号、信号量、套接字

简述一下交叉编译器的作用。

在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码,这个编译过程就叫交叉编译.简单地说,就是在一个平台上生成另一个平台上的可执行代码.

QT

Qt的信号和槽机制是什么?

信号和槽是Qt框架中用于对象间通信的机制。信号是对象发出的事件,槽是对象对信号做出的响应。通过连接信号和槽,可以实现对象之间的通信和交互。

Qt的信号和槽机制是通过元对象系统实现的。在编译时,Qt会为每个QObject派生类生成一个元对象,其中包含了类的信号和槽的信息。通过元对象系统,可以在运行时动态连接信号和槽。

硬件

SPI

串行外设接口,是一种用于芯片通信的同步串行通信接口规范,主要应用于单片机系统中。SPI接口是一种高速、全双工、同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片管脚,同时为PCB的布局上节省空间,提供方便。

iic

设计出来的一种简单、双向、二线制总线标准。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。主机启动总线,并产生时钟用于传送数据,此时任何接收数据的器件均被认为是从机。

I2C使用两根信号线进行通信:一根时钟线SCL,一根数据线SDA

iic的主要由哪几个根线?spi呢?有什么用

IIC主要由两个根线组成,分别是SCL(Serial Clock Line,串行时钟线)和SDA(Serial Data Line,串行数据线)。SCL用于同步数据传输的时钟信号,而SDA用于传输数据。

SPI主要由四个根线组成,分别是SCLK(Serial Clock,串行时钟线)、MOSI(Master Output, Slave Input,主设备输出、从设备输入线)、MISO(Master Input, Slave Output,主设备输入、从设备输出线)和SS(Slave Select,从设备选择线)。SCLK用于同步数据传输的时钟信号,MOSI用于主设备向从设备传输数据,MISO用于从设备向主设备传输数据,而SS用于选择特定的从设备进行通信。

IIC和SPI都是用于在集成电路之间进行通信的串行通信接口。它们的主要用途是在不同的芯片或模块之间传输数据,例如在微控制器和各种外部设备之间进行通信。


  • I2C:I2C是一种同步的半双工通信协议,使用两根线路(SDA和SCL)进行数据传输。其中,SDA线用于传输数据,SCL线用于传输时钟信号。

  • SPI:SPI是一种同步的全双工通信协议,使用多根线路(SCLK、MOSI、MISO和SS)进行数据传输。其中,SCLK线用于传输时钟信号,MOSI线用于主设备向从设备传输数据,MISO线用于从设备向主设备传输数据,SS线用于选择特定的从设备进行通信。

GPIO

GPIO(General Purpose Input/Output)是通用输入输出端口,它可以用于连接和控制各种外部设备和传感器。GPIO可以通过编程方式配置为输入或输出,用于读取外部信号或控制外部设备。

GPIO的主要用途包括:

  1. 输入:可以连接各种传感器,如温度传感器、光线传感器等,用于读取外部环境的状态或数据。

  2. 输出:可以连接各种执行器,如LED灯、继电器等,用于控制外部设备的开关状态。

  3. 中断:可以通过GPIO引脚的中断功能,实现对外部事件的响应,例如按键的按下或松开。

  4. 通信:可以通过GPIO实现简单的串行通信,如通过GPIO模拟I2C或SPI接口进行通信。

连接方式:

  • GPIO:通过单个引脚连接到外部设备或传感器。可以通过编程方式控制引脚的电平状态来实现输入和输出功能。

  • GPIO:通信方式相对简单,可以通过编程方式控制引脚的电平状态来实现简单的数据传输。

单片机的工作原理

单片机是一种集成电路芯片,内部集成了处理器、存储器、输入输出端口等功能模块。其工作原理可以简单描述如下:

  1. 时钟信号:单片机通过外部提供的时钟信号来驱动其内部的时序逻辑,使得各个功能模块按照特定的时序进行工作。

  2. 处理器:单片机的处理器是其核心部分,负责执行各种指令和算术逻辑操作。它根据时钟信号的驱动,从存储器中读取指令,解码并执行,完成特定的功能。

  3. 存储器:单片机内部包含了各种存储器,如程序存储器(ROM)用于存储程序代码,数据存储器(RAM)用于存储数据等。处理器可以通过地址总线和数据总线对存储器进行读写操作。

  4. 输入输出端口:单片机通过输入输出端口与外部设备进行数据交互。输入端口用于接收外部设备的输入信号,输出端口用于向外部设备输出数据。

  5. 中断系统:单片机的中断系统可以在特定条件下中断当前的程序执行,转而执行中断服务程序。通过中断系统,单片机可以实现实时响应外部事件的功能。

  6. 时序逻辑:单片机内部的各个功能模块都需要按照特定的时序进行工作,时序逻辑模块负责生成和控制这些时序信号,保证各个模块的协调工作。

总的来说,单片机的工作原理是通过时钟信号驱动处理器,处理器根据指令从存储器中读取并执行操作,通过输入输出端口与外部设备进行数据交互,同时通过中断系统实现对外部事件的实时响应。

shell

shell的使用

是的,我会使用Shell。Shell是一种命令行解释器,可以用于与操作系统进行交互和执行命令。

以下是一些常见的Shell使用例子:

  1. 列出当前目录下的文件和文件夹:

ls
  1. 切换到指定目录:

cd /path/to/directory
  1. 创建一个新的文件夹:

mkdir new_directory
  1. 删除文件或文件夹:

rm file.txt
rm -r directory
  1. 复制文件或文件夹:

cp file.txt new_file.txt
cp -r directory new_directory
  1. 移动或重命名文件或文件夹:

mv file.txt new_directory/
mv file.txt new_name.txt
  1. 查看文件内容:

cat file.txt
  1. 在文件中搜索指定的模式:

grep "pattern" file.txt
  1. 修改文件或文件夹的权限:

chmod 755 file.txt
  1. 显示当前正在运行的进程:

ps
  1. 终止指定的进程:

kill PID
  1. 在终端中输出文本:

echo "Hello, World!"

git

Git 五分钟教程 | 菜鸟教程

基于 TCP-服务器

  1. 创建一个 socket,用函数 socket()

  2. 绑定 IP 地址、端口等信息到 socket 上,用函 数 bind()

  3. 设置允许的最大连接数,用函数 listen()

  4. 接收客户端上来的连接,用函数 accept()

  5. 收发数据,用函数 send()和 recv(),或者 read()和 write()

  6. 关闭网络连接

静态变量和动态变量有什么区别?

静态变量和动态变量是 C 语言中的两种变量,它们在存储位置、生命周期和初始化方式方面存在着以下区别:

属性静态变量动态变量
存储位置静态存储区动态存储区
生命周期程序结束时释放函数调用时分配,函数返回时释放
初始化方式可以不初始化,也可以初始化必须初始化

其他

在C语言中有哪些标准库函数,列出函数名?并描述Abs函数。

<stdef.h>标准定义

<ctype.h>字符处理函数

<stdlib.h>功能函数

<errno.h>错误函数

<stdio.h>输入输出函数

<stdlib.h>字符串函数

<math.h>数学函数

Abs函数是一个数学函数,用于计算一个数的绝对值。

头文件中的 ifndef/define/endif 干什么用?

他们是c,c++的预处理操作符,用于编译时确保多个源文件包含同一个头文件只被包含一次,防止重复包含;

确保头文件只被包含一次

防止该头文件被重复引用。

#include <filename.h> 和 #include “filename.h” 有什么区别?

【标准答案】

对于#include <filename.h> ,编译器从标准库路径开始搜索 filename.h ;

对于#include “filename.h” ,编译器从用户的工作路径开始搜索 filename.h ,找不到再从标准库路径找;

大端存储和小端存储

小端存储是指将数据的低位字节存储/在内存的低地址处,高位字节存储在高地址处;

而大端存储则是将数据的高位字节存储在内存的低地址处,低位字节存储在高地址处。

应用

大端存储和小端存储在不同的计算机系统中都有应用。例如,大多数 x86 处理器采用小端存储,而大多数 ARM 处理器采用大端存储。在网络传输中,通常采用大端存储,以便不同计算机系统之间可以兼容。需要进行字节序转换。


/由于char指针只能访问地址中的一个字节,因此*c将给出x的最低有效字节。

如果系统是小端(即最低有效字节存储在最小的地址),即*c将等于0x10。

如果系统是大端(即最低有效字节存储在最大的地址),即c将等于0x76/

死锁的必要条件是

A.互斥

B.请求又保持

C.非剥夺

D.循环等待

列举常用的无线通信技术(能举例多少举例多少),并描述他们的优缺点。

wifi

蓝牙

NFC

GPS

LTE

一些最重要的 SQL 命令

  • SELECT - 从数据库中提取数据

    SELECT name,country FROM Websites; //从 "Websites" 表中选取 "name" 和 "country" 列:

    SELECT * FROM Websites;

    SELECT DISTINCT country FROM Websites; //仅从 "Websites" 表的 "country" 列中选取唯一不同的值,也就是去掉 "country" 列重复值:

  • UPDATE - 更新数据库中的数据

    UPDATE Websites SET alexa='5000', country='USA' WHERE name='菜鸟教程';

  • DELETE - 从数据库中删除数据

  • INSERT INTO - 向数据库中插入新数据

    INSERT INTO Websites (name, url, alexa, country) VALUES ('百度','百度一下,你就知道','4','CN');

  • CREATE DATABASE - 创建新数据库

  • ALTER DATABASE - 修改数据库

  • use datebase

  • CREATE TABLE - 创建新表

  • ALTER TABLE - 变更(改变)数据库表

  • DROP TABLE - 删除表

  • CREATE INDEX - 创建索引(搜索键)

  • DROP INDEX - 删除索引

  • ORDER BY

  • WHERE

    SELECT * FROM Websites WHERE country='CN' AND alexa > 50;

关键字,具体意义

auto 定义自动变量,变量的生命周期与其所在的代码块相同 break 跳出循环或开关语句 case 在开关语句中定义不同的情况 char 定义字符类型变量或函数返回值的数据类型 const 定义只读的常量,表示变量的值在程序执行期间不能被修改 continue 结束当前循环的当前迭代,并继续下一次迭代 default 在开关语句中定义黑认情况 do 定义一个循环,重复执行一段代码块,直到指定的条件不再满足 double 定义双精度浮点数类型变量或函数返回值的数据类型 else 在条件语句中定义条件不成立时执行的代码块 enum 定义枚举类型 extern 声明外部变量或函数 float 定义单精度浮点数类型变量或函数返回值的数据类型 for 定义一个循环,重复执行一段代码块 goto 将程序的控制转移到指定的标签位置 if 定义一个条件语句,根据条件的真假执行相应的代码块 int 定义整数类型变量或函数返回值的数据类型. long 定义长等级类型委量或函数返回值的数据类型 register return 从函数中返回值,并结束函数的执行. short 定义短整数类型变量或函数返回值的数据类型 signed 定义有符号数类型变量 sizeof 获取数据类型或变量的大小 static 声明静态变量或函数 struct 定义结构体类型 switch typedef 整改检查规范型 union unsigned 定义无符号数类型变量 void 声明函数无返回值或不带参数,或指定指针类型不指向特定类型 volatile 声明变量为易变变量。 while 定义一个循环,重复执行一段代码块

img

关键字1

关键字2

一、基本问题(80%)

1、const、static作用。 2、c++面向对象三大特征及对他们的理解,引出多态实现原理、动态绑定、菱形继承。 3、虚析构的必要性,引出内存泄漏,虚函数和普通成员函数的储存位置,虚函数表、虚函数表指针。 4、malloc、free和new、delete区别,引出malloc申请大内存、malloc申请空间失败怎么办。 5、stl熟悉吗,vector、map、list、hashMap,vector底层,map引出红黑树。优先队列用过吗,使用的场景。无锁队列听说过吗,原理是什么(比较并交换) 6、实现擅长的排序,说出原理(快排、堆排) 7、四种cast,智能指针 8、tcp和udp区别 9、进程和线程区别。 10、指针和引用作用以及区别。 11、c++11用过哪些特性,auto作为返回值和模板一起怎么用,函数指针能和auto混用吗。 12、boost用过哪些类,thread、asio、signal、bind、function 13、单例、工厂模式、代理、适配器、模板,使用场景。 14、QT信号槽实现机制,QT内存管理,MFC消息机制。 15、进程间通信。会选一个详细问。 16、多线程,锁和信号量,互斥和同步。 17、动态库和静态库的区别。

二、回答基本问题(80%)

1、const、static作用。

2、c++面向对象三大特征及对他们的理解,引出多态实现原理、动态绑定、菱形继承。

多态的实现原理是通过虚函数(virtual function)和动态绑定(dynamic binding)来实现的。在基类中将某个成员函数声明为虚函数,派生类可以对其进行重写。当通过基类指针或引用调用虚函数时,会根据实际对象的类型来动态绑定到相应的函数实现,而不是根据指针或引用的类型来确定调用哪个函数。

动态绑定使得程序在运行时根据对象的实际类型来确定调用的函数,实现了多态的效果。这样可以提高代码的灵活性和可扩展性,使得程序更加易于维护和扩展。

菱形继承(Diamond Inheritance)是指一个派生类同时继承自两个或多个基类,而这些基类又继承自同一个基类。这种继承关系形成了一个菱形的结构。菱形继承可能引发菱形继承问题,即多次继承同一个基类可能导致成员变量和成员函数的二义性。为了解决这个问题,C++使用虚继承(virtual inheritance)来避免多次继承同一个基类,确保只有一份基类的实例被派生类继承。虚继承可以通过在继承关系中使用关键字virtual来实现。

3、虚析构的必要性,引出内存泄漏,虚函数和普通成员函数的储存位置,虚函数表、虚函数表指针。

4、malloc、free和new、delete区别,引出malloc申请大内存、malloc申请空间失败怎么办。

当使用malloc函数申请大内存时,有可能会出现内存不足的情况,导致malloc申请空间失败。在这种情况下,可以采取以下几种处理方式:

  1. 检查返回值:在调用malloc函数后,需要检查其返回值是否为NULL。如果返回值为NULL,则表示malloc申请空间失败。可以通过判断返回值是否为NULL来确定是否申请成功。

  2. 释放已分配的内存:如果malloc申请空间失败,可以先释放已经成功申请的内存,以释放一部分内存空间,然后重新尝试申请大内存。可以使用free函数来释放已分配的内存。

  3. 使用realloc函数:如果malloc申请空间失败,可以尝试使用realloc函数来重新分配内存。realloc函数可以重新分配已分配内存的大小,如果传入NULL指针,则相当于malloc函数。可以通过realloc函数来尝试重新申请更大的内存空间。

  4. 错误处理:在malloc申请空间失败时,可以根据具体情况进行错误处理。可以输出错误信息,记录日志,或者采取其他适当的措施来处理内存申请失败的情况。

5、stl熟悉吗,vector、map、list、hashMap,vector底层,map引出红黑树。优先队列用过吗,使用的场景。无锁队列听说过吗,原理是什么(比较并交换)

我熟悉STL(Standard Template Library),它是C++标准库中的一个重要组成部分,提供了一系列的容器、算法和函数模板,用于简化和加速C++程序的开发。

  1. vector:vector是一个动态数组,底层使用连续的内存空间存储元素,支持随机访问和动态扩容。当元素数量超过当前容量时,vector会重新分配更大的内存空间,并将原有元素拷贝到新的内存空间中。

  2. map:map是一个关联容器,存储键值对,底层使用红黑树(Red-Black Tree)实现。红黑树是一种自平衡的二叉搜索树,保持了插入、删除和查找操作的对数时间复杂度。

  3. list:list是一个双向链表,底层使用指针连接节点,支持高效的插入和删除操作,但不支持随机访问。

  4. hashMap:hashMap是一个哈希表,底层使用哈希函数将键映射到对应的存储位置。哈希表通过哈希函数将键映射到桶(bucket),每个桶中存储一个链表或红黑树,用于解决哈希冲突。

优先队列(Priority Queue)是一个特殊的队列,每个元素都有一个优先级,按照优先级大小进行排列。优先队列通常使用堆(Heap)数据结构来实现,堆是一种完全二叉树,满足堆序性质。优先队列常用于解决一些和优先级相关的问题,如任务调度、最小/最大值查找等。

无锁队列(Lock-Free Queue)是一种并发数据结构,用于解决多线程环境下的数据共享和同步问题。无锁队列的原理是使用原子操作(比较并交换,CAS)来实现对队列的操作,避免了使用锁导致的线程等待和阻塞。无锁队列常用于高并发场景,提高了程序的并发性能和响应速度。

6、实现擅长的排序,说出原理(快排、堆排)

7、四种cast,智能指针

在C++中,有四种类型转换(cast)操作符可以用来进行类型转换,它们分别是:

  1. 静态转换(static_cast):用于非多态类型之间的转换,如基本类型之间的转换、父类指针/引用与子类指针/引用之间的转换等。静态转换在编译时进行类型检查,但不提供运行时类型检查。

  2. 动态转换(dynamic_cast):用于多态类型之间的转换,即基类指针/引用与派生类指针/引用之间的转换。动态转换在运行时进行类型检查,如果转换失败(即指针指向的对象不是目标类型的派生类),则返回空指针(对于指针)或抛出std::bad_cast异常(对于引用)。

  3. 重新解释转换(reinterpret_cast):用于任意类型之间的转换,如指针之间的转换、指针与整数类型之间的转换等。重新解释转换不会进行任何类型检查,它只是将一个指针或引用的位模式重新解释为另一种类型的位模式。

  4. 常量转换(const_cast):用于去除const属性,即将const修饰的指针或引用转换为非const类型。常量转换可以用于修改非const对象的常量属性,但是修改const对象是未定义行为。

智能指针是C++中用于管理动态分配内存的一种机制。它们提供了自动释放内存的功能,避免了手动调用delete来释放内存,从而减少了内存泄漏的风险。常见的智能指针包括std::shared_ptr、std::unique_ptr和std::weak_ptr。

  • std::shared_ptr是一种共享所有权的智能指针,多个shared_ptr可以指向同一个对象,当最后一个shared_ptr销毁时,会自动释放对象的内存。

  • std::unique_ptr是一种独占所有权的智能指针,同一时间只能有一个unique_ptr指向一个对象,当unique_ptr销毁时,会自动释放对象的内存。

  • std::weak_ptr是一种弱引用的智能指针,它可以指向一个由shared_ptr管理的对象,但不会增加对象的引用计数。weak_ptr可以用于解决shared_ptr之间的循环引用问题。

智能指针的使用可以提高程序的安全性和可维护性,避免内存泄漏和悬空指针等问题。

8、tcp和udp区别

9、进程和线程区别。

进程和线程是操作系统中的两个重要概念,它们之间有以下几点区别:

  1. 资源分配:进程是操作系统中资源分配的基本单位,每个进程都有独立的地址空间、文件描述符、运行时环境等。而线程是进程内的执行单元,多个线程共享同一个进程的资源,包括地址空间、文件描述符等。

  2. 调度和切换:进程之间的切换需要保存和恢复整个进程的上下文,包括程序计数器、寄存器等。而线程之间的切换只需要保存和恢复线程的上下文,开销较小。

  3. 并发性:进程是独立执行的,多个进程之间可以并发执行,彼此之间相互独立。而线程是共享进程资源的,多个线程可以在同一个进程中并发执行,共享进程的地址空间和资源。

  4. 通信和同步:进程之间通信需要使用特定的机制,如管道、消息队列、共享内存等。而线程之间通信更加方便,可以直接读写共享变量。线程之间还可以使用同步机制,如互斥锁、条件变量等来实现线程的同步和互斥操作。

  5. 创建和销毁:创建和销毁进程的开销较大,需要分配和释放资源。而创建和销毁线程的开销较小,只需要分配和释放线程的上下文即可。

总的来说,进程是操作系统中资源分配的基本单位,拥有独立的地址空间和资源,进程之间相互独立;而线程是进程内的执行单元,共享进程的资源,可以实现并发执行和共享数据。线程的切换开销较小,通信和同步更加方便,但需要注意线程之间的同步和互斥问题。

10、指针和引用作用以及区别。 11、c++11用过哪些特性,auto作为返回值和模板一起怎么用,函数指针能和auto混用吗。 12、boost用过哪些类,thread、asio、signal、bind、function 13、单例、工厂模式、代理、适配器、模板,使用场景。 14、QT信号槽实现机制,QT内存管理,MFC消息机制。 15、进程间通信。会选一个详细问。 16、多线程,锁和信号量,互斥和同步。 17、动态库和静态库的区别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值