1、C语言标识符
C语言中的标识符是指程序员自定义的名称,用于表示变量、函数、数组等程序实体。标识符必须由字母、数字和下划线组成,且首字符必须为字母或下划线,长度不能超过规定的限制。
2、C语言二维数组中的一维大小
int a[][2] = {1,2,3,4,5};
printf("%d %d %d\n",sizeof(a[1])/sizeof(int),sizeof(a)/sizeof(a[1]),sizeof(a)/sizeof(int));
//输出: 2 3 6
//第一维可不指定,由编译器按照条件给最小值,第二维必须明确。
3、C语言中打印 i++、 ++i
int i=1;
printf("%d %d %d",i++,++i,++i);
编译器处理过程:
++i ; // i=2
++i ; // i=3
temp=i ; // i=3
i++ ; // i=4
push i; // 4
push i; // 4
push temp; //3
所以运行结果为 3 4 4
4、C语言中不合法的字符常量
A. ’2’
B. ’\101’
C. ’ab’
D. ’\n‘
答案是C
理由:A。'2'表示一个字符,正确。 B。'\101'相当于字符'A',正确。 C。'ab',不是字符常量,是一个字符串,而且应该表示为“ab”(双引号)。D。'\n',换行符号,正确。
5、进制转换
以十进制到十六进制(反之)为例:
整数部分转换原理:除以16,反向取余数,直到商为0终止。
小数部分转换原理:乘16取整,正向取整数,直到符合转换精度。
6、C语言中char、short、int、long各占多少字节
windows操作系统下:
char: 1个字节
short: 2个字节
int: 4个字节
long: 4个字节
float: 4个字节
double: 8个字节
7、C语言局部变量、静态局部变量、全局变量与静态全局变量
作用域:起作用的区域,也就是可工作的区域。
代码块:用{}括起来的一段代码。
数据段:数据段存的是数,全局变量存放在数据段。
代码段:存的是程序代码,一般是只读的。
栈:局部变量存放在栈里。
堆:用于动态内存分配,位于栈和BSS段之间的内存。
局部变量:定义在函数内部,作用域为函数内,每次调用函数均需重新定义和初始化的变量。
静态局部变量:定义在函数内部,变量类型前加 static,在第一次调用时被定义与初始化,并且保持其值被下一次调用。与全局变量的区别在于静态局部变量的作用域仅在函数内部。
全局变量:定义在函数外部的变量。
普通全局变量:定义时类型前不加任何关键字,对任何.c文件均可见,所以需要确保文件间变量不重名。
静态全局变量:定义时在在数据类型加 static,用于告诉编译器该变量仅在当前文件使用。这就解决了重名问题。
局部变量和全局变量的区别:1.初始值,定义后无初始值时,局部变量的值是未定的,全局变量的值为0;2.作用域,局部变量的作用域为该变量所在的代码块内,全局变量的作用域为整个文件;3.生存周期,局部变量在当前代码块被初始化时诞生,到退出该代码块时死亡,全局变量在程序的初始化阶段诞生,到退出程序时死亡;4.数据分配位置,局部变量分配在栈上,全局变量分配在数据段上。
8、在C语言中,变量有三种类型:自动变量(automatic variable)、静态变量(static variable)和寄存器变量(register variable)。
自动变量(automatic variable):声明和初始化格式为 int a = 1; 。如果不初始化,它的值是未定义的初始值,每次进入该变量的作用域时,变量会被重新初始化。
静态变量(static variable):声明和初始化格式为 static int a = 1; 。如果不初始化,它的值是0。与自动变量所不同的是静态变量只需声明一次,每次进入该变量的作用域时均会使用上一次被调用的运算值。
寄存器变量(register variable):用于程序中某个被重度使用的变量,能够提升性能。
9、5%(-2)
5%(-2)
//结果为1
10、内存区域
由低地址到高地址依次划分为:正文段(代码区)、已初始化数据区、BSS(未初始化区)、堆、栈。
正文段:用来存放程序执行代码的内存区域。该区域的大小在代码运行前就已经确定,通常为只读。也可包含一些只读的常量,比如字符常量。
已初始化数据段:用来存放已初始化的全局变量和已初始化的静态变量的内存区域,属于静态内存分配。
BSS段:用于存放未初始化的全局变量和未初始化的静态变量的内存区域,属于静态内存分配。
堆:用于动态内存分配,位于BSS和栈之间的内存区域,由程序员申请分配与释放。由低地址向高地址增长,采用链式存储结构。
栈:用于存放函数的参数值、局部变量等。栈区由高地址向低地址增长。
11、大端和小端模式
大端模式:高地址存放低字节;小端模式:高地址存放高字节。
为什么会有大小端之分:不同架构的CPU处理字节顺序不一致。
根据强制类型转换截断判断是大端还是小端:
typedef int bool;
#define TRUE 1
#define FALSE 0
bool IS_BigEndian()
{
short a = 0x1234;
char b = *(char*)&a;
if(b == 0x12)
{
return TRUE;
}
return FALSE;
}
int main()
{
printf("%d",IS_BigEndian());
return 0;
}
12、三次握手和两次握手:三次握手是为了防止失效的连接报文突然又传到了服务器,从而导致不必要的错误和资源浪费。
13、ARP表中的MAC地址的来源:主机的网络接口、交换机的端口、路由器的接口等。
14、RT-Thread
线程调度:线程是RT-Thread的最小调度单位,线程调度算法是基于优先级的全抢占式多线程调度算法。支持256个线程优先级(也可通过配置文件更改最大支持8或32个优先级,对stm32默认配置32个线程优先级),0优先级代表最高优先级,最低优先级留给空闲线程使用;当创建多个相同优先级的线程时,支持时间片轮转调度算法进行调度使每个线程运行相应的时间。
线程控制块:用于管理线程的数据结构,存放线程的优先级、线程名称、线程状态等。
线程状态:同一时间只允许一个线程在处理器中运行,从运行过程上进行划分,可以分为初始状态、就绪状态、运行状态、挂起状态、关闭状态。初始状态:线程被创建,还未被运行时的状态;就绪状态:就绪状态下,线程按照优先级排序等待运行;运行状态:线程正在运行;挂起状态:也称阻塞态,因资源不可用而被挂起等待或线程主动延时一段时间而挂起;关闭状态:当线程运行结束时处于关闭状态,关闭状态的线程不参与线程调度。
线程优先级:RT-Thread 的优先级表示线程被调度的优先程度,
时间片:时间片仅对相同优先级的就绪态线程有效,用于约束单个线程的运行时间。
线程间同步:
15、各种运算符的优先级
转自: http://blog.csdn.net/huangblog/article/details/8271791
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | -- |
() | 圆括号 | (表达式)/函数名(形参表) | -- | ||
. | 成员选择(对象) | 对象.成员名 | -- | ||
-> | 成员选择(指针) | 对象指针->成员名 | -- | ||
2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 |
~ | 按位取反运算符 | ~表达式 | |||
++ | 自增运算符 | ++变量名/变量名++ | |||
-- | 自减运算符 | --变量名/变量名-- | |||
* | 取值运算符 | *指针变量 | |||
& | 取地址运算符 | &变量名 | |||
! | 逻辑非运算符 | !表达式 | |||
(类型) | 强制类型转换 | (数据类型)表达式 | -- | ||
sizeof | 长度运算符 | sizeof(表达式) | -- | ||
3 | / | 除 | 表达式/表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | |||
% | 余数(取模) | 整型表达式%整型表达式 | |||
4 | + | 加 | 表达式+表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式-表达式 | |||
5 | << | 左移 | 变量<<表达式 | 左到右 | 双目运算符 |
>> | 右移 | 变量>>表达式 | |||
6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | |||
< | 小于 | 表达式<表达式 | |||
<= | 小于等于 | 表达式<=表达式 | |||
7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | |||
8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量=表达式 | 右到左 | -- |
/= | 除后赋值 | 变量/=表达式 | -- | ||
*= | 乘后赋值 | 变量*=表达式 | -- | ||
%= | 取模后赋值 | 变量%=表达式 | -- | ||
+= | 加后赋值 | 变量+=表达式 | -- | ||
-= | 减后赋值 | 变量-=表达式 | -- | ||
<<= | 左移后赋值 | 变量<<=表达式 | -- | ||
>>= | 右移后赋值 | 变量>>=表达式 | -- | ||
&= | 按位与后赋值 | 变量&=表达式 | -- | ||
^= | 按位异或后赋值 | 变量^=表达式 | -- | ||
|= | 按位或后赋值 | 变量|=表达式 | -- | ||
15 | , | 逗号运算符 | 表达式,表达式,… | 左到右 | -- |
说明:
同一优先级的运算符,运算次序由结合方向所决定。
简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符
16、
int a[5] = {1,3,5,7,9};
int *ptr = (int*)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1)); //3,9
//&a+1 : 取出a的地址,+1是加上整个数组的大小
//ptr指向的是数组a外的第一个int块,ptr-1指向a[5]
17、51单片机能配置的中断:外部中断(INT)、定时器中断(T)、串口中断(RXD TXD)。
18、
int x = 5;
float y;
y = 2.0/5;
printf("%d",y);
y = 2/(float)x;
printf("%d",y);
y = (float)(2/x);
printf("%d",y);
y = 2/(x*1.0);
printf("%d",y);
//只有第三次输出为0,其它三个输出结果都一样
19、
int x = 0;
while((x++)<=5);
printf("%d",x);
//输出7
int x = 0;
while((++x)<=5);
printf("%d",x);
//输出6
20、数组指针、指针数组
数组指针:本质是一个指针,指向了一个数组。数组中每个元素都是某种数据类型的值。
int (*p)[4]; //定义了一个数组指针,指向一个大小为4的数组,数组中每个元素都是int类型。
数组指针也称为行指针,当执行p+1时,会指向数组的下一行:
int a[3][4] = {0};
int (*p)[3] = a; //p指向了a[0][0]
p++; //p指向了a[1][0]
指针数组:本质是一个数组,数组中每个元素都为指针。
int *p[4]; //定义了一个指针数组,数组中每个元素都为int*类型的指针。
int a[3][4];
int *p[3]; //定义了一个数组,该数组中有3个int*指针变量,分别为p[0]、p[1]、p[2]
//p++; //若执行此语句,则数组p指向下一个数组元素
for(int i=0;i<3;i++){
p[i]=a[i]; //数组p中有3个指针,分别指向二维数组a的每一行
}
21、排序算法的复杂度
来源链接:十大经典排序算法的复杂度分析_排序算法时间复杂度_阿尔兹的博客-CSDN博客
22、输入可用会议数meet_num,二维数组time存储数量为meet_num的会议的起始和结束时间,求解一天中可参加最多的会议数。(大华笔试编程题)
int main()
{
int meet_num,i,j,n=1;
scanf("%d",&meet_num);
int time[meet_num][2];
for(i=0;i<meet_num;i++)
{
scanf("%d %d",&time[i][0],&time[i][1]);
}
for(i=0;i<meet_num;i++)
{
int temp=1;
int k=i;
for(j=i+1;j<meet_num;j++)
{
if(time[k][1]<=time[j][0])
{
temp++;
k=j;
}
}
if(temp>n)
n=temp;
}
printf("%d",n);
return 0;
}
23、输出结果与其它三项不同的是哪一项。(大华笔试题)暂时没搞懂
char *ptr0 = "\032";
printf("%d\n",ptr0[0]);
char *ptr1 = "\x01234561a\032";
printf("%d\n",ptr1[1]);
char *ptr2 = "\x78988881a";
printf("%d\n",ptr2[0]);
char *ptr3 = "\032123";
printf("%d\n",ptr3[1]);
24、(大华笔试题)
int x = 10;
int md()
{
return (x+=10);
}
int cv(int x)
{
return (x+=1);
}
void main()
{
int x = 10;
x++;
cv(x);
x++;
md();
printf("%d",x); //12
x++;
cv(x);
printf("%d",x); //13
md();
printf("%d",x); //13
}
25、(大华笔试题)
char buf[8];
memset(buf,'a',sizeof(buf));
strncpy(buf,"123456789",sizeof(buf));
//buf的最后一个字符为 ‘8’
26、const 的用法
1、const 变量:只读不可改变,必须初始化。
const int a = 1;
const int b; //编译错误,未给b赋初值
b = 2; //编译错误,const变量不可改变
2、指向const 变量的指针,其中保存着一个const变量的指针,指向的变量不可改变,但指针的指向可以改变。
const int a = 1;
const int b = 2;
const int *p = &a;
p = &b; //编译正确,指针的指向可以改变
*p = 3; //编译错误,指针指向的变量不可改变
3、const 指针,指针的指向不应该被改变,而指向的内容可以改变。
const int a = 1;
const int b = 1;
int * const p = &a;
*p = 2; //指针指向的内容可以改变
p = &b; //指针的指向不可改变
4、const VS define
const 用于定义普通变量,define 用于定义宏。define定义的宏是在预处理阶段进行,const 定义的只读变量是在编译运行阶段进行。
const 定义的是常变量,define定义的是常量。define定义的宏在编译后就不存在了,不占用内存等。const定义的变量占用内存,具有数据类型。
编译器可以对 const 定义的变量进行类型检查,而对 define 定义的常量不能进行类型检查,只能进行机械性的字符替换。
27、满二叉树和完全二叉树
满二叉树:一个二叉树的每一层的节点数都达到最大值。
完全二叉树:最后一层以上的节点都是满的,最后一层的节点都连续集中在左侧。堆是完全二叉树,分为大根堆和小根堆,大根堆:父节点的值大于等于子节点的值;小根堆:子节点的值大于等于父节点的值。
28、linux的内存管理
29、linux的inode中记录的数据
30、linux中,unmask为003,创建一个新的目录的默认权限是
31、互斥量和信号量的区别
1、互斥量主要用于实现线程间的互斥;信号量主要用于线程间的同步。
2、互斥量值只能为0和1,是一种特殊的二值信号量;信号量值为非负整数。
3、互斥量的获取和释放必须由同一线程执行,信号量的释放可以由一个线程执行,由另一线程获取。
32、内存映射的原理
是在进程的虚拟地址空间中创建一个映射,分为文件支持的内存映射,和无文件支持的内存映射。
文件支持的内存映射:把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件。
无文件支持的内存映射:把物理内存映射到进程的虚拟地址空间,无数据源。
33、C语言与C++的区别:
C语言将char视作整数,将字符编码存放在变量char中,C++将其视为char类型,在C中:
char a = 'ABCD';
int x = 'ABCD';
printf("%c %c\n",a,x); //输出 D D (只使用最后一个字节)
printf("%x %x\n",a,x); //输出 44 41424344 (char表示一字节整数)
结构和联合的称与变量冲突时:C解释为变量,C++解释为结构或联合。
float a = 123.01;
struct a
{
int b;
int c;
};
struct a aa = {1,2};
printf("%f",a); //123.01
对于枚举:C++不允许将整数赋值给枚举变量,也不能自增一个枚举变量;C++可以不使用关键字enum声明枚举变量。
enum sample {a,b,c};
enum sample ss;
ss = 1; //C++不允许
ss++; //C++不允许
sample sss; //C++允许,C不允许
void类型指针:在C++中,只有使用强制类型转换才能将void类型指针转换为其它类型指针。
int *p1;
void *p2
p1 = (int *)p2; //使用(int *)将void类型指针转换为int类型指针
34、GPIO的输入输出工作模式:
4种输入模式+2种输出模式+2种复用输出模式:带上拉输入、带下拉输入、浮空输入、模拟输入、开漏输出、推挽输出、开漏复用输出、推挽复用输出。
35、sizeof 和 strlen 的区别:sizeof 是一个计算数据类型所占内存空间的单目运算符(包含了‘\0’的位置),strlen 是计算字符串长度的函数。
char a[]="abc";
char b[]={'a','b','c'};
printf("%d\n",sizeof(a)); //4
printf("%d\n",strlen(a)); //3
printf("%d\n",sizeof(b)); //3
printf("%d\n",strlen(b)); //3
35、TCP和UDP的区别:
1、TCP进行通信时,需要先建立连接,也就是需要先将客户端和服务端建立好,而UDP是无连接的。
2、TCP是可靠的,UDP是不可靠的。
TCP通过以下方式保证自身数据可靠:数据包检验(16位数据检验和)防止发送错误的数据包;确认序列号对失序报文进行重新排序;丢弃重复数据包,防止数据冗余重复;确认应答机制,接收方接受完一个数据后会发送一个确认;超时重传机制,发送方发送数据后会启动定时器,超过定时器时间后依旧未收到对方确认则重发;流量控制,确保接收方接收到的数据不会再自身缓冲区溢出;拥塞控制,确保数据在传输中的可靠性,降低丢包的概率。UDP则没有上述传输机制。
选择TCP or UDP:对于实时性要求高的场景选择UDP,比如网络游戏、视频流通信等,其它大部场景使用TCP。
36、野指针的产生
野指针是由于指针变量中保存的值不是合法的内存地址或指针指向了不可用内存造成的。
野指针怎么产生:
1、指针变量没有被初始化:
int *p1; //定义指针变量时,未初始化,产生了野指针
int *p2 = NULL; //指针被初始化
2、使用了已经释放的指针:
int *p = (int *)malloc(sizeof(int));
free(p);
*p = 1; //指针p已经被释放,但仍被使用
避免野指针产生的方法:定义指针时,初始化为NULL;指针解引用前,检查指针是否为NULL;指针使用完后,赋值为NULL;指针使用前,将其值赋给一个可用的地址。
37、数组和指针的区别:
数组是一块连续的内存空间,其大小在编译时确定,通过下标索引访问;指针变量存放的是指向变量的地址,通过解引用访问指向的变量。
38、如何防止重复引用头文件:
#ifndef HEADER_FILE_H
#define HEADER_FILE_H
#endif //HEADER_FILE_H
//或者使用
#pragma once
39、栈和队列的区别
队列:限定只能在表的一端插入,而在另一端进行删除操作的线性表;
栈:限定在表的一端进行删除和插入的线性表。
1、操作的名称不同:队列的插入称为入队,队列的删除称为出队;栈的插入称为入栈,栈的删除称为出栈。
2、操作的限定不同:队列在队尾入队,在队首出队,两端都可操作;栈在栈顶入栈和出栈,无法直接对栈底直接操作。
3、操作规则不同:队列是先进先出,栈是后进先出。
4、遍历数据速度不同:队列是基于地址指针遍历,可以从头部或尾部进行遍历,但不能同时遍历,无需开辟空间,所以遍历速度快;栈只能从顶部取数据,最先进入栈底的需要遍历完整个栈才能取出来,同时需要为数据开辟临时存储空间以此保持数据的一致性,所以遍历速度慢。
5、栈常用于表达式求值、函数调用、括号匹配等需要后进先出的场景;队列常用于任务调度、缓冲区管理、消息传递等需要先进先出的场景。
40、linux系统
linux系统4个主要部分:内核、shell、文件系统和应用程序。
linux内核由下几部分组成:内存管理、进程管理、设备驱动程序、文件系统和网络管理等。
linux驱动的3个基础大类:字符设备驱动、块设备驱动、网络设备驱动。字符设备驱动:鼠标、键盘、显示器等;块设备:磁盘、U盘等;网络设备:网卡、wifi、蓝牙等。
41、linux驱动----第一个驱动helloworld
#include <linux/init.h>
#include <linux/module.h>
static int hello_init(void)
{
printk("hello, world!\r\n");
return 0;
}
static void hello_exit(void)
{
printk("byebye!\r\n");
}
module_init(hello_init());
module_exit(hello_exit());
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JU");
42、递归计算阶乘:
int func(int n)
{
if(n!=1)
{
return n*func(n-1);
}
else
{
return 1;
}
}
int main()
{
int n;
printf("请输入一个整数:");
while(scanf("%d",&n)!=EOF)
{
printf("%d的阶乘是:%d\n",n,func(n));
printf("请输入一个整数:");
}
return 0;
}
43、动态内存分配
int main() {
int n1, n2, i, j;
int **arr;
printf("请输入所要创建的动态数组的第一维长度:");
scanf("%d", &n1);
printf("请输入所要创建的动态数组的第二维长度:");
scanf("%d", &n2);
if ((arr = (int **) malloc(n1 * sizeof(int *))) == NULL)//使用malloc给第一维分配内存
{
printf("分配内存空间失败!\n");//如果内存分配失败,报错异常退出
return -1;
}
for (i = 0; i < n1; i++) {
if ((arr[i] = (int *) malloc(n2 * sizeof(int))) == NULL)//使用malloc给第二维分配内存
{
printf("分配内存空间失败!\n");//如果内存分配失败,报错异常退出
return -1;
}
}
for (i = 0; i < n1; i++) { //通过循环给数组赋值并在控制台进行打印
for (j = 0; j < n2; j++) {
arr[i][j] = i * n2 + j + 1;
printf("%d\t", arr[i][j]);
}
printf("\n");
}
for (i = 0; i < n1; i++) {
free(arr[i]);//释放第二维
}
free(arr);//释放第一维
return 0;
}
44、为什么中断不能传递参数:
中断是异步调用,无法知道什么时候会被调用,不能像函数一样主动调用。
45、串口数据帧格式:
起始位、数据位、校验位、停止位。
46、中断的概念:
中断是计算机系统中一种重要的用于响应某个事件或条件发生的机制,是一种异步的事件,可以打断当前正在执行的程序或任务,以处理紧急情况或外部设备的请求。
47、static的作用
修饰局部变量:
1、用静态关键字static修饰的局部变量,在编译的过程中,会在数据区为该变量开辟空间,并对其进行初始化,如果代码中未对其进行初始化,则系统默认初始化为0。
2、用static修饰的局部变量,会延长局部变量的寿命,超出函数的生存期。
修饰全局变量:
全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。
普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。
静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。
在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。
修饰函数:
函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:
静态函数只能在声明它的文件中可见,其他文件不能引用该函数
不同的文件可以使用相同名字的静态函数,互不影响
非静态函数可以在另一个文件中直接引用,甚至不必使用extern声明。
48、中断的执行过程:
中断的执行过程包括中断请求、中断控制器响应、中断响应、中断向量确定、中断处理程序执行和中断处理程序结束。
49、多态:
多态是面向对象编程中的一种特性,它允许以一种统一的方式处理不同类型的对象,通过相同的接口可以表现出不同的行为。
50、C语言中的内存分配方式有几种:静态内存分配,动态内存分配,栈上内存分配。
51、struct 和 class 的区别:
1.默认访问权限不同,struct的默认访问权限是public,class的默认访问权限是private。
2.继承方式不同,struct的默认继承方式是公有继承,class的继承方式是私有继承。
3.在一般情况下,struct被用于表示数据结构,而class则更多用于表示具有行为和数据的对象。
52、函数和中断的区别
1.函数调用不会发生上下文切换,中断调用会发生上下文切换。
2.函数可以主动被调用,中断无法主动被调用。
3.函数调用是同步的,中断是异步的。
4.函数可以有返回值和参数,中断没有返回值和参数。
53、二叉树的深度和节点数计算:
具有N个节点的二叉树的最小深度为 log2(N)的向上取整;
一棵深度为N的三叉树,最多有多少个节点 :(3^N-1)/(3-1);
一棵深度为N的二叉树,最多有多少个节点 :(2^N-1)/(2-1);
一棵深度为N的M叉树,最多有多少个节点 :(M^N-1)/(M-1)。
54、软中断是由内核机制的触发事件引起的(例如进程运行超时),而硬中断是由外设引发的。
55、队列和数组的区别:
结构上,队列是一种线性数据结构,按照先进先出的原则管理元素,新元素插入到队尾,从队头删除元素;数组是连续的固定大小的内存块,用于存储一组相同类型的元素,数组的元素在内存中是相邻存储的,并通过索引直接访问。
大小的限制,队列的长度灵活可变,数组的大小在使用过程中不可改变。
存储特性,队列通常使用链表或数组来实现。
56、PWM SPWM SVPWM
PWM:脉宽调制。通过微处理器的IO口输出高低电平的数字量形成PWM波形。应用场景:呼吸灯、通信调制。
SPWM:正弦脉宽调制。形成基本原理为面积等效原理,即冲量相等而形状不同的窄脉冲加在具有惯性的环节上,其效果基本相同。生成SPWM的方法:自然采样法和规则采样法。自然采样法:用需要调制的正弦波与锯齿波的交点来确定最终的PWM脉冲所需要输出的时间宽度;规则采样法:信号等幅,通过正弦波的幅值控制SPWM的的占空比。广泛应用于电机驱动,逆变电源等领域的调制技术。
SVPWM:空间矢量脉宽调制。使用稳态直流电压并通过六个开关(例如晶体管)模拟频率和振幅可调的三相正弦波形。用于将给定电压矢量施加到三相电动机(永磁体或感应电机)。
57、RAM 和FLASH的区别:
RAM:与CPU交换数据的内部存储器,可随时读写,作为正在执行程序的临时数据存储介质。
FLASH:只读存储器,主要用于一般数据存储。
58、linux查看cpu的指令
lscpu:可以显示有关处理器架构、型号、速度以及缓存等信息 。
top或htop:可以实时监控进程和资源使用情况,并显示当前活动进程所占用cpu的百分比。
mpstat:多核cpu利用率报告。
59、进程和线程之间的区别:
进程
1、进程之间不共享任何状态
2、进程的调度由操作系统完成
3、每个进程都有自己独立的内存空间
4、进程间通讯主要是通过信号传递的方式来实现的,实现方式有多种,信号量、管道、事件等,任何一种方式的通讯效率都需要过内核,导致通讯效率比较低
5、由于是独立的内存空间,上下文切换的时候需要保存先调用栈的信息、cpu各寄存器的信息、虚拟内存、以及打开的相关句柄等信息,所以导致上下文进程间切换开销很大,通讯麻烦。
线程
1、线程之间共享变量,解决了通讯麻烦的问题对于变量的访问需要锁
2、一个进程可以拥有多个线程,但是其中每个线程会共享父进程像操作系统申请资源,这个包括虚拟内存、文件等,由于是共享资源,所以创建线程所需要的系统资源占用比进程小很多,相应的可创建的线程数量也变得相对多很多。
3、另外在调度方面也是由于内存是共享的,所以上下文切换的时候需要保存的东西就像对少一些,这样一来上下文的切换也变得高效。
60、static的用法:
在C语言中,static是一个关键字,用于指定变量、函数和代码块的作用域和生命周期。下面是static的一些定义、用法和注意事项以及实际案例:
定义:在C语言中,static可以用于变量、函数和代码块。
用法:
1.变量:使用static关键字声明的变量是静态变量,它们的作用域被限制在定义它们的源文件中,它们的生命周期从程序开始运行到程序结束。静态变量的初始值为0。
2.函数:使用static关键字声明的函数是静态函数,它们的作用域被限制在定义它们的源文件中,它们不能被其他文件中的函数调用。静态函数只能在定义它们的文件中使用。
3.代码块:使用static关键字声明的代码块被称为静态代码块,它们只会在第一次使用它们的时候被执行一次,之后不会再次执行。
61、volatile的用法:
C语言中的 volatile 关键字被设计用来修饰变量,用于表明该变量是易变的。具体来说,它告诉编译器不要对该变量进行优化,因为该变量可能会在程序的执行过程中被外部因素改变。
在一些特定的场景中,变量的值可能会在程序的执行过程中被外部因素改变,比如:
- 在多线程程序中,多个线程可能会同时访问同一个变量;
- 在嵌入式系统中,变量的值可能会被硬件中断改变;
- 在使用内存映射 I/O(Memory-Mapped I/O)的系统中,变量的值可能会被设备寄存器改变。
应用场景:
1) 访问硬件寄存器
在嵌入式系统中,常常需要访问硬件寄存器。由于硬件寄存器通常具有特殊的访问方式和访问顺序,因此需要使用 volatile 关键字来确保对硬件寄存器的访问符合要求。
2) 多线程程序中的共享变量
在多线程程序中,多个线程可能会同时访问同一个变量。为了保证程序的正确性,需要使用 volatile 关键字来保证变量的内存可见性。
3) 信号处理程序中的变量
在信号处理程序中,由于信号的不确定性,可能会导致程序出现不可预测的错误。为了避免这种错误,需要使用 volatile 关键字来确保对变量的访问符合要求。
4) 外部中断处理程序中的变量
在外部中断处理程序中,由于外部中断的不确定性,可能会导致程序出现不可预测的错误。为了避免这种错误,需要使用 volatile 关键字来确保对变量的访问符合要求。
5) 某些特殊的数据类型
在某些特殊的数据类型中,使用 volatile 关键字可以确保对变量的访问符合要求。比如,C++ 中的 std::atomic 类型,就使用了 volatile 关键字来确保对变量的访问符合要求。
62、结构体中为什么要字节对齐:
63、TCP为什么需要三次握手、四次挥手:
64、sizeof(a)为多少
char **a[4][4];
printf("%d",sizeof(a));
a是一个二维数组,每个元素是指向指针的指针,a的内存空间大小为4*4*sizeof(char**)。指针大小取决于操作系统和编译器的位数(4字节或8字节)。
65、可能导致堆栈溢出的情形
1、非常大的局部数组,超过栈的容量就会发生栈溢出;2、递归调用,如果递归调用深度太大以至栈无法容纳,就会发生栈溢出;3、未正确管理动态分配的内存,申请大量内存而不释放则会导致栈溢出。
66、断言(assert)是一种在程序中用于检查假设条件的工具。它通常用于调试和验证程序的正确性。需要注意的是,断言在发布版本的程序中通常会被禁用,因为它们可能带来性能损耗。因此,断言主要用于开发和调试阶段,以帮助开发人员快速发现和解决问题。在发布版本中,应该适当地移除或禁用断言语句。
断言主要用于程序内部的假设条件的验证,而不是用于对外部传递的函数参数进行检查。在编写程序时,应该根据具体的需求选择合适的错误处理方式,包括使用断言、条件判断语句、异常处理等。