做游戏下载网站赚钱,wordpress农历插件,页面设计简洁明快,建设文明网 联盟网站的C语言部分#xff1a;
1.gcc的四步编译过程
1.预处理
展开头文件#xff0c;删除注释、空行等无用内容#xff0c;替换宏定义。
gcc -E hello.c -o hello.i
2.编译
检查语法错误#xff0c;如果有错则报错#xff0c;没有错误则生成汇编文件。
gcc -S hello.i -o h…C语言部分
1.gcc的四步编译过程
1.预处理
展开头文件删除注释、空行等无用内容替换宏定义。
gcc -E hello.c -o hello.i
2.编译
检查语法错误如果有错则报错没有错误则生成汇编文件。
gcc -S hello.i -o hello.s
3.汇编
将汇编文件生成二进制目标文件并非纯粹的二进制文件
gcc -c hello.s -o hello.o
4. 链接
将目标文件链接库文件最终生成机器能够运行的二进制可执行程序。
gcc hello.o -o hello
2.内存分区位置 Linux的内存空间简单可以分为5个部分
高地址
-------------------------------------------------
栈区由系统自动开辟自动释放的空间用于存放局部变量
-------------------------------------------------
堆区由用户手动申请手动释放的空间malloc和free
-------------------------------------------------
全 .bss 未初始化的全局变量和静态变量
局 ----------------------------------------------
区 .data 已初始化的全局变量和静态变量
-------------------------------------------------
常量区存放常量
-------------------------------------------------
代码段存放用户代码
-------------------------------------------------
低地址 ————————————————
3.局部变量与全局变量
局部变量
定义位置定义在函数体内部
存储位置栈区
未初始化初值为随机数
作用域当前函数
生命周期在函数执行结束后就被释放 全局变量
定义位置定义在函数体外部
存储位置全局区.data和.bss段
未初始化初值为0
作用域整个程序
生命周期在整个程序执行结束后才会被释放 局部变量可以与全局变量同名在函数内引用这个变量时会用到同名的局部变量而不会用到全局变量。对于有些编译器而言在同一个函数内可以定义多个同名的局部变量比如在两个循环体内都定义一个同名的局部变量而那个局部变量的作用域就在那个循环体内。
4. auto:自动型
修饰的变量存放在栈区
修饰的变量特点初值随机如果变量不赋初值
栈区由系统自动开辟与释放 5.static的作用
本质是延长生命周期同时限制其作用域。
static修饰的局部变量只需初始化一次未初始化初值为0。而且变量存储在全局数据段静态存储区中而不是栈中其生命周期持续到程序退出。
static修饰的全局变量、函数仅当前文件内可用其他文件不能引用。
6.extern的作用
1单个源文件的情况
对于单个源文件的程序如果某个全局变量不是在文件开头定义而是在中间某个位置那么如果在定义位置之前的函数想使用这个全局变量则可以采用extern来声明变量。
2多个源文件情况
如果某程序包含多个源文件模块一个源文件中定义了全局变量其它多个源文件均需要使用该全局变量只需要在各个使用此全局变量的文件中通过extern对全局变量进行声明即可使用。值得注意的是这种情况下涉及到多个文件对一个变量的操作某个文件修改了变量的值可能会影响其他文件的使用需谨慎使用。
3其它使用
此外extern也可用于函数的外部链接声明。我们知道函数的声明定义也可以包括存储类型但只有extern/static两种。当函数声明为extern说明函数具有外部链接其它文件可以调用此函数当函数声明为static说明函数是内部链接即只能在定义函数的文件内部调用函数当不指明函数存储类型则默认该函数具有外部链接。 注意
在使用同一程序不同文件的全局变量通常加extern进行外部引用。
extern引用语句放在全局引用到该文件的所有的函数均可访问这个全局变量。
extern引用语句放在局部仅能在所在函数体内部使用。 7.大小端三种验证方法
概念多字节数据在存储的过程中会有不同的存储形式。
小端模式高地址存高位数据低地址存低位数据。
大端模式高地址存低位数据低地址存高位数据。
大小端验证方法
方法一强制类型转换
#includestdio.hint main(int argc, const char *argv[]){int num 0x12345678;char ch (char)num;printf(%#x\n,ch); //0x78 低地址存了低位数据因此是小端存储模式。return 0;}方法二 利用共用体验证 利用联合体共用同一段内存的特性申请一段大小为4个字节的空间然后根据一个16进制占4位bit两个16进制占8位bit共1字节的原理用0x12345678共8个16进制将联合体内的4个字节全部填充完整最后直接取第一个字节的值即取较低地址当中的值根据我们上述说的小端模式中较低的有效字节存放在较低的存储器地址可知较低的有效字节为0x78如果该地址存放的值恰好等于0x78那么即为小端模式如果等于0x12则为大端模式。 #includestdio.hunion data{char a;int b;};int main(int argc, const char *argv[]){union data s;s.b 0x12345678;printf(%#x\n,s.a);return 0;}
方法三利用指针
#includestdio.hint main(int argc, const char *argv[]){int num 0x12345678; void *p num;printf(%#x\n,*(char *)p);return 0;}
8. strlen和sizeof的区别
1. strlen 是函数sizeof 是运算符。
2. strlen 测量的是字符的实际长度以\0 结束不包含\0 。而sizeof 测量的是字符的分配大小如果未分配大小则遇到\0 结束包含\0 也就是strlen测量的长度加1如果已经分配内存大小返回的就是分配的内存大小。
3.在子函数中ziseof 会把从主函数中传进来的字符数组当作是指针来处理。指针的大小又是由机器来决定而不是人为的来决定的。
4.strlen的结果要在运行的时候才能计算出来是用来计算字符串的长度不是类型占内存的大小。而大部分编译程序在编译的时候就把sizeof计算过了是类型或是变量的长度。
5.sizeof可以用类型做参数strlen只能用char*做参数且必须是以\0结尾的。 9. 结构体(struct)和联合体(union)的区别
两者最大的区别在于内存利用
1. 结构体struct 各成员各自拥有自己的内存各自使用互不干涉同时存在的遵循内存对齐原则。一个struct变量的总长度等于所有成员的长度之和。
2. 联合体union 各成员共用一块内存空间并且同时只有一个成员可以得到这块内存的使用权(对该内存的读写)各变量共用一个内存首地址。因而联合体比结构体更节约内存。一个union变量的总长度至少能容纳最大的成员变量而且要满足是所有成员变量类型大小的整数倍。 10. 结构体字节对齐三原则 结构体字节对齐的细节和具体编译器实现相关但一般而言满足三个准则
1首地址对齐按最大结构体变量的首地址能够被其最宽基本类型成员的大小所整除
2成员对齐按成员类型结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍如有需要编译器会在成员之间加上填充字节(internal adding)
3总大小对齐按最大结构体的总大小为结构体最宽基本类型成员大小的整数倍如有需要编译器会在最末一个成员之后加上填充字节{trailing padding}。 对于以上规则的说明如下 第一条编译器在给结构体开辟空间时首先找到结构体中最宽的基本数据类型然后寻找内存地址能被该基本数据类型所整除的位置作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。 第二条为结构体的一个成员开辟空间之前编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员大小的整数倍若是则存放本成员反之则在本成员和上一个成员之间填充一定的字节以达到整数倍的要求也就是将预开辟空间的首地址后移几个字节。 第三条结构体总大小是包括填充字节最后一个成员满足上面两条以外还必须满足第三条否则就必须在最后填充几个字节以达到本条要求。
11. 数组名和指针的区别与联系是什么
1. 数据保存方面 指针保存的是地址(保存目标数据地址自身地址由编译器分配)内存访问偏移量为4个字节无论其中保存的是何种数据均已地址类型进行解析。 数组保存的数据。数组名表示的是第一个元素的地址 内存偏移量是保存数据类型的内存偏移量;只有对数组名取地址(数组名)时数组名才表示整个数组内存偏移量是整个数组的大小(sizeof(数组 名))。2.数据访问方面 指针对数据的访问方式是间接访问需要用到解引用符号(*数组名)。 数组对数据的访问则是直接访问,可通过下标访问或数组名元素偏移量的方式3.使用环境 指针多用于动态数据结构(如链表,等等)和动态内存开辟。 数组多用于存储固定个数且类型统一的数据结构(如线性表等等)和隐式分配。
12. const有什么作用
const意味着只读
1、修饰变量
C语言中采用const修饰变量功能是对变量声明为只读特性并保护变量值以防被修改。举例说明如下
const int i 5;
上面这个例子表明变量i具有只读特性不能够被更改若想对i重新赋值如i 10则是错误的。
值得注意的是定义变量的同时必须初始化。定义形式也可以写成int const i5同样正确。
此外const修饰变量还起到了节约空间的目的通常编译器并不给普通const只读变量分配空间而是将它们保存到符号表中无需读写内存操作程序执行效率也会提高。
2、修饰数组
C语言中const还可以修饰数组举例如下
const int array[5] {1,2,3,4,5};
array[0] array[0]1; //错误
数组元素与变量类似具有只读属性不能被更改一旦更改如程序将会报错。
3、 修饰指针
C语言中const修饰指针要特别注意共有两种形式一种是用来限定指向空间的值不能修改另一种是限定指针不可更改。举例说明如下
int i 5;
int j 6;
int k 7;
const int * p1 i; //定义1
int * const p2 j; //定义2
上面定义了两个指针p1和p2。
在定义1中const限定的是*p1即其指向空间的值不可改变若改变其指向空间的值如*p120则程序会报错但p1的值是可以改变的对p1重新赋值如p1k是没有任何问题的。
在定义2中const限定的是指针p2若改变p2的值如p2k程序将会报错但*p2即其所指向空间的值可以改变如*p280是没有问题的程序正常执行。
4、修饰函数参数
const关键字修饰函数参数对参数起限定作用防止其在函数内部被修改。所限定的函数参数可以是普通变量也可以是指针变量。举例如下
void fun1(const int i)
{
其它语句
……
i; //对i的值进行了修改程序报错
其它语句
}
void fun2(const int *p)
{
其它语句
……
(*p); //对p指向空间的值进行了修改程序报错
其它语句
} 13. POSIX是什么
POSIXPortable Operating System Interface便携式操作系统接口是一个标准化的操作系统接口旨在促进不同操作系统之间的可移植性和互操作性。POSIX标准由IEEEInstitute of Electrical and Electronics Engineers制定具体定义在IEEE POSIX标准化文件中。
POSIX标准定义了一组函数、系统调用、头文件和工具命令用于编写可移植的应用程序和系统软件。这些标准化接口规范了操作系统的核心功能包括文件操作、进程管理、线程、信号处理、网络通信等。
通过使用POSIX接口开发者可以编写与特定操作系统无关的应用程序使其能够在符合POSIX标准的不同操作系统上进行编译和运行而无需进行大规模的修改和适应。POSIX的目标是提供一致的编程接口使应用程序能够跨多个操作系统平台进行移植和部署。
POSIX接口最初是针对类UNIX操作系统的但它也被其他操作系统如Linux、macOS等采纳并在许多嵌入式系统和实时操作系统中得到广泛应用。通过POSIX接口开发者可以编写可移植的、与操作系统无关的代码提高代码的可重用性和可移植性同时减少对特定操作系统的依赖性。 14. define 与typedef的区别
define 与typedef大体功能都是使用时给一个对象取一个别名增强程序的可读性但它们在使用时有以下几点区别
1原理不同
#define是C语言中定义的语法是预处理指令在预处理时进行简单而机械的字符串替换不作正确性检查只有在编译已被展开的源程序时才会发现可能的错误并报错。
typedef是关键字在编译时处理有类型检查功能。它在自己的作用域内给一个已经存在的类型一个别名但不能在一个函数定义里面使用typedef。用typedef定义数组、指针、结构等类型会带来很大的方便不仅使程序书写简单也使意义明确增强可读性。
2功能不同
typedef用来定义类型的别名起到类型易于记忆的功能。
#define不只是可以为类型取别名还可以定义常量、变量、编译开关等。
3作用域不同
#define没有作用域的限制只要是之前预定义过的宏在以后的程序中都可以使用而typedef有自己的作用域。 15. Makefile 里有什么
Makefile 里主要包含了五个东西显式规则、隐晦规则、变量定义、文件指示和注释。
1、显式规则。 显式规则说明了如何生成一个或多的的目标文件。这是由 Makefile 的书写者明显指 出要生成的文件文件的依赖文件生成的命令。
2、隐晦规则。 由于我们的 make 有自动推导的功能所以隐晦的规则可以让我们比较粗糙地简略地书 写 Makefile这是由 make 所支持的。3、变量的定义。 在 Makefile 中我们要定义一系列的变量变量一般都是字符串这个有点你 C 语言中 的宏当 Makefile 被执行时其中的变量都会被扩展到相应的引用位置上。4、文件指示。 其包括了三个部分一个是在一个 Makefile 中引用另一个 Makefile就像 C 语言中的 include 一样另一个是指根据某些情况指定 Makefile 中的有效部分就像 C 语言中的预 编译#if 一样还有就是定义一个多行的命令。有关这一部分的内容我会在后续的部分中 讲述。5、注释。 Makefile 中只有行注释和 UNIX 的 Shell 脚本一样其注释是用“#”字符这个就 像 C/C中的“//”一样。如果你要在你的 Makefile 中使用“#”字符可以用反斜框进行 转义如“#”。 最后还值得一提的是在 Makefile 中的命令必须要以[Tab]键开始。
16. C语言中修饰符volatile含义是什么其应用场合有哪些
volatile提醒编译器它后面所定义的变量随时都有可能改变因此编译后的程序 每次需要存储或读取这个变量的时候都会直接从变量地址中读取数据。如果没 有volatile关键字则编译器可能优化读取和存储可能暂时使用寄存器中的值 如果这个变量由别的程序更新了的话将出现不一致的现象。
16. 关键字volatile有什么含义并给出几个应用场合。 volatile的本意是“易变的”由于访问寄存器的速度要快过RAM,所以编译器一般都 会作减少存取外部RAM的优化但有可能会读脏数据。当要求使用volatile声 明的变量的值的时候系统总是重新从它所在的内存读取数据即使它前面的指 令刚刚从该处读取过数据。而且读取的数据立刻被保存。 一般说来volatile用在如下的几个地方 1、中断服务程序中修改的供其它程序检测的变量需要加volatile 2、多任务环境下各任务间共享的标志应该加volatile 3、存储器映射的硬件寄存器通常也要加volatile说明因为每次对它的读写都可 能由不同意义
16. volatile作用 防止编译器优化编译器常常会对代码进行优化例如将变量存储在寄存器中以提高程序的执行效率。然而在某些情况下变量的值可能会被程序之外的因素修改例如多线程环境、硬件中断等。如果使用 volatile 关键字声明变量编译器会禁止对该变量的优化每次都会从内存中读取变量的最新值。 确保内存操作的可见性在多线程或并发编程中volatile 关键字还可以用于确保内存操作的可见性。当一个变量被声明为 volatile 时每次对它的读写操作都会直接访问内存而不会使用缓存。这样可以保证多个线程之间对该变量的操作是按顺序执行的并且对一个线程的写入操作对其他线程是可见的。
17. 数组和链表的区别
1、顺序表在内存当中连续存储的(数组)但是链表在内存当中是不连续存储的通过指针将数据链接在一起。
2、顺序表的长度是固定的但是链表长度不固定。
3、顺序表查找方便下标但是插入和删除麻烦post~last链表插入和删除方便查找麻烦。
18. 对指针的理解
指针是一种数据类型用于存储内存地址。它指向某个特定类型的数据可以通过指针来访问和操作内存中的数据。指针的正确使用对于直接管理内存、处理复杂数据结构、减少内存占用等方面至关重要。指针的错误使用可能导致内存泄漏、野指针、访问非法内存等问题引发程序崩溃或安全漏洞。因此在使用指针时需要小心并确保正确初始化、安全地引用和释放相关内存。
19. 堆和栈的区别 分配方式 栈栈使用自动内存分配。在函数调用时局部变量和函数参数被分配在栈上并在函数返回时自动释放。栈的分配和释放是按照固定规则自动进行的不需要额外的手动操作。堆堆使用动态内存分配。程序员可以通过调用动态内存分配函数如 malloc()、new在堆上显式地分配内存同时还需要手动释放已分配的内存使用 free()、delete。 空间大小 栈栈的大小通常比较小由编译器在编译时确定并在运行时分配。栈的大小限制取决于操作系统或编译器的设定。堆堆的大小通常比较大并且相对于栈来说更加灵活可以根据需要动态分配和释放内存。 数据访问速度 栈由于栈上的数据是按照固定规则分配和释放的并且在访问上有较好的局部性因此栈上的数据访问速度较快。堆堆上的数据访问速度相对较慢因为堆上的数据通常需要通过指针间接访问。 20. memcpy和strcpy的区别
1功能不同
memcpy(): memcpy()函数用于将指定内存区域的数据复制到另一个内存区域。它可以复制任意数量的字节数据不仅限于字符串。strcpy(): strcpy()函数用于将一个字符串复制到另一个字符串。它复制字符直到遇到空字符(‘\0’)为止表示字符串的结束。
2参数不同
memcpy(): memcpy()函数接受三个参数目标内存地址、源内存地址和要复制的字节数。它不会自动添加字符串结束符。strcpy(): strcpy()函数接受两个参数目标字符串地址和源字符串地址。它会自动复制整个源字符串并在末尾添加空字符(‘\0’)。
21. 什么是段错误怎么解决段错误
段错误Segmentation Fault是一种常见的运行时错误它在程序访问无效的内存地址或试图对只读内存进行写操作时发生。当程序产生段错误时操作系统会终止程序的执行并生成一个错误报告。
常见导致段错误的情况包括 解引用空指针当程序试图访问空指针所指向的内存区域时由于空指针没有有效的内存地址就会产生段错误。 访问越界当程序访问数组、指针或缓冲区等数据结构时超出了其有效范围就会导致段错误。 写入只读内存当程序试图对只读内存如字符串常量进行写操作时就会引发段错误。
解决段错误需要进行调试和修复代码。以下是一些常见的方法和技巧 使用调试器使用调试器如GDB可以帮助定位段错误的发生位置。通过在程序崩溃时运行调试器并检查堆栈跟踪和变量状态可以确定引发段错误的具体代码段。 检查空指针确保程序中的指针在使用之前都进行了有效的初始化避免解引用空指针。 检查数组和指针边界确保程序中的数组和指针访问不会越界超出有效范围。 避免写入只读内存确保程序不会对只读内存进行写操作。如果需要修改字符串或数据应该使用可写的内存。 使用内存管理工具使用内存管理工具如Valgrind可以帮助检测内存访问错误和泄漏并提供详细的错误报告。 编码规范和静态分析工具遵循编码规范如避免未初始化变量、正确使用指针等并使用静态分析工具如Clang静态分析器可以帮助识别潜在的段错误问题。
注意在解决段错误时需要根据具体情况进行调试和修复。段错误可能是由于逻辑错误、内存管理错误或其他编程错误引起的。通过细致的调试和代码审查可以找到并解决这些问题以确保程序能够正确执行。
22. 什么是内存泄漏什么是野指针
内存泄漏Memory Leak指的是在程序运行过程中动态分配的内存空间没有被正确释放或释放的时机不合适导致这些内存无法再被程序使用从而造成内存的浪费。当内存泄漏累积到一定程度时会导致程序占用的内存越来越多最终可能导致性能下降或程序崩溃。
内存泄漏的发生通常是因为程序没有及时调用 free() 或 delete 来释放已动态分配的内存或者释放的顺序不正确从而导致一部分内存无法被回收。这种情况经常发生在使用动态内存分配如 malloc()、new的情况下。要解决内存泄漏问题需要确保在不需要使用动态分配的内存时及时释放它们避免造成资源的浪费。
野指针Dangling Pointer指的是指向已释放或无效的内存空间的指针。当一个指针指向的内存被释放后如果仍然使用该指针进行读写操作就会导致 undefined behavior未定义行为可能产生程序崩溃或其他异常。野指针的发生通常是由于程序中未及时更新指针或释放指针后未将其置空引起的。
要避免野指针问题应当养成良好的指针使用习惯使用指针前确保其指向有效的内存空间并在释放指针后将其置空或设置为合适的值。另外可以使用一些编程实践如避免在函数返回后返回指向局部变量的指针或者使用智能指针等工具来自动管理指针的生命周期减少野指针问题的发生。
总之内存泄漏和野指针都是常见的内存管理问题要写出高质量的程序需要注意及时释放不再使用的内存并确保指针的有效性。
23. 头文件 和 的区别
#include 头文件 : 编译器只会从系统配置的库环境中去寻找头文件不会搜索当前文件夹。通常用于引用标准库头文件。#include 头文件 : 编译器会先从当前文件夹中寻找头文件,如果找不到则到系统默认库环境中去寻找。一般用于引用用户自己定义使用的头文件。
24. #ifndef的作用
使用 #ifndef 可以有效防止头文件的重复包含避免重复定义变量、结构体或函数等并提高编译效率。一般情况下我们会在头文件的开头使用 #ifndef 和 #define 配合将整个头文件的内容包含在其中确保头文件只会被编译一次。
25. gdb调试
GDB调试方式准确定位行
使用过的调试方式有哪些
1GDB
2printf打印
调试工具GDB段错误地址非法操作指针
gdb调试
(在用GDB调试之前确定你的代码没有语法错误。设置断点的时候必须让主函数运行起来第一次断点设置设置在主函数当中的某一行这样编译器才能从入口函数进来。在gcc编译选项中一定要加入‘-g’。只有在代码处于“运行”或“暂停”状态时才能查看变量值。设置断点后程序在指定行之前停止
先制造一个产生段错误的代码
1) gcc -g test.c
2) gdb a.out
3)gdbl列出源文件内容
4gdbb 10设置断点在第10行
5gdbr :运行设置断点后一定要先运行才能进行单步调试往下
6gdbn:单步调试断点行是不被运行的n单步调试的时候不进子函数
7gdbs:单步运行断点行是不被运行的s单步调试的时候进子函数
单步调试时s进入子函数16版本的虚拟机有问题想进入系统函数12版本的正常
8) gdbp 变量名 查看变量值
9)q:退出调试界面
26. C语言中static函数与普通函数的区别是什么
静态函数
在函数的返回类型前加上关键字static函数就被定义成为静态函数。
函数的定义和声明默认情况下是extern的但静态函数只是在声明他的文件当
中可见不能被其他文件所用。
定义静态函数的好处
1其他文件中可以定义相同名字的函数不会发生冲突
2静态函数不能被其他文件所用。
局部静态变量
在局部变量之前加上关键字static局部变量就被定义成为一个局部静态变量。
1内存中的位置静态存储区
2初始化未经初始化的全局静态变量会被程序自动初始化为0自动对象的
值是任意的除非他被显示初始化
3作用域作用域仍为局部作用域当定义它的函数或者语句块结束的时候
作用域随之结束。
全局静态变量
在全局变量之前加上关键字static全局变量就被定义成为一个全局静态变量。
1内存中的位置静态存储区静态存储区在整个程序运行期间都存在
2初始化未经初始化的全局静态变量会被程序自动初始化为0自动对象的
值是任意的除非他被显示初始化
3作用域全局静态变量在声明他的文件之外是不可见的。准确地讲从定义之
处开始到文件结尾。
好处
定义全局静态变量的好处
1不会被其他文件所访问修改
2其他文件中可以使用相同名字的变量不会发生冲突
27. 写一个标准的宏 MINX这个宏输入两个参数并返回较小的一个?
#define MIN(A, B) ((A)(B)? (B) : (A))
28. 什么是MMU,MMU的作用
MMU是Memory Management Unit(内存管理单元)
1虚拟内存。有了虚拟内存可以在处理器上运行比实际物理内存大的应用程
序。为了使用虚拟内存操作系统通常要设置一个交换分区通常是硬盘通
过将不活跃的内存中的数据放入交换分区操作系统可以腾出其空间来为其它的
程序服务。虚拟内存是通过虚拟地址来实现的。
2内存保护。根据需要对特定的内存区块的访问进行保护通过这一功能我
们可以将特定的内存块设置成只读、只写或是可同时读写。
29. 左值和右值是什么
左值是指可以出现在等号左边的变量或表达式它最重要的特点就是可写(可寻址)。也就是说它的值可以被修改如果一个变量或表达式的值不能被修改那么它就不能作为左值。 右值是指只可以出现在等号右边的变量或表达式。它最重要的特点是可读。一般的使用场景都是把一个右值赋值给一个左值。 通常左值可以作为右值但是右值不一定是左值。
30. 什么是短路求值
在短路求值中当使用逻辑与操作时如果第一个表达式为false则不会对第二个表达式进行求值因为无论第二个表达式的结果如何整个逻辑与操作的结果都将为false。这意味着如果第一个表达式的结果已经确定逻辑与的结果为false进一步的计算就可以被跳过以提高性能和效率。
类似地当使用逻辑或操作时如果第一个表达式为true则不会对第二个表达式进行求值因为无论第二个表达式的结果如何整个逻辑或操作的结果都将为true。这意味着如果第一个表达式的结果已经确定逻辑或的结果为true进一步的计算就可以被跳过。
31. 数组指针与指针数组有什么区别
数组指针 指针数组 32. 函数指针与指针函数有什么区别
函数指针 指针函数 33. 指针常量常量指针指向常量的常量指针有什么区别? 34. 头文件的作用
头文件的作用主要表现为以下两个方面: 1.通过头文件来调用库功能。出于对源代码保密的考虑源代码不便(或不准)向用户公布只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能而不必关心接口是怎么实现的。编译器会从库中提取相应的代码。 2.头文件能加强类型安全检查。当某个接口被实现或被使用时其方式与头文件中的声明不一致编译器就会指出错误大大减轻程序员调试、改错的负担。 35. C语言怎么进行函数调用的
大多数CPU上的程序实现使用栈来支持函数调用操作栈被用来传递函数参数、存储返回信息、临时保存寄存器原有的值以备恢复以及用来存储局部变量。 函数调用操作所使用的栈部分叫做栈帧结构每个函数调用都有属于自己的栈帧结构栈帧结构由两个指针指定帧指针(指向起始)栈指针指向栈顶)函数对大多数数据的访问都是基于帧指针。下面是结构图: 36. 如何求解整型数的二进制表示中1的个数? 37. 如何求解二进制中0的个数 38. 交换两个变量的值不使用第三个变量。即a3,b5,交换之后a5,b3; 39. 给定一个整型变量a写两段代码第一个设置a的bit 3第二个清除a的bit 3。在以上两个操作中要保持其它位不变。 void clearbit3(int a)
{a~(13);
}
void setbit3(int a)
{a|13;
}
40. 以下没有直接使用ASIC C语言中malloc()函数而是二次封装为void *Malloc(U16 size),其目的是什么 typedef struct {
S16 size;
U16 maxsize;
}ST_MEMPOOL,*PT_MEMPOOL;
static ST_MEMPOOL stMemInfo{0,8192};
void * Malloc(U16 size)
{
U16 newsize stMemInfo.size size;
if(newsize stMemInfo.maxsize)
return NULL;
stMemInfo.size stMemInfo.size size;
return malloc(size);
}
答
这段代码是将malloc函数进行二次封装为Malloc(U16 size)函数。其目的是实现对内存分配的控制和限制。
在封装函数中首先计算出新的内存大小newsize stMemInfo.size size然后判断新的内存大小是否超过了预设的最大内存限制stMemInfo.maxsize。如果超过限制则返回NULL表示内存分配失败。
如果没有超过限制则更新内存池的大小stMemInfo.size stMemInfo.size size然后调用malloc函数进行实际的内存分配并返回分配的内存地址。
这段代码的目的是在进行内存分配时通过内存池的限制来控制和管理可用的内存大小。通过封装malloc函数可以在每次分配内存前进行大小检查以确保分配的内存不会超过预设的最大限制。这样可以避免内存溢出的问题并提供更好的内存使用控制和管理。
41. 数据类型占用的空间(字节数)
除了long型其他类型在32位4字节和64位8字节系统下占用字节数是一样的 42. 运算符优先级 优先级从高向低
单目运算符 ~ --
算术运算符 * / % -
移位运算符
关系运算符 !
位与运算符
异或运算符 ^
位或运算符 |
逻辑运算符 ||
条件运算符
赋值运算符 * / ...
口诀单算移关与异或逻条赋
结合性从右向左单条赋
43. 往内存0x12ff7c地址上存入一个整型数0x100
我们知道可以通过一个指针向其指向的内存地址写入数据那么这里的内存地址0x12ff7c其本质不就是一个指针嘛。所以我们可以用下面的方法
int *p ( int *) 0x12ff7c ;
*p 0x100 ; 数据结构部分
1. 单链表的增删改查操作实现思路
2. 单链表的倒置思路
linklist.h文件
#ifndef __LINKLIST__H_
#define __LINKLIST__H_
#include stdio.h
#include stdlib.h
typedef int datatype;
typedef struct node_t
{datatype data;struct node_t *next;
}link_node_t,*link_list_t;link_list_t CreateEplist();
int InsertIntoPostLinkList(link_list_t p,int post,datatype data);
void ShowLinkList(link_list_t p);
int LengthLinkList(link_list_t p);
int DeleteLinkList(link_list_t p,int post);
int IsEpLinkList(link_list_t p);
int ChangePostLinkList(link_list_t p,int post,datatype data);
int SearchDataLinkList(link_list_t p,datatype data);
void ReverseLinkList(link_list_t p);
void ClearLinkList(link_list_t p);
int DeleteDataLinkList(link_list_t p,int data); #endif
linklist.c文件
#include linklist.h //创建一个空的单向链表有头单向链表
link_list_t CreateEplist()
{ link_list_t h(link_list_t)malloc(sizeof(link_node_t)); if(NULLh) { printf(error); return NULL; } h-nextNULL; return h;
} //向单链表的指定位置插入数据
int InsertIntoPostLinkList(link_list_t p,int post,datatype data) /
{ if(post0||postLengthLinkList(p)) { printf(erro\n); return -1; } link_list_t pnew(link_list_t)malloc(sizeof(link_node_t)); if(NULLpnew) { printf(error); return -1; } pnew-datadata; pnew-nextNULL;for(int i0;ipost;i) { pp-next; } pnew-nextp-next; p-nextpnew; return 0;
} //遍历单向链表
void ShowLinkList(link_list_t p)
{ if(p-nextNULL) { printf(empt LinkList\n); } while(p-next!NULL) { pp-next; printf(%d,p-data); putchar(10); } }//求单链表长度的函数
int LengthLinkList(link_list_t p)
{ int lenth0; while(p-next!NULL) { pp-next; lenth; } return lenth;
} //删除单链表中指定位置的数据
int DeleteLinkList(link_list_t p,int post)
{ if(post0||postLengthLinkList(p)||IsEpLinkList(p)) { printf(erro\n); return -1; } for(int i0;ipost;i) { pp-next; } link_list_t pdelp-next; p-nextpdel-next; free(pdel); pdelNULL; return 0;
} //判断单链表是否为空1代表空0代表非空
int IsEpLinkList(link_list_t p)
{ return p-nextNULL; } // 修改指定位置上的数据
int ChangePostLinkList(link_list_t p,int post,datatype data)
{ if(post0||postLengthLinkList(p)||IsEpLinkList(p)) { printf(erro\n); return -1; } for(int i0;ipost;i) { pp-next; } p-datadata; return 0; } //查找指定数据出现的数据
int SearchDataLinkList(link_list_t p,datatype data)
{ if(IsEpLinkList(p)) { printf(error\n); return -1; } int i0; while(p-next!NULL){ pp-next; if(p-datadata) { return i; } i; } return -1;
} //转置链表
void ReverseLinkList(link_list_t p)
{ link_list_t qp-next; link_list_t tempNULL; p-nextNULL; while(q!NULL) { tempq-next; q-nextp-next; p-nextq; qtemp; } } //清空单向链表
void ClearLinkList(link_list_t p)
{ link_list_t pdelNULL; while(p-next!NULL) { pdelp-next; p-nextpdel-next; free(pdel); pdelNULL; } } //删除单向链表中出现的指定数据
int DeleteDataLinkList(link_list_t p,int data)
{ if(IsEpLinkList(p)) { printf(erro\n); return -1; } link_list_t pdelNULL; while(p-next!NULL) if(p-next-datadata) { pdelp-next; p-nextpdel-next; free(pdel); pdelNULL; } else{ pp-next; } return 0;
}
main.c文件
#include linklist.h
int main(int argc, const char *argv[])
{ link_list_t pCreateEplist();
#if 1 InsertIntoPostLinkList(p,0,1); InsertIntoPostLinkList(p,1,2); InsertIntoPostLinkList(p,2,3); InsertIntoPostLinkList(p,3,4); ShowLinkList(p); putchar(10);
#endif DeleteLinkList(p,0); ShowLinkList(p); putchar(10); ChangePostLinkList(p,1,5); ShowLinkList(p); putchar(10); printf(%d\n,SearchDataLinkList(p,5)); putchar(10); ReverseLinkList(p); ShowLinkList(p); putchar(10); DeleteDataLinkList(p,5); ShowLinkList(p); putchar(10); ClearLinkList(p); free(p);pNULL; ShowLinkList(p); return 0;
}
3. 单链表和双向链表的区别
单链表Singly Linked List和双向链表Doubly Linked List是两种常见的链表数据结构它们之间的主要区别在于节点内部的指针个数以及操作的复杂性。 单链表Singly Linked List 每个节点包含一个数据项和一个指向下一个节点的指针通常称为next。单链表只支持单向遍历即从头节点到尾节点。插入或删除节点时需要更新相邻节点的链接。单链表通常需要更少的内存空间因为每个节点只需要存储一个指针。 双向链表Doubly Linked List 每个节点包含一个数据项、一个指向前一个节点的指针通常称为prev和一个指向下一个节点的指针通常称为next。双向链表支持双向遍历可以从头节点到尾节点也可以从尾节点到头节点。插入或删除节点时不仅需要更新相邻节点的链接还需要更新前一个节点的指针。双向链表相对于单链表来说具有更强的操作灵活性因为它可以更高效地在任意位置插入或删除节点。
总结
单链表只有一个指向下一个节点的指针支持单向遍历操作相对简单。双向链表有两个指针分别指向前一个节点和后一个节点支持双向遍历操作相对复杂但更灵活。双向链表相比单链表占用更多的内存空间因为每个节点需要存储两个指针。
4. 栈和队列的区别
栈的特点
栈是一种后进先出Last In First OutLIFO的数据结构。元素的插入和删除操作都是在同一端进行该端称为栈顶。只能在栈顶进行插入压栈和删除弹栈操作。最后进入栈的元素第一个被删除即先进入的元素最后出栈。由于栈的特性适合处理具有嵌套关系和程序调用等后进先出的场景。常见的栈的应用包括函数调用栈、表达式求值和回溯等。常见栈的应用场景包括括号问题的求解表达式的转换和求值函数调用和递归实现深度优先搜索遍历等
队列的特点
队列是一种先进先出First In First OutFIFO的数据结构。元素的插入操作入队在一端进行称为队尾元素的删除操作出队在另一端进行称为队首。最先进入队列的元素最先被删除。队列可以用于实现流程控制例如任务调度、消息传递和缓冲区管理等。常见的队列的应用包括广度优先搜索算法、打印队列和消息队列等。常见的队列的应用场景包括计算机系统中各种资源的管理消息缓冲器的管理和广度优先搜索遍历等。
简要总结
栈是后进先出LIFO适合具有嵌套关系和程序调用等场景。队列是先进先出FIFO适合流程控制和顺序处理等场景。
5. 两个栈怎实现一个队列思路说一下
用两个栈s1和s2模拟一个队列时s1作输入栈,逐个元素压栈,以此模拟队列元素的入队。 当需要出队时将栈s1退栈并逐个压入栈s2中s1中最先入栈的元素在s2中处于栈顶。 s2退栈,相当于队列的出队实现了先进先出。 显然只有栈s2为空且s1也为空,才算是队列空。
6. 冒泡排序原理时间复杂度多少
通过不断交换相邻元素的位置来实现排序。其原理如下
从待排序的数据中依次比较相邻的两个元素如果它们的顺序不正确逆序则交换它们的位置。在第一轮比较结束后最大的元素会被交换到数组的末尾或是最小的元素被交换到数组的开头。在第二轮比较中从剩余的元素中再次重复上述比较和交换过程直到所有元素都按照从小到大或从大到小的顺序排序完成。
冒泡排序的关键在于每一轮比较都会确定一个当前轮次的最大或最小元素的位置这样每一轮循环都将缩小待排序元素的范围。
冒泡排序的时间复杂度为 O(n^2)其中 n 是待排序元素的个数。在最坏情况下需要进行 n-1 轮比较每轮比较需要进行 n-i-1 次交换操作i 表示当前轮次因此总的时间复杂度为 O(n^2)。冒泡排序的空间复杂度为 O(1)因为只需要少量的额外空间来存储临时变量。
值得注意的是冒泡排序是一种稳定的排序算法即相等元素的相对顺序在排序后保持不变。另外冒泡排序在实际应用中效率较低对于大规模数据的排序不具备优势。
7. 什么是二叉树、满二叉树 二叉树Binary Tree是一种特殊的树形数据结构其中每个节点最多有两个子节点分别称为左子节点和右子节点。 满二叉树Full Binary Tree是一种特殊的二叉树其中每个节点要么没有子节点叶节点要么同时有左子节点和右子节点。换句话说满二叉树的每个非叶节点的度数都是 2且所有叶节点都在同一层上。
二叉树的特点重点
(1)二叉树第kk1层上的节点最多为2的k-1次幂个。
(2)深度为kk1的二叉树最多有2的k次幂-1个节点。//满二叉树的时候
(3)在任意一棵二叉树中树叶的数目比度数为2的节点的数目多一
满二叉树的特点
在满二叉树中如果高度为 h则总共有 2^h - 1 个节点。当给定满二叉树的高度时可以直接计算出树中的节点数量。满二叉树是一种平衡二叉树即左右子树的高度差不超过 1。
8. 二叉树的前序中序后序遍历
前序 根---- 左 ----- 右
中序: 左---- 根 ----- 右
后序: 左---- 右 ----- 根
9.查找算法学过哪些二分查找的时间复杂度多少
顺序查找折半查找二分查找分块查找树表的查找。
二分查找的时间复杂度为O(log₂n)
10. 哈希表的原理
哈希表Hash table也叫散列表所谓hash表就是以 键-值(key-indexed) 的形式存储的数据结构。可以根据key来快速的查找到value。也就是说它通过把key值映射到表中一个位置来访问记录以加快查找的速度。这个映射函数叫做散列函数存放记录的数组叫做散列表。
当要插入/查找一个键值对时哈希表使用哈希函数计算键的哈希值并据此确定存储位置。如果该位置已经被占用就需要处理哈希冲突。哈希冲突发生在不同的键经过哈希函数计算后得到相同的哈希值的情况下。
常见的处理哈希冲突的方法有两种
链地址法Chaining链地址法的基本思想是把具有相同散列地址的记录放在同一个单链表中称为同义词链表。有 m 个散列地址就有 m 个单链表同时用数组 HT[O…m-1]存放各个链表的头指针凡是散列地址为 l 的记录都以结点方式插入到以 HT[i]为头结点的单链表中。
开放地址法的基本思想是把记录都存储在散列表数组中当某一记录关键字 key的初始散列地址H0H(key)发生冲突时以凡为基础 采取合适方法计算得到另一个地址H1,如果凡仍然发生冲突以凡为基础再求下一个地址H2若仍然冲突再求得H3。依次类推直至Hk不发生冲突为止则凡为该记录在表中的散列地址。常见的探测方法包括线性探测法二次探测法伪随机探测法
11. 排序算法学过哪些快速排序的实现原理 12. 如何判断一个链表是不是环形链表
定义两个指针一个称为慢指针slow另一个称为快指针fast。初始化慢指针和快指针为链表的头节点即指向链表的起始位置。遍历链表每次移动慢指针一步快指针两步。检查快指针是否为空即指向了链表的末尾节点或者超过了末尾节点。如果为空则说明链表不是环形链表因为快指针已经到达链表的末尾没有环。 如果快指针和慢指针相遇即两个指针指向同一个节点则说明链表是环形链表因为快指针追上了慢指针。
bool CircleInList(Link* pHead)
{
if(pHead NULL || pHead-next NULL)//无节点或只有一个节点并且无自环
return (false);
if(pHead-next pHead)//自环
return (true);
Link *pTemp1 pHead;//step 1
Link *pTemp pHead-next;//step 2
while(pTemp ! pTemp1 pTemp ! NULL pTemp-next ! NULL)
{
pTemp1 pTemp1-next;
pTemp pTemp-next-next;
}
if(pTemp pTemp1)
return (true);
return (false);
}
网络编程部分 当一个TCP连接长时间没有数据传输时可能会出现网络故障或者其它原因导致连接失效。
1. 服务器如何知道客户端client 是否已断电
心跳包技术心跳包之所以叫心跳包是因为它像心跳一样每隔固定时间发一次以此来告诉服务器这个客户端还活着。事实上这是为了保持长连接至于这个包的内容是没有什么特别规定的不过一般都是很小的包或者只包含包头的一个空包。
方法1应用层自己实现的心跳包 使用定时器-----适合有数据流动的情况)
思路一由应用程序自己发送心跳包来检测连接是否正常。 大致的方法是服务器端在一个 定时事件中 定时向客户端发送一个短小的数据包然后启动一个线程在该线程当中不断检测客户端的ACK应答包。如果在定时时间内收到了客户端的ACK应答包说明客户端与服务器端的TCP连接仍然是可用的。但是如果定时器已经超时、而服务器仍然没有收到客户端的ACK应答包即可以认为客户端已经断开。同样道理如果客户端在一定时间内没有收到服务器的心跳包则也会认为改TCP连接不可用了。
思路二心跳包应该由客户端在一个定时事件中定时向客户端发送一个短小的数据包如果服务端收到客户端的心跳包或正常报文则服务端的计数器归零服务端启动一个定时器定时累加计数器当计数器的累加值超过一定值时则认为客户端断开。 方法2TCP协议的KeepAlive保活机制 (使用socket选项SO_KEEPALIVE------适合没有数据流动的情况。 因为要考虑到一个服务器通常会连接很多个客户端因此由用户在应用层自己实现心跳包代码较多而且稍显复杂。而利用TCPIP协议层的内置的KeepAlive功能来实现心跳功能则简单得多。不论是服务器端还是客户端只要一端开启KeepAlive功能后就会自动的在规定时间内向对端发送心跳包 而另一端在收到心跳包后就会自动回复以告诉对端主机我仍然在线。 因为开启KeepAlive功能需要消耗额外的宽带和流量所以TCP协议层默认是不开启KeepAlive功能的。尽管这微不足道但是在按流量计费的环境下增加了费用另一方面KeepAlive设置不合理的话有可能会 因为短暂的网络波动而断开健康的TCP连接。并且默认的KeepAlive超时需要即2小时探测次数为5次。对于很多服务端应用程序来说2小时的空闲时间太长。因此我们需要手工开启KeepAlive功能并设置合理的KeepAlive参数。
2. 字节序
小端序little-endian - 低序字节存储在低地址
大端序big-endian- 高序字节存储在低地址
网络中传输的数据必须使用网络字节序即大端字节序
1主机字节序到网络字节序
u_long htonl (u_long hostlong); u_short htons (u_short short); //掌握这个
2网络字节序到主机字节序
u_long ntohl (u_long hostlong); u_short ntohs (u_short short);
2. 在网络应用中函数htons,htonl,ntohs,ntohl的作用是什么
小端序little-endian - 低序字节存储在低地址
大端序big-endian- 高序字节存储在低地址
网络中传输的数据必须使用网络字节序即大端字节序
1主机字节序到网络字节序
uint32_t htonl(uint32_t hostlong);//32位的主机字节序转换到网络字节序
uint16_t htons(uint16_t hostshort);//16位的主机字节序转换到网络字节序
2网络字节序到主机字节序
uint32_t ntohl(uint32_t netlong);//32位的网络字节序转换到主机字节序
uint16_t ntohs(uint16_t netshort);//16位的网络字节序转换到主机字节序
(皆为大小端的改变)
3. IPv4把所有的IP地址分为A、B、C、D、E五类。请写出B类地址和C类地 址的范围和掩码D类地址的用途是什么
主机号的第一个和最后一个都不能被使用第一个作为网段号最后一个最为广播地址。
A类1.0.0.1~126.255.255.254
B类128.0.0.1~~191.255.255.254
C类192.0.0.1~~223.255.255.254
D类组播地址224.0.0.1~~239.255.255.254
0.0.0.0在服务器中0.0.0.0指的是本机上的所有IPV4地址如果一个主机有两个IP地址192.168.1.1 和 10.1.2.1并且该主机上的一个服务监听的地址是0.0.0.0,那么通过两个ip地址都能够访问该服务。
127.0.0.1回环地址/环路地址所有发往该类地址的数据包都应该被loop back。
IP地址网络号主机号使用子网掩码来进行区分
网络号表示是否在一个网段内局域网
主机号标识在本网段内的ID同一局域网不能重复
子网掩码是一个32位的整数作用是将某一个IP划分成网络地址和主机地址 子网掩码长度是和IP地址长度完全一样 网络号全为1主机号全为0 D类地址用于多点播送 E类地址保留仅作实验和开发用 全零“0000”地址指任意网络。 全“1”的IP地址“255255255255”是当前子网的广播地址。 4. TCP和UDP的区别
TCPUDP名称传输控制协议(Transmission Control Protocol)用户数据报协议User Datagram Protocol是否连接面向连接无连接是否可靠可靠性通信数据无误、无丢失、无失序、无重复到达不可靠传输传输方式面向字节流面向报文连接对象个数一对一通信一对一、一对多、多对一、多对多交互通信适用场景需要可靠数据传输的场合对传输质量要求较高的通信即时通讯、小尺寸数据、广播/组播式通信。
TCP
TCP即传输控制协议是一种面向连接的传输层协议它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)。
适用场景
适合于对传输质量要求较高的通信
在需要可靠数据传输的场合通常使用TCP协议
MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议
UDP
UDPUser Datagram Protocol用户数据报协议是不可靠的无连接的协议。在数据发送前因为不需要进行连接所以可以进行高效率的数据传输。
适用场景
发送小尺寸数据如对DNS服务器进行IP地址查询时
适合于广播/组播式通信中。
MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议
5. TCP的三次握手过程
三次握手是TCP用来确保建立可靠连接的方式 1、客户端调用connect给服务器发送一个SYN同步包。
其中标志位SYN置为1初始序列号为客户端端随机生成的一个值seq
表示需要建立TCP连接。SYN1seqxx为随机生成数值 2、当服务器接收到客户端发送的SYN同步包会回一个ACK确认包同时给客户端发送一个SYN同步包。
其中标志位ACK置为1ACK确认号数值是在客户端发送过来的序列号seq的基础上加1
标志位SYN置为1序列号数值为服务器端随机生成的一个值seq
以便客户端收到信息时知晓自己的TCP建立请求已得到验证。SYN1ACK1ackx1seqyy为随机生成数值这里的ack加1可以理解为是确认和谁建立连接 3、客户端接收到服务器的确认包和同步包回一个ACK确认包三次握手完成。
其中标志位ACK置为1确认号数值为服务端发过来的序列号seq上加1
seq序列号数值由客户端的的初始化序列号加1。
ACK1,acky1seqx1。 服务器段accept返回客户端connect返回进行通信 扩展为什么需要三次握手两次不行吗
第一次握手客户端发送网络包服务端收到了。 这样服务端就能得出结论客户端的发送能力、服务端的接收能力是正常的。 第二次握手服务端发包客户端收到了。 这样客户端就能得出结论服务端的接收、发送能力客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。 第三次握手客户端发包服务端收到了。 这样服务端就能得出结论客户端的接收、发送能力正常服务器自己的发送、接收能力也正常。 因此需要三次握手才能确认双方的接收与发送能力是否正常。
如果是用两次握手则会出现下面这种情况
如客户端发出连接请求但因连接请求报文丢失而未收到确认于是客户端再重传一次连接请求。后来收到了确认建立了连接。数据传输完毕后就释放了连接客户端共发出了两个连接请求报文段其中第一个丢失第二个到达了服务端但是第一个丢失的报文段只是在某些网络结点长时间滞留了延误到连接释放以后的某个时间才到达服务端此时服务端误认为客户端又发出一次新的连接请求于是就向客户端发出确认报文段同意建立连接不采用三次握手只要服务端发出确认就建立新的连接了此时客户端忽略服务端发来的确认也不发送数据则服务端一致等待客户端发送数据浪费资源。
6. TCP的四次挥手过程
四次挥手是TCP用来确关闭可靠连接的方式 1、客户端调用close关闭通信会给服务器发一个FIN结束包
其中标志位FIN置为1初始序列号为客户端随机生成的一个值seq
表示需要断开TCP连接。FIN1seqxx由客户端随机生成 2、服务器端接收到客户端的FIN结束包会给客户端会一个ACK确认包。recv就会返回等于0.
其中标志位ACK置为1ACK确认号数值是在客户端发送过来的序列号seq的基础上加1
序列号数值为服务器端随机生成的一个值seq
以便客户端收到信息时知晓自己的TCP断开请求已经得到验证。ACK1ackx1seqyy由服务端随机生成
3、服务器调用close关闭通信文件描述符给客户端再发送一个FIN结束包。
其中标志位FIN置为1序列号为服务器端随机生成的一个(因为是半关闭状态服务器可能又发送了一些数据)
标志位ACK置为1,ACK确认号数值是在客户端发送过来的序列号seq的基础上加1
FIN1ACK1,ackx1seqzz由服务端随机生成 4、客户端接收到FIN结束包给服务器回一个ACK确认包断开连接。
标志位ACK置为1ACK确认号数值是在服务器端端发送过来的序列号seq的基础上加1
seq序列号数值由客户端的的初始化序列号加1。
ACK1ackz1seqx1 扩展挥手为什么需要四次 因为当服务端收到客户端的SYN连接请求报文后可以直接发送SYNACK报文。其中ACK报文是用来应答的SYN报文是用来同步的。但是关闭连接时当服务端收到FIN报文时很可能并不会立即关闭SOCKET所以只能先回复一个ACK报文告诉客户端“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了我才能发送FIN报文因此不能一起发送。故需要四次挥手。 7. TCP是如何保证可靠传输的
首先TCP 的连接是基于三次握手而断开则是四次挥手。确保连接和断开的可靠性。 其次TCP 的可靠性还体现TCP 通过校验和、ACK 应答、超时重传来记录哪些数据发送了哪些数据被接受了哪些没有被接受并且保证数据包按序到达保证数据传输不出差错。 再次TCP 的可靠性还体现在通过流量控制滑动窗口和拥塞控制来控制发送方发送速率。
数据分片在发送端对用户数据进行分片在接收端进行重组由TCP确定分片的大小并控制分片和重组 到达确认接收端接收到分片数据时根据分片数据序号向发送端发送一个确认包 超时重发发送方在发送分片后计时若超时却没有收到相应的确认包将会重发对应的分片 滑动窗口TCP连接双方的接收缓冲空间大小都固定接收端只能接受缓冲区能接纳的数据。 失序处理TCP的接收端需要重新排序接收到的数据。 重复处理如果传输的TCP分片出现重复TCP的接收端需要丢弃重复的数据。 数据校验TCP通过保持它首部和数据的检验和来检测数据在传输过程中的任何变化。 8. TCP的粘包问题 原因
TCP是面向流的, 流要说明就像河水一样, 只要有水, 就会一直流向低处, 不会间断。TCP为了提高传输效率, 发送数据的时候, 并不是直接发送数据到网路, 而是先暂存到系统缓冲, 超过时间或者缓冲满了, 才把缓冲区的内容发送出去, 这样, 就可以有效提高发送效率。所以会造成所谓的粘包, 即前一份Send的数据跟后一份Send的数据可能会暂存到缓冲当中, 然后一起发送。
粘包、拆包发生原因: 发生TCP粘包或拆包有很多原因常见的几点 1、要发送的数据大于TCP发送缓冲区剩余空间大小将会发生拆包。 2、待发送数据大于MSS最大报文长度TCP在传输前将进行拆包。 3、要发送的数据小于TCP发送缓冲区的大小TCP将多次写入缓冲区的数据一次发送出去将会发生粘包。 4、接收数据端的应用层没有及时读取接收缓冲区中的数据将发生粘包。
粘包、拆包解决办法: 解决问题的关键在于如何给每个数据包添加边界信息常用的方法有如下1、发送端给每个数据包添加包首部首部中应该至少包含数据包的长度这样接收端在接收到数据后通过读取包首部的长度字段便知道每一个数据包的实际长度了。 2、发送端将每个数据包封装为固定长度不够的可以通过补0填充这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。 3、可以在数据包之间设置边界如添加特殊符号这样接收端通过这个边界就可以将不同的数据包拆分开。 等等。4.延时、效率低
9. UDP的丢包问题
UDP,是面向报文形式, 系统不会缓冲, 也不会做优化, Send的时候, 就会直接Send到网络上, 对方收不收到也不管, 所以会造成丢包问题。
原因
1. 客户端发送过快网络状况不好或者超过服务器接收速度就会丢包。 2. 服务器收到包后还要进行一些处理而这段时间客户端发送的包没有去收造成丢包。解决方法
1. 客户端降低发送速度可以等待回包或者加一些延迟。 2. 服务器部分单独开一个线程去接收UDP数据存放在一个缓冲区中又另外的线程去处理收到的数据尽量减少因为处理数据延时造成的丢包。
10. TCP编程框架 服务器1.创建流式套接字socket()------------------------ 有手机2.指定本地的网络信息struct sockaddr_in---------- 有号码3.绑定套接字bind()------------------------------绑定手机4.监听套接字listen()----------------------------待机5.链接客户端的请求accept()----------------------接电话6.接收/发送数据recv()/send()--------------------通话7.关闭套接字close()-----------------------------挂机客户端1.创建流式套接字socket()-----------------------有手机2.指定服务器的网络信息struct sockaddr_in-------有对方号码3.请求链接服务器connect()----------------------打电话4.发送/接收数据send()/recv()-------------------通话5.关闭套接字close()--------------------------- 挂机
11. TCP/IP网络模型分为几层每一层什么作用都有哪些协议 网络接口和物理层屏蔽硬件差异驱动向上层提供统一的操作接口。
网络层提供端对端的传输可以理解为通过IP寻址机器。
传输层决定数据交给机器的哪个任务进程去处理通过端口寻址。
应用层应用协议和应用程序的集合。
12. OSI模型 常见网络协议:
网络接口和物理层:ppp拨号协议老式电话线上网方式ARP地址解析协议 IP--MACRARP:反向地址转换协议 MAC--IP
网络层: IP(IPV4/IPV6):网间互连的协议ICMP网络控制管理协议ping命令使用IGMP网络分组管理协议广播和组播使用
传输层TCP传输控制协议UDP用户数据报协议
应用层SSH:加密协议telnet远程登录协议FTP文件传输协议HTTP超文本传输协议DNS地址解析协议SMTP/POP3邮件传输协议 注意TCP和IP是属于不同协议栈层的只是这两个协议属于协议族里最重要的协议所以协议栈或者模型以之命名了。
13. 如何实现并发服务器
同一个时刻可以响应多个客户端的请求常用的模型有多进程模型/多线程模型/IO多路复用模型。 多进程和多线程实现的思想每当有一个客户端连接成功就创建一个子进程或线程和这个客户端通信父进程或主线程循环等待下一个客户端连接。
借助select、poll、epoll机制将新连接的客户端描述符增加到描述符表中只需要一个线程即可处理所有的客户端连接。 select、poll、epoll共同优点 1占用资源少,因为是单进程处理。相比于多进程、多线程 2性能好可一次等待多个进程。 14. Select、poll、epoll的区别
select实现IO多路复用特点
1. 一个进程最多只能监听1024个文件描述符 千级别 2. select被唤醒之后需要重新轮询一遍驱动的poll函数效率比较低消耗CPU资源; 3. select每次会清空表每次都需要拷贝用户空间的表到内核空间效率低一个进程0~4G0~3G是用户态3G~4G是内核态拷贝是非常耗时的;
select优点: ( 1select()的可移植性更好在某些Unix系统上不支持poll() 2select() 对于超时值提供了更好的精度微秒而poll是毫秒。
poll实现IO多路复用的特点
1. 优化文件描述符个数的限制;(根据poll函数第一个函数的参数来定如果监听的事件为1个则结构体数组元素个数为1如果想监听100个那么这个结构体数组的元素个数就为100由程序员自己来决定)。 2. poll被唤醒之后需要重新轮询一遍驱动的poll函数效率比较低。 3. poll不需要重新构造文件描述符表只需要从用户空间向内核空间拷贝一次数据即可。
poll优点 1poll() 不要求开发者计算最大文件描述符加一的大小。 2poll() 在应付大数目的文件描述符的时候相比于select速度更快 3它没有最大连接数的限制原因是它是基于链表来存储的。
epoll实现IO多路复用的特点
1. 监听的最大的文件描述符没有个数限制理论上取决与你自己的系统。 2. 异步I/OEpoll当有事件产生被唤醒之后文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符不需要轮询效率高。 3. epoll不需要重新构造文件描述符表只需要从用户空间向内核空间拷贝一次数据即可。
epoll优点
1支持一个进程打开大数目的socket描述符(FD) 2IO效率不随FD数目增加而线性下降 3使用mmap加速内核与用户空间的消息传递。
15. selet实现
实现步骤
1.创建表
2.将关心文件描述符添加到表中
3.进入循环调用select函数检测
4.当有一个或多个事件产生select函数返回
5.判断是哪个或哪几个产生事件
6.处理事件 6. 优点 16. poll实现
1.创建表
2.将关心文件描述符添加到表中
3.进入循环调用poll函数监测
4.遍历数组检查哪些文件描述符已经就绪。
5.根据就绪的文件描述符进行相应的操作例如读取数据、写入数据或处理异常情况。 17. epoll
1. 创建树
2. 将关心文件描述符添加到树上
3. 进入循环在循环中使用 epoll_wait 函数等待事件发生并阻塞等待。
4.当事件发生后epoll_wait 函数返回就绪的文件描述符和对应的事件。根据就绪的文件描述符进行相应的操作。 18. 广播和组播的区别
广播方式发给所有的主机。过多的广播会大量占用网络带宽造成广播风暴影响正常的通信。
组播是一个人发送加入到多播组的人接收数据。多播方式既可以发给多个主机又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)。
19. 用过抓包工具么抓过什么数据包 wireshark抓包工具。
TCP的三次握手和四次挥手modbusmqtt
20. 学过什么数据库增删改查语句
1SQLite。 增加数据INSERT INTO 表名[(列1,列2,...)] VALUES(值1,值2,...)
INSERT INTO myemp(empno,ename,job,mgr,hiredate,sal,deptno)
VALUES(8888,张三,厨师,7839,DATETIME(now,localtime),10000,40);
删除数据DELETE FROM 表名 [WHERE 删除条件(s)]
DELETE FROM myemp WHERE deptno30;修改数据UPDATE 表名 SET 列名1值1,列名2值2,...[WHERE 更新条件(s)]
UPDATE myemp SET sal5000,comm2000 WHERE enameSMITH;查询数据 创建表命令 CREATE TABLE
删除表命令
DROP TABLE
21. 如何判断在不同网络中的两台电脑是否可以通讯,用什么工具或者方法,
Ping对方的IP;用路由器实现两台电脑通信。
22. 请比较面向连接服务和无连接服务的异常点,TCP/UDP分别是什么服务,QQ聊天通信中使用的是哪种服务。
面向连接(connection-oriented)在发送任何数据之前要求建立会话连接(与拨打电话类似)然后才能开始传送数据传送完成后需要 释放连接。建立连接是需要分配相应的资源如缓冲区以保证通信能正常进行。这种方法通常称为“可靠”的网络业务。它可以保证数据以相同的顺序到达。面向连 接的服务在端系统之间建立通过网络的虚链路。
无面向连接(connectiongless)不要求发送方和接收方之间的会话连接。发送方只是简单地开始向目的地发送数据分组(称为数据报)。这与现 在风行的手机短信非常相似:你在发短信的时候只需要输入对方手机号就OK了。此业务不如面向连接的方法可靠但对于周期性的突发传输很有用。系统不必为 它们发送传输到其中和从其中接收传输的系统保留状态信息。无连接网络提供最小的服务仅仅是连接。无连接服务的优点是通信比较迅速,使用灵活方便,连接开 销小;但可靠性低,不能防止报文的丢失,重复或失序. 适合于传送少量零星的报文。 TCP是面向连接UDP是无连接。
QQ是UDP。
23. 什么是域名解析及域名解析的过程,
英文缩写DNS(Domain Name System) 简单的说就是把你在浏览器地址栏输入的网页地址解析成IP地址.由于IP地址是用四段数字组成,相对英文来说不好记,通过域名解析就可以不用记IP地址,直接通过输入的英文或汉字链接到网站上了.
1.客户机提出域名解析请求,并将该请求发送给本地的域名服务器。
2.当本地的域名服务器收到请求后,就先查询本地的缓存,如果有该纪录项,则本地的域名服务器就直接把查询的结果返回。
3.如果本地的缓存中没有该纪录,则本地域名服务器就直接把请求发给根域名服务器,然后根域名服务器再返回给本地域名服务器一个所查询域(根的子域)的主域名服务器的地址。
4.本地服务器再向上一步返回的域名服务器发送请求,然后接受请求的服务器查询自己的缓存,如果没有该纪录,则返回相关的下级的域名服务器的地址。
5.重复第四步,直到找到正确的纪录。
6.本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时还将结果返回给客户机。
IO与进程、线程部分
1. 标准IO和文件IO的区别 标准IO 文件IO 在C库中定义输入输出的函数 在posix中定义的输入输出的函数 有缓冲机制 围绕流操作FILE* 默认打开三个流stdin/stdout/stderr 只能操作普通文件 没有缓冲机制 围绕文件描述符操作int非负整数 默认打开三个文件描述符0/1/2 除d外其他任意类型文件 打开文件fopen/freopen 读写文件fgetc/fputc、fgets/fputs、fread/fwrite 关闭文件fclose 文件定位rewind、fseek、ftell 打开文件open 读写文件read、write 关闭文件close 文件定位lseek
2. 静态库和动态库的区别
静态库和动态库本质区别是代码被载入时刻不同。
1 静态库在程序编译时会被连接到目标代码中。
优点程序运行时将不再需要该静态库运行时无需加载库运行速度更快
缺点静态库中的代码复制到了程序中因此体积较大
静态库升级后程序需要重新编译链接
2 动态库是在程序运行时才被载入代码中。
优点程序在执行时加载动态库代码体积小
程序升级更简单
不同应用程序如果调用相同的库那么在内存里只需要有一份该共享库的实例。
缺点运行时还需要动态库的存在移植性较差
3. 怎么创建进程
#include stdio.h
#include unistd.hint main(int argc, char const *argv[])
{int num 10;pid_t id;id fork(); //创建子进程if(id 0){perror(fork err);return -1;}else if(id 0){//in the childprintf(in the child\n);}else{// int s;// wait(s); //回收子进程资源,阻塞函数// printf(%d\n, s);wait(NULL);//in the parentprintf(in the parent\n);while(1);}// while(1);return 2;
}
4. 什么是守护进程
1、 特点 守护进程是后台进程不依赖于控制终端
生命周期比较长从运行时开启系统关闭时结束
它是脱离控制终端且周期执行的进程。
2. 步骤 1 创建子进程父进程退出
让子进程变成孤儿进程成为后台进程fork()
2 在子进程中创建新会话
让子进程成为会话组组长为了让子进程完全脱离终端setsid()
3 改变进程运行路径为根目录
原因进程运行的路径不能被卸载chdir(/)
4 重设文件权限掩码
目的增大进程创建文件时权限提高灵活性umask(0)
5 关闭文件描述符
将不需要的文件关闭close()
5. 什么是僵尸进程什么是孤儿进程
若父进程先结束子进程成为孤儿进程被init进程收养子进程变成后台进程。
若子进程先结束父进程如果没有及时回收子进程变成僵尸进程要避免僵尸进程产生
6. 时间片了解么 时间片timeslice又称为 “量子”quantum或 “处理器片”processor slice是分时操作系统分配给每个正在运行的进程微观上的一段 CPU 时间在抢占内核中是从进程开始运行直到被抢占的时间。 简单来说时间片就是 CPU 分配给各个程序的时间即该进程允许运行的时间。如果进程在时间片结束时还在运行则 CPU 将被强制剥夺并分配给另一个进程如果进程在时间片结束前就阻塞或结束则 CPU 会在阻塞或结束时当即进行切换。
7. 进程和线程区别
共性都为操作系统提供了并发执行能力
不同点
调度和资源线程是系统调度的最小单位进程是资源分配的最小单位
地址空间方面同一个进程创建的多个线程共享进程的资源进程的地址空间相互独立
通信方面线程通信相对简单只需要通过全局变量可以实现但是需要考虑临界资源保护的问题进程通信比较复杂需要借助进程间的通信机制(借助3g-4g内核空间)
安全性方面线程安全性差一些当进程结束时会导致所有线程退出进程相对安全
8. 线程的同步怎么实现
通过信号量实现线程间同步。
信号量由信号量来决定线程是继续运行还是阻塞等待信号量代表某一类资源其值表示系统中该资源的数量
信号量是一个受保护的变量只能通过三种操作来访问初始化、操作(申请资源)、操作(释放资源)
信号量的值为非负整数
9. 线程的互斥怎么实现
临界资源一次仅允许一个进程所使用的资源
临界区指的是一个访问共享资源的程序片段
互斥多个线程在访问临界资源时同一时间只能一个线程访问 互斥锁通过互斥锁可以实现互斥机制主要用来保护临界资源每个临界资源都由一个互斥锁来保护线程必须先获得互斥锁才能访问临界资源访问完资源后释放该锁。如果无法获得锁线程会阻塞直到获得锁为止。
10. 进程间通信方式有哪些哪种效率最高
无名管道、有名管道、信号消息队列共享内存信号灯集、套接字(socket)
共享内存效率最高
11. 线程同步通讯的方法
1信号量2读写锁3条件变量4互斥锁5自旋锁
12. 什么时候用进程什么时候用线程
1创建和销毁较频繁使用线程因为创建进程花销大。
2需要大量数据传送使用线程因为多线程切换速度快不需要跨越进程边界。
3安全稳定选进程快速频繁选线程
13. 说明什么是上下文切换 有很多角度有进程上下文有中断上下文。
进程上下文一个进程在执行的时候CPU的所有寄存器中的值、进程的状态以及堆栈中的内容当内核需要切换到另一个进程时它需要保存当前进程的所有状态即保存当前进程的进程上下文以便再次执行该进程时能够恢复切换时的状态继续执行。
中断上下文由于触发信号导致CPU中断当前进程转而去执行另外的程序。那么当前进程的所有资源要保存比如堆栈和指针。保存过后转而去执行中断处理程序快读执行完毕返回返回后恢复上一个进程的资源继续执行。这就是中断的上下文。
14. 通信方式的优缺点对比 无名管道Unnamed Pipe 优点 简单易用是最基本的进程间通信机制。仅限于具有亲缘关系的进程间通信。只能实现单向通信即数据流只能从一个进程传递给另一个进程。缺点 容量有限传输数据量受限。只能在同一个主机上进行通信。只能用于单个读者和单个写者之间的通信。 有名管道Named Pipe 优点 允许无关进程间的通信不仅限于具有亲缘关系的进程。具有独立的读写权限可以在同一管道上进行双向通信。缺点 容量有限传输数据量受限。需要通过文件操作系统来进行配置和使用。与管道相比有名管道的设置和管理较为繁琐。 信号Signal 优点 提供异步通信机制用于进程间的通知和事件处理。可以响应外部事件例如按下CtrlC键等。缺点 信号通信的灵活性有限只能传递简单的信号值不能传输大量数据。信号可能导致竞争条件和不可预测的行为。不适用于高速数据传输和复杂的通信需求。 消息队列Message Queue 优点 提供异步通信机制发送和接收进程无需同时存在。具有缓冲区使得消息的发送和接收可以彼此独立进行提高了系统的灵活性和容错性。缺点 通信开销较大因为需要将数据从用户空间复制到内核空间。需要定义特定的消息格式和协议以保持消息的一致性和正确性。 共享内存Shared Memory 优点 最快的进程间通信方式进程可以直接访问共享内存区域无需数据复制。适合大量数据的高速传输。允许进程间异步通信。缺点 需要额外的同步机制如信号量或互斥锁来控制对共享内存的并发访问。可能引发一些资源管理和数据一致性的问题。 信号灯集Semaphore 优点 用于进程间同步和互斥操作实现进程间的资源共享和互斥访问。缺点 使用复杂需控制信号灯的值和操作。可能导致竞争条件和死锁问题。 套接字Socket 优点 提供网络通信的能力允许不同主机上的进程进行通信。提供可靠的、面向连接的通信适用于传输大量数据和实时通信。支持多种通信协议如TCP/IP和UDP/IP。缺点 编程复杂需处理网络通信的细节。通信开销较大需要进行数据的序列化和反序列化。
15. 有名管道和无名管道的区别 16. 共享内存的实现方式
0创建key值 // 两个进程key相同
1创建或打开共享内存 shmget
2映射 shmat
3取消映射 shmdt
4删除共享内存 shmctl
17. 消息队列的实现方式
1创建key值 ftok
2创建或打开消息队列 msgget
3添加消息/读取消息 msgsnd/msgrcv
4删除消息队列 msgctl
18. fork和vfork区别
1. fork( )的子进程拷贝父进程的数据段和代码段; vfork( )的子进程与父进程共享数据段 2.fork()的父子进程的执行次序不确定;vfork( )保证子进程先运行在调用exec或exit之前与父进程数据是共享的在它调用exec或exit之后父进程才可能被调度运行。 3. vfork( )保证子进程先运行在它调用exec或exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进—步动作则会导致死锁。 4.当需要改变共享数据段中变量的值则拷贝父进程。
fork()和vfork()都是在UNIX和类UNIX系统中使用的系统调用用于创建一个新的进程。它们之间的主要区别在于它们对父子进程之间的共享资源的处理方式。 fork(): fork()调用通过创建一个父进程的副本来创建一个新的子进程。父进程和子进程各自拥有独立的虚拟地址空间和内存映像。父进程的所有资源包括打开的文件、信号处理器、锁等都会被复制到子进程中。父进程和子进程之间共享的唯一资源是打开的文件描述符的引用计数也就是说它们引用相同的文件表项。但是它们使用不同的文件描述符表和虚拟内存地址空间。子进程将从fork()调用之后的下一行代码开始执行。fork()调用返回两次在父进程中返回子进程的进程IDPID在子进程中返回0。这样父子进程可以根据返回值进行不同的操作。 vfork(): vfork()调用是为了解决fork()的性能问题而引入的。vfork()调用创建一个新的子进程但是它会暂停父进程的执行直到子进程调用exec()系统调用或者_exit()系统调用或者父进程调用exit()系统调用。父进程和子进程共享同一个虚拟地址空间也就意味着它们共享同一份代码和数据段。子进程在执行期间不能访问任何变量否则可能导致意外的行为。vfork()调用返回两次与fork()类似但是父进程会等待子进程调用exec()、_exit()或者exit()之后才恢复执行。
总结
fork()创建父子进程的完全副本拥有独立的资源和虚拟地址空间。vfork()创建共享虚拟地址空间的子进程父进程暂停执行直到子进程调用exec()、_exit()或者exit()。通常情况下fork()更常见且更安全因为它避免了在共享虚拟地址空间中对变量的意外修改。
19. 线程的死锁怎么避免
是指两个或两个以上的进程/线程在执行过程中由于竞争资源或者由于彼此通信而造成的一种阻塞的现象若无外力作用它们都将无法推进下去
死锁产生的四个必要条件
1、互斥使用即当资源被一个线程使用(占有)时别的线程不能使用
2、不可抢占资源请求者不能强制从资源占有者手中夺取资源资源只能由资源占有者主动释放。
3、请求和保持即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待即存在一个等待队列P1占有P2的资源P2占有P3的资源P3占有P1的资源。这样就形成了一个等待环路。
注意当上述四个条件都成立的时候便形成死锁。当然死锁的情况下如果打破上述任何一个条件便可让死锁消失。
20. sem_ wait(sem_ *sem)的功能是什么和sem_trywait(sem_ *sem)有什么区别?
sem_wait(sem_t *sem): 这个函数用于对信号量进行等待操作。当调用sem_wait时如果信号量的值大于0则将信号量的值减1并继续执行。如果信号量的值为0表示没有可用资源则会阻塞当前线程直到有资源可用。当其他线程或进程通过sem_post增加信号量的值时被阻塞的线程将解除阻塞并继续执行。
sem_trywait(sem_t *sem): 这个函数也用于对信号量进行等待操作。调用sem_trywait时它会尝试对信号量进行等待如果信号量的值大于0则将信号量的值减1并继续执行。如果信号量的值为0sem_trywait函数并不会阻塞当前线程而是直接返回不对信号量进行等待。
所以sem_wait函数会阻塞当前线程并等待信号量的值变为非零而sem_trywait函数会立即返回不对信号量进行等待。使用这两个函数可以根据实际情况选择阻塞等待或立即返回的方式来处理信号量。
21. 程序和进程的区别
1程序是编译好的可执行文件存放在磁盘上的指令和数据的有序集合文件 进程是一个独立的可调度的任务它是执行一个程序所分配的资源的总称
2程序是静态的观念进程是动态的观念包括创建、调度、执行和消亡
3程序是永存的进程是暂时的是程序在数据集上的一次执行有创建有撤销存在是暂时 的
4进程具有并发性而程序没有
5进程是竞争计算机资源的基本单位程序不是。
6进程是程序的一次执行过程 C部分
1. 指针和引用相同点和不同点相互转换
相同点
1.都是地址的概念指针指向某一内存、它的内容是所指内存的地址;引用则是某块内存的别名。
2.从内存分配上看:两者都占内存程序为指针会分配内存一般是4个字节;而引用的本质是指针常量指向对象不能变但指向对象的值可以变。两者都是地址概念所以本身都会占用内存。
不同点 1.指针是实体而引用是别名。 2.指针和引用的自增运算符意义不同指针是对内存地址自增而引用是对值的自增。
3.引用使用时无需解引用(*)指针需要解引用;(关于解引用大家可以看看这篇博客传送门)。
4.引用只能在定义时被初始化一次之后不可变;指针可变。 5.引用不能为空指针可以为空。 6.sizeof 引用得到的是所指向的变量(对象)的大小而sizeof 指针得到的是指针本身的大小在32位系统指针变量一般占用4字节内存。
转换 2. 类和结构体区别
1.类型不同结构体是一种值类型而类是引用类型。值类型用于存储数据的值引用类型用于存储对实际数据的引用。那么结构体就是当成值来使用的类则通过引用来对实际数据操作。
2.存储不同结构使用栈存储而类使用堆存储。栈的空间相对较小.但是存储在栈中的数据访问效率相对较高.堆的空间相对较大.但是存储在堆中的数据的访问效率相对较低。
3.作用不同类是反映现实事物的一种抽象而结构体的作用只是一种包含了具体不同类别数据的一种包装结构体不具备类的继承多态特性
4.初始化不同类可以在声明的时候初始化结构不能在申明的时候初始化不能在结构中初始化字段否则报错。 3. C中Static、const作用
static作用
类中的static主要用于创建静态成员变量、静态成员函数与静态局部变量。
普通的成员变量使用static修饰就是静态成员变量。这种静态成员变量需要类内声明类外初始化。
静态成员函数没有this指针因此不能调用非静态成员非静态成员变量和成员函数。
使用static修饰的局部变量就是静态局部变量。静态局部变量所在的代码块被第一次调用时静态局部变量内存开辟与非静态局部变量不同的是代码块调用完成后静态局部变量不会被销毁下次代码被调用时继续使用之前的静态局部变量直到程序运行终止后才会被自动销毁。
const作用
const修饰的成员函数称为常成员函数。
const修饰对象表示该对象是一个常量对象。
const修饰的成员变量表示常成员变量。
const修饰局部变量表示局部变量不可变通常用于引用类型的函数参数。 4. C和C的区别
1. C是面向过程的语言而C是面向对象的语言
2. C和C动态管理内存的方法不一样C是使用malloc/free函数而C除此之外还有new/delete关键字
3. C中的struct和C的类C的类是C所没有的但是C中的struct是可以在C中正常使用的并且C对struct进行了进一步的扩展使struct在C中可以和class一样当做类使用而唯一和class不同的地方在于struct的成员默认访问修饰符是public,而class默认的是private;
4. C支持函数重载而C不支持函数重载而C支持重载的依仗就在于C的名字修饰与C不同例如在C中函数int fun(int ,int)经过名字修饰之后变为 _fun_int_int ,而C是 _fun一般是这样的所以C才会支持不同的参数调用不同的函数
5. C中有引用而C没有
6. C全部变量的默认链接属性是外链接而C是内连接
7. C 中用const修饰的变量不可以用在定义数组时的大小但是C用const修饰的变量可以
8. C语言和C的最大区别在于它们解决问题的思想方法不一样。C语言主要用于嵌入式领域驱动开发等与硬件直接打交道的领域 C可以用于应用层开发用户界面开发等于操作系统打交道的领域。
5. 什么是构造函数、析构函数
构造函数 是一种特殊的方法。主要用来在创建对象时初始化对象 即为对象成员变量赋初始值总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。
析构函数(destructor) 与构造函数相反当对象结束其生命周期如对象所在的函数已调用完毕时系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作例如在建立对象时用new开辟了一片内存空间delete会自动调用析构函数后释放内存。
6. New、delete、malloc、free区别
1、new/delete是C的操作符而malloc/free是C中的函数。
2、new做两件事一是分配内存二是调用类的构造函数同样delete会调用类的析构函数和释放内存。而malloc和free只是分配和释放内存。
3、new建立的是一个对象而malloc分配的是一块内存new建立的对象可以用成员函数访问不要直接访问它的地址空间malloc分配的是一块内存区域用指针访问可以在里面移动指针new出来的指针是带有类型信息的而malloc返回的是void指针。
4、new/delete是保留字不需要头文件支持malloc/free需要头文件库函数支持。
7. 虚函数的作用
指向基类的指针在操作它的多态类对象时可以根据指向的不同类对象调用其相应的函数这个函数就是虚函数。 虚函数的作用:在基类定义了虚函数后可以在派生类中对虚函数进行重新定义并且可以通过基类指针或引用在程序的运行阶段动态地选择调用基类和不同派生类中的同名函数。(如果在派生类中没有对虚函数重新定义则它继承其基类的虚函数。) 8. 对多态的理解多态的原理
多态可以被概括为“一个接口多重状态”只写一个函数接口在程序运行时才决定调用类型对应的代码。
多态的使用需要有以下条件
必须使用公有继承派生类要有函数覆盖重写基类引用/指针指向派生类对象
多态性的原理可以总结为以下步骤
声明一个基类并在其中定义一个或多个虚函数。派生类继承基类并重写覆盖基类中的虚函数。创建基类的指针或引用并将其指向派生类的对象。在运行时通过基类指针或引用调用虚函数。运行时系统根据对象的实际类型动态确定调用的函数实现。根据对象的实际类型在虚函数表中查找对应的函数地址并进行调用。
多态性使得代码具备更高的灵活性可以通过统一的接口处理不同类型的对象而不需要显式地针对每种类型编写不同的代码逻辑。它提供了代码的可扩展性和可维护性同时也是面向对象编程中的一项重要特性。
9. 为什么在继承中将析构函数定义为虚析构函数
在C中将析构函数定义为虚析构函数是为了解决基类指针指向派生类对象时的内存释放问题。当一个类通过继承关系被作为基类使用时通常会使用基类指针或引用指向派生类的对象。如果基类的析构函数不是虚析构函数没有加上virtual关键字那么在通过基类指针或引用释放派生类对象时只会调用基类的析构函数而不会调用派生类的析构函数这可能导致派生类中动态分配的资源没有被正确释放造成内存泄漏。
使用虚析构函数可以解决这个问题。当基类的析构函数被声明为虚析构函数时在删除派生类对象时会先调用派生类的析构函数然后再调用基类的析构函数。这样可以确保在释放对象时正确调用派生类和基类的析构函数从而正确释放对象中的资源。
10. vector与list的区别vector、list、map是什么存储结构
vector内部靠数组实现随机存取固定位置读写比较高效相对插入删除操作效率较低支持下标操作。
list内部由双向链表实现因此元素的内存空间不连续不能通过下标进行元素访问但是能高效地进行插入和删除操作。
Vector的存储结构是连续的数组元素在内存中依次排列。List的存储结构是双向链表通过指针将元素连接起来。Map是一种关联容器它将键和值配对存储。内部实现通常是基于红黑树以保持键的有序性并提供高效的查找和插入操作。
11. 迭代器的作用
迭代器Iterator是一种抽象的数据访问方式它提供了一种统一的方法遍历容器或容器类似的数据结构中的元素而不需要了解底层的存储结构。迭代器可以在容器中前进、后退以及访问当前位置的元素使得对容器的遍历和操作更加方便和灵活。
迭代器的主要作用如下 遍历容器迭代器提供了对容器中元素的顺序访问可以遍历容器中的每一个元素而不需要使用特定的循环结构如for循环或while循环。 访问元素通过迭代器可以直接访问容器中当前位置的元素获取或修改其值。 插入和删除元素某些类型的迭代器如双向迭代器和随机访问迭代器还支持在容器中插入和删除元素而不会影响其他迭代器的有效性。 支持泛型编程迭代器的设计是为了支持泛型编程使得可以使用相同的算法和操作来处理不同类型的容器只需改变迭代器的类型即可。
总的来说迭代器提供了一种统一的、通用的方式来访问和操作容器中的元素使得代码更加简洁、可读性更高并且增加了代码的灵活性和可重用性。
12. 智能指针有哪些
首先我们C中有四种智能指针都在标准名字空间下使用时都需要引入头文件*include 这四种指针分别是
auto_ptr自动指针C98中引入目前推荐不使用 unique_ptr 唯一指针C11引入 shared_ptr共享指针C11引入 weak_ptr虚指针C11中引入
13. C11新特性有哪些 自动类型推导auto使用auto关键字可以自动推导变量的类型。 基于范围的for循环使用范围range迭代器遍历容器中的元素。 列表初始化Initializer lists可以使用初始化列表语法对变量和容器进行初始化。 初始化列表构造函数类可以定义初始化列表构造函数来接受和处理初始化列表。 空指针nullptr引入了nullptr关键字来表示空指针替代了NULL和0的模棱两可的语义。 强类型枚举类strongly typed enums增强了枚举类型的类型安全性可以指定底层类型以及作用域。 Lambda表达式允许在代码中定义匿名函数提供了更方便的函数对象编程方式。 函数对象Function objects新增了函数对象std::function和std::bind方便地封装函数和成员函数。 智能指针Smart pointers引入了std::shared_ptr、std::unique_ptr和std::weak_ptr等智能指针类用于管理动态分配的内存提供更安全和方便的内存管理机制。 新增关键字和特殊标识符引入了关键字如constexpr、decltype、static_assert等以及特殊标识符如override和final等用于增强语言的表达能力和类型检查。 移动语义Move semantics引入了右值引用rvalue references和移动构造函数、移动赋值运算符等机制提供了更高效的资源管理和对象移动操作。 并发编程库Concurrency library引入了std::thread、std::mutex和std::atomic等线程和原子操作相关的库方便进行并发编程。
这只是C11引入的一些主要特性还有其他的一些特性如正则表达式、统一的初始化语法、类型推导等等它们一起显著改进了C语言的功能和表达能力。
14. 深拷贝和浅拷贝区别
深拷贝Deep Copy和浅拷贝Shallow Copy是在对象复制过程中常用的两种方式它们具有以下区别 拷贝的内容 浅拷贝执行浅拷贝时只复制对象本身的值包括基本数据类型的成员变量的值以及指针成员变量的地址。这意味着原对象和拷贝对象会共享指针指向的内存区域。深拷贝执行深拷贝时除了复制对象本身的值外还会复制指针指向的内存区域使得原对象和拷贝对象拥有彼此独立的内存空间。 对象间的关联 浅拷贝在浅拷贝中原对象和拷贝对象共享指针指向的内存如果其中一个对象修改了共享内存的值另一个对象也会受到影响。深拷贝由于深拷贝会为指针成员变量分配独立的内存空间所以原对象和拷贝对象之间不存在共享内存的情况。修改一个对象的数据不会影响另一个对象。 对象生命周期 浅拷贝当原对象被销毁时共享的指针将成为野指针并且如果拷贝对象也没有进行释放就会导致内存泄漏或访问非法内存的问题。深拷贝拷贝对象拥有独立的内存空间所以在对象销毁时能够正确释放自己的资源不会导致野指针或内存泄漏问题。
在使用自定义的类或结构体时需要根据具体需求来选择深拷贝或浅拷贝。如果对象的成员变量不包含指针或其他动态分配的资源浅拷贝可以满足需求。但如果对象包含指针成员或动态分配的资源为了避免潜在的问题深拷贝是更安全和可靠的方式。
需要注意的是执行深拷贝需要自定义拷贝构造函数和赋值运算符重载确保正确地进行内存分配和释放以避免内存泄漏或重复释放的问题。
15. This指针的作用
解决成员变量与参数名冲突当类的成员函数中存在与参数名同名的成员变量时可以使用this指针来区分它们。通过使用this指针可以显式地访问当前对象的成员变量而不会与同名的函数参数产生混淆。在类的成员函数中访问当前对象this指针可以在类的成员函数内部获取当前对象的地址使得成员函数能够访问和操作当前对象的成员变量和成员函数。
总的来说this指针的作用是在类的成员函数中指向当前对象使得可以在函数内部访问和操作当前对象的成员变量和成员函数。它解决了成员变量与参数名冲突的问题并提供了方便的方式来操作当前对象。
16. 重载、覆盖、隐藏的区别 重载Overloading 定义是指在同一个作用域内根据函数参数的类型、个数或顺序定义具有相同名称但不同参数的多个函数。特点 函数重载是静态多态性编译时多态在编译时根据函数调用的参数类型或个数匹配对应的函数。函数重载与函数的返回类型无关。函数重载可以在同一个类中或不同的类之间进行。 覆盖Override 定义是指子类重新定义或实现了从父类继承的同名、同参数列表的成员函数。特点 覆盖是动态多态性运行时多态在运行时根据对象的实际类型调用对应的函数。函数覆盖要求函数名、参数类型和个数相同且返回类型也必须兼容。覆盖只能存在于继承关系中即子类可以覆盖父类的成员函数。 隐藏Hiding 定义是指子类定义了与父类同名的成员函数但参数列表不同。特点 隐藏是静态多态性编译时多态在编译时根据函数调用时的静态类型声明类型进行函数匹配。函数隐藏要求函数名相同但参数类型和个数不同。函数隐藏可以在继承关系中以及在同一个类中进行。
总结来说重载是根据函数参数的类型、个数或顺序定义多个同名函数覆盖是子类重新定义父类的同名函数而隐藏是同时定义了相同函数名但不同参数的函数。重载是编译时多态覆盖和隐藏是运行时多态。重载和隐藏可以在同一个类中进行而覆盖只能存在于继承关系中。
17. 访问权限private、protected、public区别 18. 在 C 程序中调用被 C 编译器编译后的函数为什么要加 extern “C” 答C语言支持函数重载C 语言不支持函数重载。函数被 C编译后在库中的名字与 C 语言的不同。假设某个函数的原型为 void foo(int x, int y);
该函数被 C 编译器编译后在库中的名字为_foo而 C编译器则会产生像_foo_int_int 之类 的名字。
C提供了 C 连接交换指定符号 extern“C”来解决名字匹配问题 19. Public继承、protected继承、private继承的区别
1、public继承就是公有继承完还是公有保护还是保护私有还是私有
2、protected继承就是公有变保护保护还是保护私有还是私有
3、private继承就是所有变成私有
20. extern”C” 的作用
我们可以在C中使用C的已编译好的函数模块这时候就需要用到extern”C”。也就是extern“C” 都是在c文件里添加的。
extern在链接阶段起作用四大阶段预处理--编译--汇编--链接。stm32单片机部分
1. RTOS是什么
实时操作系统Real Time Operating System简称RTOS是指当外界事件或数据产生时能够接受并以足够快的速度予以处理其处理的结果又能在规定的时间之内来控制生产过程或对处理系统做出快速响应调度一切可利用的资源完成实时任务并控制所有实时任务协调一致运行的操作系统。提供及时响应和高可靠性是其主要特点。
实时操作系统RTOS是一种专门设计用于实时应用的操作系统。RTOS旨在满足实时应用对于可靠性、可预测性和响应性的需求并提供任务调度、中断处理、资源管理和通信机制等功能。
RTOS与通用操作系统如Windows、Linux等的主要区别在于其专注于实时性能和可靠性。实时应用要求系统能够在严格的时间限制内响应和处理事件因此RTOS需要提供可靠的任务调度和中断处理机制以确保关键任务能够及时执行并保持适当的响应时间。
RTOS通常具有以下特点 实时性RTOS要能够在严格的时间限制内响应事件并提供最小的延迟和最大的确定性。 可预测性RTOS需要提供可预测的任务执行和中断处理时间以便应用程序能够按照需求进行时间规划和调度。 资源管理RTOS需要提供有效的资源管理机制如任务管理、内存管理和设备管理以便应用程序能够合理地使用系统资源。 通信和同步RTOS需要提供通信和同步机制以便多个任务之间能够安全地共享数据和协调操作。 中断处理RTOS需要提供可靠的中断处理机制以便及时响应外部事件和设备请求。
RTOS广泛应用于各种实时应用领域如航空航天、汽车电子、医疗设备、工业自动化、嵌入式系统等这些领域对于实时性和可靠性的要求较高。常见的RTOS有FreeRTOS、RTOS-32、QNX等它们提供了丰富的功能和工具帮助开发人员构建可靠的实时系统。
2. 谈谈你对中断的理解
在处理器中中断是一个过程即CPU在正常执行程序的过程中遇到外部/内部的紧急事件需要处理暂时中止当前程序的执行转而去为处理紧急的事件待处理完毕后再返回被打断的程序处继续往下执行。
中断在计算机多任务处理尤其是即时系统中尤为重要。比如uCOSFreeRTOS等。
中断能提高CPU的效率同时能对突发事件做出实时处理
实现程序的并行化实现嵌入式系统进程之间的切换
3. Linux操作系统和RTOS的区别 实时性要求RTOS专注于满足实时应用对于响应时间和可预测性的严格要求。它们提供了可靠的任务调度和中断处理机制以确保关键任务能够及时执行并保持适当的响应时间。而Linux操作系统通常是一种通用用途的操作系统优先考虑资源利用率和多任务处理而不一定满足实时性需求。 内核大小和复杂性RTOS的内核通常较小而精简专注于提供实时性能和简单的任务调度。反之Linux内核较大且功能丰富它需要支持多种硬件设备和文件系统提供了广泛的功能和驱动程序支持。 资源管理RTOS提供了更严格和精确的资源管理机制以确保对处理器、内存和其他设备资源的可预测性和有效利用。Linux操作系统则更侧重于通用资源管理和多任务处理。 网络和图形界面Linux操作系统在网络通信和图形界面方面具有强大的支持它提供了丰富的网络协议栈和图形用户界面GUI功能。而RTOS通常专注于实时控制任务这些功能可能没有如此广泛的支持。 可定制性RTOS通常提供了可定制和裁剪的机制以便满足特定应用需求和资源限制。开发人员可以根据需求选择和配置所需的功能以减小内核大小和优化性能。相比之下定制Linux内核的过程较为复杂。
总体而言RTOS适用于对实时性能要求较高的应用如航空航天、汽车电子、医疗设备等。而Linux操作系统则适用于更通用的计算机应用如服务器、个人计算机、嵌入式系统等在这些应用中实时性能可能不是首要关注点而更注重通用性和功能的丰富性。
4. NVIC在哪里
NVIC提供中断控制器用于总体管理异常称之为“内嵌向量中断控制器:Nested Vectored Interrupt Controller (NVIC)” 在Cortex-M系列处理器中NVIC通常是集成在处理器的内部作为其核心的一部分。具体而言在ARM Cortex-M0内核中NVIC位于内部与处理器的内核一起工作。ARM Cortex-M3、M4和M7等内核也拥有类似的NVIC实现。
5. 简述一下冯诺依曼架构和哈佛架构
解释冯诺依曼体系结构 (1) 一律用二进制数表示数据和指令。
(2) 顺序执行程序。执行前将需要的程序和数据先放入存储器PC为内存。当执行时把要执行的程序和要处理的数据按顺序从存储器中取出指令一条一条地执行称作顺序执行程序。
(3) 计算机硬件由运算器、控制器、存储器、输入设备和输出设备五大部分组成。 定义 冯诺依曼结构采用指令和数据统一编址使用同条总线传输CPU读取指令和数据的操作无法重叠。
哈佛结构采用指令和数据独立编址使用两条独立的总线传输CPU读取指令和数据的操作可以重叠。
利弊 冯诺依曼结构主要用于通用计算机领域需要对存储器中的代码和数据频繁的进行修改统一编址有利于节约资源。 哈佛结构主要用于嵌入式计算机程序固化在硬件中有较高的可靠性、运算速度和较大的吞吐。
区别
1、存储器结构不同
1、冯诺依曼结构冯诺依曼结构是一种将程序指令存储器和数据存储器合并在一起的存储器结构。
2、哈佛结构哈佛结构使用两个独立的存储器模块分别存储指令和数据每个存储模块都不允许指令和数据并存。
2、总线不同
1、冯诺依曼结构冯诺依曼结构没有总线CPU与存储器直接关联。
2、哈佛结构哈佛结构使用独立的两条总线分别作为CPU与每个存储器之间的专用通信路径而这两条总线之间毫无关联。
3、执行效率不同
1、冯诺依曼结构冯诺依曼结构其程序指令和数据指令执行时不可以预先读取下一条指令需要依次读取执行效率较低。
2、哈佛结构哈佛结构其程序指令和数据指令执行时可以预先读取下一条指令具有较高的执行效率。 6. M0内核有多少个内部中断多少个外部中断
Cortex-M0内核 可以处理15个内部异常和32个外部中断
7. 简述外部中断事件框图 外部中断/事件处理过程框图分析 编号1是信号输入线EXTI支持产生多达28个外部事件/中断请求。 编号2是边沿检测电路用于监测上升沿或下降沿信号。 编号3是一个或门电路信号来源是外部事件或者软件中断/事件寄存器产生。 编号4是一个与门电路信号来源是编号3送来的信号和中断屏蔽寄存器的值 如果中断屏蔽寄存器为0也不会将信号送到NVIC 只有编号3送来了中断信号且中断屏蔽寄存器允许产生中断才会将中断信号送入NVIC. 编号5是一个与门电路信号来源是编号3送来的信号和事件屏蔽寄存器的值 如果事件屏蔽寄存器为0不会将信号送到脉冲发生器 只有编号3送来了信号且事件屏蔽寄存器允许产生事件才会将信号送入脉冲发生器编号 6 进而产生脉冲来控制外部设备做出动作。
8. 简述单片机中的前后台操作什么是前台什么是后台
前台操作指的是主要的任务或功能通常是基于事件驱动的需要及时响应和处理。这些前台任务是优先级较高的任务它们负责实时性较高的任务处理例如处理外部中断、采集传感器数据、控制外围设备等。前台任务需要在最短的时间内完成并及时响应外部事件或请求。
后台操作指的是次要的任务或功能通常是基于轮询方式的可以延迟响应或周期性执行。这些后台任务是优先级较低的任务它们执行的频率相对较低例如处理数据存储、更新界面显示、执行周期性的系统检测等。后台任务的执行时间较长不需要实时响应可以在需要时进行处理。
前台和后台操作可以通过任务优先级的设置来实现。前台任务的优先级较高使其在任务调度时具有更高的执行优先级以确保及时响应和处理。而后台任务的优先级较低将在前台任务没有紧急处理需求时执行。
使用前后台操作的优势在于能够灵活管理和调度多个任务提高系统的有效利用率和响应能力。通过合理设置前后台任务之间的优先级关系可以满足不同任务的实时性要求提升系统性能和稳定性。
需要注意的是前后台操作的具体实现方式可能因单片机的架构和使用的实时操作系统RTOS的不同而有所差异。因此在使用单片机进行任务管理时根据具体的硬件和软件环境开发人员需要合理设计任务优先级和任务调度策略以实现前后台操作的有效管理。
9. 如何保障串口通信过程中数据的安全性 数据完整性使用校验机制来确保数据在传输过程中的完整性。常见的校验方式包括奇偶校验、CRC校验等。发送端在将数据发送前计算校验值并将其附加在数据中接收端在接收到数据后进行校验如果校验失败则表示数据损坏或篡改。 数据加密对敏感数据进行加密处理确保只有合法的接收方能够解密并获取到原始数据。常见的加密算法包括对称加密如AES算法和非对称加密如RSA算法。发送方在发送数据前对其进行加密接收方在接收到数据后进行解密。 访问控制通过访问控制机制确保只有授权的设备或用户能够访问串口通信。可以使用密码、加密锁等方式来实现访问控制限制非授权设备或用户的接入。 数据的时效性在一些对实时性要求较高的应用场景中可以使用时间戳或序列号等机制来确保数据的时效性。接收方可以根据时间戳或序列号来判断是否接收到最新的数据并进行相应处理。 物理安全性在物理层面上采取措施来保障串口通信的安全性例如使用加密传输线路、采取防护措施以避免串口的物理接触受到破坏或篡改。
需要根据具体的应用场景和安全需求选择适当的安全措施来保障串口通信的安全性。以上只是一些常见的安全保障措施具体的实现可以根据实际情况进行定制。
10. 简述同步通信和异步通信的区别 同步通信通信双发根据同步信号进行通信IIC\SPI 异步通信双方都有各自的独立的时钟约定好通信速度进行通信。UART 通信速度单位时间内发送或接收的数据位数
同步通信
时间同步同步通信是基于时钟同步的方式进行数据传输。
传输速率稳定在同步通信中发送方和接收方的时钟频率需要相同且传输速率数据传输速度是固定的
需要专用的时钟信号线同步通信还需要额外的时钟信号线用于保持发送方和接收方的时钟同步以控制数据的传输。
异步通信
时间异步异步通信不依赖于时钟同步数据的传输不需要时钟信号进行同步。
传输速率灵活在异步通信中数据的传输速率可以是可变的发送方和接收方可以以不同的速率进行数据传输。
无需额外的时钟信号线异步通信不需要额外的时钟信号线。
11. 谈谈你对ARM的理解
1-ARM是一家公司ARM公司是一家芯片知识产权IP供应商它与一般的半导体公司最大的不同就是不制造芯片且不向终端用户出售芯片而是通过转让设计方案由合作伙伴生产出各具特色的芯片。 2 - ARM处理器ARM处理器是英国Acorn有限公司设计的低功耗低成本的第一款RISC微处理器。 经典处理器 ARM7\ARM9\ARM11,后续处理器开始以cortex命名 Cortex-A 高性能 Cortex-R 汽车电子 Cortex-M 低成本、低功耗
3 - ARM代表一种技术。具有性能高、成本低和能耗省的特点。在智能机、平板电脑、嵌入控制、多媒体数字等处理器领域拥有主导地位。
12. ARM指令集和RISC之间的关系RISC-V和RISC之间的关系
ARM指令集和RISC之间的关系
ARM指令集与RISCReduced Instruction Set Computer精简指令集计算机之间有着密切的关系。事实上ARM是一种基于RISC原则设计的指令集架构。
RISC是一种计算机体系结构设计原则旨在通过使用较少且简单的指令来提高指令执行的效率和性能。RISC的设计原则包括指令集精简、采用固定长度的指令、使用寄存器操作等。RISC架构的优点包括执行速度快、指令编码简单、资源利用高等。
ARMAdvanced RISC Machines是一家英国半导体公司它开发了自己的处理器架构和指令集也称为ARM架构或ARM指令集。ARM架构是基于RISC原则设计的旨在提供低功耗和高性能的解决方案广泛应用于移动设备、嵌入式系统和其他领域。
ARM指令集特点如下
精简指令集ARM指令集采用了精简的指令集设计使得指令编码简单且易于解码有助于提高指令执行的效率和性能。可扩展性ARM指令集具有可扩展性支持不同的指令集扩展和指令集特性以满足不同应用的需求。接口标准化ARM指令集和架构的接口是标准化的这意味着可以通过使用ARM架构的不同处理器和核心实现对不同系统进行复用和兼容性。
总结起来ARM指令集是基于RISC原则设计的一种指令集架构。它的设计目标是通过精简的指令集和高性能的处理器核心实现低功耗、高效率的计算。ARM架构在移动设备、嵌入式系统等领域得到了广泛应用并成为当前最流行的指令集架构之一。
RISC-V和RISC之间的关系
RISC-VRISC-Five是一种开放指令集架构ISA它基于RISCReduced Instruction Set Computer精简指令集计算机原则。RISC-V的设计目标是提供一个免费、开放和可扩展的指令集架构适用于广泛的应用领域。
RISC-V与传统的商业指令集架构如ARM、x86等以及其他RISC架构如MIPS、PowerPC等相比有一定的区别 开放性RISC-V是开放的指令集架构其设计和规范是公开可用的任何人都可以免费使用和实现RISC-V架构。这使得RISC-V成为一个开放的生态系统吸引了大量的研究、创新和社区参与。 可扩展性RISC-V的设计非常灵活支持可扩展的指令集。它提供了基本的核心指令集RV32I、RV64I等并以模块化的方式支持标准扩展指令集如乘法、浮点运算、向量处理等以及自定义指令集扩展。 简洁性与某些商业指令集架构相比RISC-V采用了相对简洁的指令编码使得指令解码和执行效率得到提高。 应用范围RISC-V旨在满足广泛的应用需求从嵌入式系统和物联网设备到高性能计算和云计算等领域。
总之RISC-V是基于RISC原则设计的一种开放指令集架构。它提供了自由、灵活和可扩展的设计成为了一个受到广泛关注的开源生态系统并在学术界、工业界和社区中获得了越来越多的应用和支持。需要注意的是尽管RISC-V和传统RISC架构有相似之处但RISC-V是一种独立的指令集架构与其他RISC架构如MIPS、PowerPC等之间并没有直接的关联。 13. DHT11使用的是什么协议
DHT11传感器使用的是一种基于单总线One-Wire协议。该协议允许通过一个单一的数据线来进行通信包括发送控制命令和接收数据。基于单总线协议的设备可以通过对数据线的电平变化来进行通信从而实现数据的传输和控制。
14. 简述DMA作用
DMA的传输方式无需CPU参与可以直接控制传输
DMA给外部设备和内存开辟了一条直接数据传输的通道
15. SPI是同步通信还是异步通信 16. 简述单片机中断触发过程 进入中断
处理器自动保存现场到堆栈里
{PC, xPSR, R0-R3, R12, LR}
一旦入栈结束ISR(中断服务程序)便可开始执行
退出中断
中断前的现场被自动从堆栈中恢复
一旦出栈完成继续执行被中断打断的指令
出栈的过程也可被打断使得随时可以响应新的中断而不再进行 现场保存
17. 在PWM中占空比是什么
在脉冲宽度调制PWM中占空比用于描述高电平脉冲信号上电平为逻辑高状态在一个周期内所占的时间比例。
一般情况下周期指的是PWM信号的完整周期即一个高电平和一个低电平的总时间。
占空比的计算公式为 占空比 (高电平时间 / 周期) * 100%
占空比可以用来控制PWM信号产生的平均功率或电压。通过改变占空比可以调节输出信号的平均电平和能量从而实现对电机速度、LED亮度等的控制。
18. 简述几个PWM的应用场景
呼吸灯
PWM常用于电机速度控制温度控制。
PWM还可用于音频信号的数字到模拟转换DAC
在无线通信中PWM可用于产生特定频率的调制信号
19. 单片机IO框图中保护二极管如何实现的保护功能保护的谁
在单片机的IO框图中保护二极管用于保护IO引脚免受外部电压或电流的损坏。
保护二极管一般采用反向并联的结构通常是PN结构二极管的一种。当外部电压或电流在正向电压范围内施加在IO引脚上时保护二极管将开始导通将多余的电流通过二极管导向地或电源以保护IO引脚。
20. ARM内核的单片机通常情况下使用的是精简指令集还是复杂指令集
精简指令集
21. 单片机I/O口有什么作用上拉电阻与下拉电阻的作用
输入功能作为输入口I/O口可以接收外部设备发送的信号。
输出功能作为输出口I/O口可以向外部设备发送信号。
上拉电阻上当I/O口为输入模式时外部没有提供足够的信号上拉电阻会将I/O口电平拉高至Vcc。保证在没有外部信号输入时I/O口维持高电平状态。当外部信号输入时会引起电平变化从而使I/O口能够检测到外部信号。
下拉电阻下当I/O口为输入模式时如果外部没有提供足够的信号下拉电阻会将I/O口电平拉低至地。保证在没有外部信号输入时I/O口维持低电平状态。当外部信号输入时会引起电平变化从而使I/O口能够检测到外部信号。
22. 单片机的寻址方式有哪些
直接寻址直接寻址是最常见的寻址方式在程序指令中直接给出操作数的地址。
立即寻址立即寻址是将操作数直接包含在指令中而不是传递地址。
间接寻址间接寻址是使用一个存储在寄存器或内存中的地址来访问数据。
寄存器寻址寄存器寻址是将寄存器作为操作数的地址。指令中直接指定使用特定的寄存器来存储或访问数据。
间接寄存器寻址间接寄存器寻址是使用一个寄存器中的值作为地址然后在该地址上进行数据存取。
相对寻址相对寻址是将操作数与当前指令指针程序计数器中的值进行相对偏移从而得到最终的地址。
基址寻址基址寻址是通过将基址寄存器与偏移值相加来计算最终的地址
23. 看门狗的原理?
看门狗的工作原理就是通过定期喂狗操作来防止看门狗定时器达到超时值。如果系统未能按时喂狗看门狗定时器将会触发复位信号使系统重新启动或采取其他应对措施。通过这种方式看门狗能够监视系统的运行状态并在出现故障或异常情况下采取自动纠正措施提高系统的可靠性和稳定性。
24. 复位时单片机有什么动作? 初始化寄存器复位时单片机的寄存器通常会被初始化为默认值。这包括各种控制寄存器、状态寄存器以及通用寄存器等以确保单片机处于预定义的初始状态。 清除中断状态复位操作通常会清除所有中断状态。这意味着在复位后所有中断请求被清除并且中断向量指针回到预定义的初始值。 执行复位向量复位向量是一个特殊的程序入口地址指示单片机在复位时首先执行的指令。该向量可以是内部存储器中的特定地址也可以是外部引脚上的输入信号。执行复位向量的目的是在复位后执行一些特定的初始化操作例如设置堆栈指针、配置时钟和外设等。 关闭外设复位时可能会关闭某些外设或模块以确保它们处于初始状态或低功耗状态。具体关闭哪些外设或模块取决于单片机的设计和配置。 启动时钟复位后需要启动适当的时钟源和时钟分频器以确保单片机的时钟系统正常运行。时钟系统对于单片机的正常工作至关重要因为它提供了指令执行、外设操作和时序同步等功能。
25. 中断嵌套的好处与坏处?
中断嵌套是指在一个中断处理程序中又触发了另一个中断。
好处
1更高的灵活性中断嵌套可以允许在一个中断处理程序中处理更紧急或更高优先级的中断请求。
2减少延迟
3确保关键任务的执行
坏处
1复杂性增加
2可能引发竞态条件如果多个中断处理程序并发地访问共享资源可能会引发数据竞争和不确定的行为。
3响应时间不确定性中断嵌套会增加中断被延迟处理的可能性。
26. 什么是可重入型函数使用时需要注意哪些问题
可重入性是指一个函数在同一时间可以被多个执行线程并发调用而不会出现竞争条件或导致不可预期的结果。
使用可重入型函数时需要注意的重点是避免对共享资源的竞争访问保证数据一致性考虑上下文切换和选择合适的同步机制。这样可以确保函数在多线程环境下的安全可用性。
27. 单片机片内外设和外设
在单片机中有片内外设和外部外设两种类型的设备。
1片内外设On-chip Peripherals
片内外设是指嵌入在单片机芯片内部与核心处理器CPU集成在一起的功能模块。这些模块通常与单片机的核心处理器通过总线或专用接口进行连接共享片内资源。一些常见的片内外设包括 GPIO通用输入输出口用于接收和输出数字信号。 定时器/计数器Timer/Counter用于生成定时、计数、脉冲宽度调制PWM等信号。 串行通信接口Serial Communication Interface如UART、SPI、I2C等用于与其他设备进行串行通信。 ADC/DAC模数转换器/数模转换器用于模拟信号的转换与处理。 中断控制器Interrupt Controller用于处理外部中断信号实现异步事件的处理。 存储器Memory片内存储器如闪存Flash用于存储程序代码和数据。 时钟模块Clock Module用于产生系统时钟信号。
2外部外设External Peripherals
外部外设是指通过单片机芯片的引脚或专用接口与单片机连接的外部设备或模块。这些外设通常是单独的硬件组件通过与单片机的引脚进行连接来实现与单片机的通信和交互。外部外设的选择取决于应用需求可以是传感器、执行器、通信模块、显示器、存储设备等等。 传感器Sensors如温度传感器、压力传感器、光敏传感器等用于获取环境或物理量的数据。 执行器Actuators如电机、继电器、LED等用于控制物理动作或输出信号。通信模块Communication Modules如以太网模块、Wi-Fi模块、无线射频模块等用于实现与网络或其他设备的通信。显示器Display如液晶显示器LCD、数码管等用于显示文本、图形、数值等信息。存储设备Storage Devices如SD卡、EEPROM、外部闪存等用于扩展单片机的存储容量。
28.MOS管和三极管的区别
1. MOS管是电压控制的元件而三级管是电流控制的元件。
2. 三极管比MOS管的功耗大。
3. MOS管常用于功率开关和大电流开关电路三极管常用于数字电路的开关控制。
29. IIC主机和从机的区别 控制能力主机可以控制通信的启动、停止、时序和速度而从机没有这些控制能力它只能被动响应主机的命令。 地址选择主机通过发送地址来选择要与之通信的从机而从机被动等待主机的命令并根据接收到的地址判断是否是自己需要响应的通信。 通信顺序主机与从机是一对多的通信关系主机可以与多个从机进行通信但每个时刻只能与一个从机进行通信。从机只能在被选择时才能进行通信。
30. 常见的嵌入式文件系统
嵌入式系统中比较常用的文件系统有cramfs、JFFS2、NFS、initrd、yaffs2、Ext4、squashfs linux底层部分
1. 设计或开发 Linux 操作系统的内核时你会考虑哪些基本的管理模块并描述其作用。
进程管理进程的创建销毁调度等功能 注可中断不可中断就是是否被信号打断。从运行状态怎样改到可中断等待态和不可中断等待态操作系统开始会对每个进程分配一个时间片当进程里面写了sleep函数进程由运行到休眠态但是此时CPU不可能等着。有两种方法1根据时间片CPU自动跳转2程序里面自己写能引起CPU调度的代码就可以
文件管理通过文件系统ext2/ext3/ext4 yaff jiffs等来组织管理文件
网络管理通过网络协议栈OSITCP对数据进程封装和拆解过程数据发送和接收是通过网卡驱动完成的网卡驱动不会产生文件在Linux系统dev下面没有相应的文件所以不能用open等函数,而是使用的socket。
内存管理通过内存管理器对用户空间和内核空间内存的申请和释放
设备管理 设备驱动的管理(驱动工程师所对应的)
字符设备驱动: led 鼠标 键盘 lcd touchscreen触摸屏 1.按照字节为单位进行访问顺序访问(有先后顺序去访问)
2.会创建设备文件open read write close来访问
块设备驱动 :camera u盘 emmc 1.按照块512字节扇区来访问可以顺序访问可以无序访问
2.会创建设备文件open read write close来访问
网卡设备驱动:猫 1.按照网络数据包来收发的。
2. bootloader、内核 、根文件的关系 启动顺序bootloader-linux kernel-rootfile-app Bootloader全名为启动引导程序是第一段代码它主要用来初始化处理器及外设然后调用Linux内核。
Linux内核在完成系统的初始化之后需要挂载某个文件系统作为根文件系统RootFilesystem然后加载必要的内核模块启动应用程序。
一个嵌入式Linux系统从软件角度看可以分为四个部分引导加载程序BootloaderLinux内核文件系统应用程序。
3. 中断和异常的区别
内中断同步中断异常是由cpu内部的电信号产生的中断其特点为当前执行的指令结束后才转而产生中断由于有cpu主动产生其执行点必然是可控的。
外中断异步中断是由cpu的外设产生的电信号引起的中断其发生的时间点不可预期。
4. linux命令
1、改变文件属性的命令chmod (chmod 777 /etc/squid
运行命令后squid文件夹目录的权限就被修改为777可读可写可执行)
2、查找文件中匹配字符串的命令grep
3、查找当前目录pwd
4、删除目录rm -rf 目录名
5、删除文件rm 文件名
6、创建目录文件夹mkdir
7、创建文件touch
8、vi和vim 文件名也可以创建
9、打包tar -cvzf 目录文件夹解压tar -xvzf 压缩包
10、查看进程对应的端口号5. 硬链接与软链接 链接操作实际上是给系统中已有的某个文件指定另外一个可用于访问它的名称。对于这个新的文件名我们可以为之指定不同的访问权限以控制对信息的共享和安全性的问题。如果链接指向目录用户就可以利用该链接直接进入被链接的目录而不用打一大堆的路径名。而且即使我们删除这个链接也不会破坏原来的目录。
1硬链接
硬链接只能引用同一文件系统中的文件。它引用的是文件在文件系统中的物理索引(也称为inode)。当您移动或删除原始文件时硬链接不会被破坏因为它所引用的是文件的物理数据而不是文件在文件结构中的位置。硬链接的文件不需要用户有访问原始文件的权限也不会显示原始文件的位置这样有助于文件的安全。如果您删除的文件有相应的硬链接那么这个文件依然会保留直到所有对它的引用都被删除。
2软链接符号链接
软连接其实就是新建立一个文件这个文件就是专门用来指向别的文件的那就和windows 下的快捷方式的那个文件有很接近的意味。软连接产生的是一个新的文件但这个文件的作用就是专门指向某个文件的删了这个软连接文件那就等于不需要这个连接和原来的存在的实体原文件没有任何关系但删除原来的文件则相应的软连接不可用。
QT部分
1. Qt的跨平台特性是如何体现的
2. 上位机与下位机的关系是什么你做过哪些相关项目
3. 如何使用样式表更改一个进度条的外观
4. 如何提升组件
5. QApplication的功能是什么
6. 信号槽连接的参数是什么参数之间有什么关系当多个信号连接到同一个槽的时候如何区分
7. 随便找一个电脑软件请问这个软件界面的布局思路是什么
8. 定时器是做什么的能举出一些在实际项目中的使用场景吗
9. 如果要写一个飞机大战请简述一些编写的思路
10. 如果要写一个notepad请简述一下编写的思路
11. 一个父对象与一个子对象互相传值如何处理
12. 描述一下异步刷新的原理为什么使用
13. 数据库预处理的意义是什么如何操作
14. 请简述事件机制
简历部分 1. Modbus协议
modbusTCP协议是主从式协议属于基于TCP协议的应用层高层协议主要用于工业上工业设备之间的网络通信。有四类寄存器线圈寄存器、离散输入寄存器、保持寄存器、输入寄存器其中线圈一般表示开关型设备保持和输入表示数值型设备输入寄存器只能读不能写保持寄存器可读可写。不同的寄存器要基于不同的功能码来操作我都用过XXX功能码。1,5,15 3,6,16 2 4 2. Mqtt 协议
MQTTMessage Queuing Telemetry Transport消息队列遥测传输协议是一种基于客户端-服务器的消息发布/订阅的轻量级通讯协议该协议构建于TCP/IP协议上是基于TCP协议实现的一种应用层协议同时具有TCP可靠性的特点与和UDP的分包协议特点点对点的特点以及灵活的特点。MQTT最大优点在于可以以极少的代码和有限的带宽为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议使其在物联网、小型设备、移动应用等方面有较广泛的应用。 3. http协议
HTTP协议是Hyper Text Transfer Protocol超文本传输协议的缩写是用于Web Browser浏览器到Web Server服务器进行数据交互的传输协议。
HTTP是一个基于TCP通信协议传输来传递数据HTML 文件, 图片文件, 查询结果等的应用层协议。浏览器请求的本质其实是
浏览器作为TCP的客户端按照http协议的要求组合一个字符串通过网络把数据发送给TCP服务器webserver这里要注意TCP服务器指定的端口如果默认是80浏览器不需要写端口如果其它端口需要我们在浏览器地址手动写端口。TCP服务器通过recv收到浏览器请求字符串后判断请求的类型GET/POST和请求内容然后根据这两者做出回应回应的格式需要遵循http协议的规范。浏览器收到响应后会根据相应内容进行渲染页面。
4. wifi连接方式
我当时使用的单片机开发板自带wifi透传模组ESP12F本质上就是ESP8266芯片的封装。它实现了wifi和串口的透明传输当需要通过网络发送数据时候只需要将要发送的数据发给模块的串口模组即可自动转换为wifi协议发送出去。同理从网络过来的数据模组会自动接收并通过串口转发给单片机的主控。
5. TCP/IP网络模型几层作用协议
TCP/IP协议实际上是一个协议族主要分为4层分别是网络接口和物理层作用是屏蔽硬件差异驱动向上层提供统一的操作接口以太网网络层作用是提供端对端的传输可以理解为通过IP寻址机器ip传输层作用是决定数据交给机器的哪个任务进程去处理通过端口寻址(TCP,UDP应用层应用协议和应用程序的集合(FTP,HTTP)。 6.Socket 套接字
(1定义
套接字是一种通信机制通信的两方的一种约定socket屏蔽了各个协议的通信细节提供了tcp/ip协议的抽象对外提供了一套接口同过这个接口就可以统一、方便的使用tcp/ip协议的功能。这使得程序员无需关注协议本身直接使用socket提供的接口来进行互联的不同主机间的进程的通信。我们可以用套接字中的相关函数来完成通信过程。 发送方的发送数据的处理流程大致为用户空间 - 内核 - 网卡 - 网络
在用户态空间调用发送数据接口 send/sento/wirte 等写数据包在内核空间会根据不同的协议走不同的流程。以TCP为例TCP是一种流协议内核只是将数据包追加到套接字的发送队列中真正发送数据的时刻则是由TCP协议来控制的。TCP协议处理完成之后会交给IP协议继续处理最后会调用网卡的发送函数将数据包发送到网卡。
接收方的接收数据的处理流程大致为网络 - 网卡 - 内核(epoll等) - 进程(业务处理逻辑)
网卡会通过轮询或通知的方式接收数据Linux做了优化组合了通知和轮询的机制简单来说在CPU响应网卡中断时不再仅仅是处理一个数据包就退出而是使用轮询的方式继续尝试处理新数据包直到没有新数据包到来或者达到设置的一次中断最多处理的数据包个数。数据离开网卡驱动之后就进入到了协议栈经过IP层、网络层协议的处理就会触发IO读事件比如epoll的reactor模型中就会触发对应的读事件然后回调对应的IO处理函数数据之后会交给业务线程来处理比如Netty的数据接收处理流程就是这样的。
2特性
套接字的特性有三个属性确定它们是域domain类型type和协议protocol。
域指定套接字通信中使用的网络介质。最常见的套接字域是 AF_INETIPv4或者AF_INET6(IPV6)它是指 Internet 网络。
类型
流套接字SOCK_STREAM 流套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送并按顺序接收。流套接字之所以能够实现可靠的数据服务原因在于其使用了传输控制协议即TCP 数据报套接字SOCK_DGRAM 数据报套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性数据有可能在传输过程中丢失或出现数据重复且无法保证顺序地接收到数据。数据报套接字使用UDPUser Datagram Protocol协议进行数据的传输。 原始套接字SOCK_RAW 原始套接字与标准套接字标准套接字指的是前面介绍的流套接字和数据报套接字的区别在于原始套接字可以读写内核没有处理的IP数据包而流套接字只能读取TCP协议的数据数据报套接字只能读取UDP协议的数据。因此如果要访问其他协议发送数据必须使用原始套接字。
协议IPPROTO_TCPIPPROTO_UDP
3缓冲区
每个 socket 被创建后都会分配两个缓冲区输入缓冲区和输出缓冲区。write()/send() 并不立即向网络中传输数据而是先将数据写入缓冲区中再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区函数就可以成功返回不管它们有没有到达目标机器也不管它们何时被发送到网络这些都是TCP协议负责的事情。read()/recv() 函数也是如此也从输入缓冲区中读取数据而不是直接从网络中读取。 6. GPIO
GPIO(general porpose intput output):通用输入输出接口的简称。可以通过软件控制其输出和输入。stm32芯片的GPIO引脚与外部设备连接起来从而实现与外部通信控制以及数据采集的功能。
GPIO是嵌入式系统、单片机开发过程中最常用的接口用户可以通过编程灵活的对接口进行控制实现对电路板上LED、数码管、按键等常用设备控制驱动也可以作为串口的数据收发管脚或AD的接口等复用功能使用。因此其作用和功能是非常重要的。
7. USART (通用同步异步收发器)
Universal Synchonous Asynchronous receiver transmitter
USARTUniversal Synchronous/Asynchronous Receiver Transmitter是一种通用的串行通信接口常用于微控制器和其他电子设备之间的数据通信。
USART 提供了同时支持同步和异步通信的能力因此它可以在多种串行通信模式下操作。它可以用来传输数据和与外部设备进行通信例如调试终端、传感器、无线模块等。 8. SPI
SPI接口是Motorola 首先提出的全双工三线/四线同步串行外设接口Serial Peripheral Interface采用主从模式Master Slave架构。
时钟由Master控制在时钟移位脉冲下数据按位传输高位在前低位在后MSB firstSPI接口有2根单向数据线为全双工通信。 SPI总线被广泛地使用在FLASH、ADC、LCD等设备与MCU间要求通讯速率较高的场合。
四线制
1MOSI数据线主器件数据输出从器件数据输入
2MISO数据线主器件数据输入从器件数据输出
3SCLK 时钟线主器件产生时钟信号
4/SS片选线从器件使能信号由主器件控制
9. I2C集成电路总线
I2C集成电路总线由Philips公司2006年迁移到NXP在1980年代初开发的一种简单、双线双向的同步串行总线它利用一根时钟线和一根数据线在连接总线的两个器件之间进行信息的传递为设备之间数据交换提供了一种简单高效的方法。每个连接到总线上的器件都有唯一的地址任何器件既可以作为主机也可以作为从机但同一时刻只允许有一个主机。 I2C 标准是一个具有冲突检测机制和仲裁机制的真正意义上的多主机总线它能在多个主机同时请求控制总线时利用仲裁机制避免数据冲突并保护数据。作为嵌入式开发者使用I2C总线通信的场景有很多例如驱动FRAM、E2PROM、传感器等。
总结来说I2C总线具有以下特点
只需要SDA、SCL两条总线 没有严格的波特率要求 所有组件之间都存在简单的主/从关系连接到总线的每个设备均可通过唯一地址进行软件寻址 I2C是真正的多主设备总线可提供仲裁和冲突检测 传输速度分为四种模式 标准模式Standard Mode100 Kbps 快速模式Fast Mode400 Kbps 高速模式High speed mode3.4 Mbps 超快速模式Ultra fast mode5 Mbps 最大主设备数无限制 最大从机数理论上1008个从节点寻址模式的最大节点数为2的7次方或2的10次方但有16个地址保留用于特殊用途。 I2C有16个保留I2C地址。这些地址对应于以下两种模式之一0000 XXX或1111 XXX。下表显示了为特殊目的而保留的I2C地址。
I2C 节点地址 R/W 位功能描述 0000 000 0 广播地址 0000 000 1 起始字节 0000 001 X CBUS 地址 0000 010 X 保留用于不同总线格式 0000 011 X 保留供未来使用 0000 1XX X 高速模式主代码 1111 1XX X 保留供未来使用 1111 0XX X 10位节点地址 I2C还有两个变体分别专注于系统和电源应用称为系统管理总线SMBus和电源管理总线PMBus。 10. 中断
在处理器中中断是一个过程即CPU在正常执行程序的过程中遇到外部/内部的紧急事件需要处理暂时中止当前程序的执行转而去为处理紧急的事件待处理完毕后再返回被打断的程序处继续往下执行。
中断在计算机多任务处理尤其是即时系统中尤为重要。比如uCOSFreeRTOS等。
1.2 意义 中断能提高CPU的效率同时能对突发事件做出实时处理
实现程序的并行化实现嵌入式系统进程之间的切换
1.3 中断处理过程 进入中断
处理器自动保存现场到堆栈里
{PC, xPSR, R0-R3, R12, LR}
一旦入栈结束ISR(中断服务程序)便可开始执行
退出中断
中断前的现场被自动从堆栈中恢复
一旦出栈完成继续执行被中断打断的指令
出栈的过程也可被打断使得随时可以响应新的中断而不再进行 现场保存
7. ADC
Analog-to-Digital Converter模数转换器将模拟信号转换为数字信号的转换器
1. ADC作用 ADC是一个逐次逼近型的模数转换器可以将连续的模拟信号(电压、温度、光照、压力....)转换成离散的数字信号
传感器与之不同将非电学量转换成电学量
最直观的体现模拟信号是连续变化的曲线而数字量是不连续的一个个离散的点
8. DMA - 数据的搬运工 1. DMA作用 DMA的传输方式无需CPU参与可以直接控制传输
DMA给外部设备和内存开辟了一条直接数据传输的通道
2. 目的 给CPU节省资源使CPU的工作效率提高
3. DMA特性 1同一个DMA模块可以有多个优先级请求很高 高 中等 低 2每个通道有3个事件标志 DMA半传输 DMA传输完成 DMA传输出错 3数据源 目标源 数据传输宽度对齐 4传输数据 字节8位 半字16位 全字32位 5存储器-存储器 外设-存储器 外设-外设 6闪存(flash) SRAM APB AHB 外设均可以作为源或者目标 7搬移数据的最大长度为65535字节 9. PWM
PWM是脉冲宽度调制Pulse Width Modulation的缩写。它是一种在电子设备中常用的调制技术用于控制电平或信号的占空比。
脉冲宽度调制通过改变脉冲信号的脉宽来调节输出信号的平均功率或电平。一般情况下脉冲信号是由一个周期性重复的方波信号构成其中脉冲的宽度高电平的持续时间可以根据需要进行调节。
工作原理如下当脉冲信号的脉宽增加时脉冲信号的平均值也相应增加从而增大了输出信号的功率或电平。相反当脉宽减小时输出信号的功率或电平就会减小。
PWM广泛应用于多个领域包括电力电子、电机控制、数码产品、通信等。它的主要优势在于能够高效地调节输出信号的功率而不会产生大量的能量损耗。例如PWM在直流电机速度调节、LED亮度控制、音频放大器等方面得到广泛应用。
通过调节脉冲信号的脉宽PWM技术可以实现精确的控制和调节使得电子设备可以根据需求实现不同的输出功率或电平从而满足各种应用的要求。