能,局部会屏蔽全局
while (1){} 或者 for ( ; ; )
1)、new和delete是C++的关键字,不需要头文件,需要编译器支持;malloc和free是C/C++的库函数,需要带头文件stdlib.h
2)、使用new操作符申请内存分配时,无需制定内存块的大小,编译器会根据类型信息自行计算;而malloc则需要支持本地所分配的大小。
static限制作用域
在C语言中,关键字static有三个明显的作用:
1)、用static修饰局部变量:使其变为静态存储方式,那么这个局部变量执行完成时不会被释放,继续保留在内存中。
2)、用static修饰全局变量:使其在本文件内部有效,而其他文件不可以被引用或链接该变量。
3)、用static修饰函数:使函数只在本文件内部有效,对其他文件不可见,这样的函数又叫静态函数;使用静态函数的好处,不用担心与其他文件的同名函数产生干扰,也是对函数本身保护的一种机制。
const主要用来修饰变量、函数形参和类成员函数:
1)、用const修饰常量:定义时就初始化,以后不能修改。
2)、用const修饰形参:func (const int a) {} 该函数在函数内不能改变。
3)、用const修饰成员函数:该函数对成员变量只能进行只读操作,就是不能修改成员变量的数值。
下面声明都是什么意思?
const int a;
int const a;
const int *a;
int *const a;
int const * a const;
第一个和第一个作用是一样的,a是常整型数。
第三个意味着a是一个指向整型数的指针。(整型数不可修改,但指针可以)
第四个意思是a是一个指向整型数的常数的指针。(指针指向的整型数是可以修改的,但指针是不可修改的)
第五个意味着a是一个指向整型数的常指针。(指针指向的整型数是不可修改的,同时指针也是不可修改的)
结论和记忆方法:
(1) const在*前面,就表示const作用于p所指向的量。所以这时候p所指向的是个常量。
(2) const在*后面,表示p本身是常量,但是p指向的不一定是常量。
(3) *在const中间,表示p本身是常量,p指向的也是个常量。
用#define max 100; 定义的常量没有类型(不进行类型安全检查,可能会出现意想不到的错误),所给出的是一个立即数,编译器只是把所定义的常量值与所在的常量名字联系起来,define所定义的宏变量在预处理阶段的时候进行替换,在程序中使用到该常量的地方都要进行拷贝替换。
用const int max = 255; 定义的常量有类型(编译时会进行类型检查)名字,存储在静态区域中,在编译时确定其值。在程序运行过程中const变量只有一个拷贝,而#define所定义的宏变量却又多个拷贝,所以宏定义在程序运行过程中所消耗的内存比const变量的大很多。
一个定义为volatile的变量是说这变量可能被意想不到的改变,这样不会假设这个变量的值了。
以下几种情况都会用到volatile:
1)、并行设备的硬件存储器。
2)、一个中断服务子程序会访问到的非自动变量。
3)、多线程应用中被几个任务共享的变量。
全局变量:
在所有函数体外部定义,程序所在的部分(甚至其他文件中的代码)都可以使用。
全局变量不受作用域的影响(也就是全局变量的生命周期一直到程序的结束)。
局部变量:
出现在一个作用域内,它们是局限于一个函数的。
局部变量经常被称为自动变量,它们进入作用域时自动生成,离开作用域时自动消失。
关键字auto可以显示说明这个问题,但是局部变量默认为auto,所以没有必要声明auto。
局部变量可以和全局变量重名,在局部变量作用域范围内,全局变量失效,采用的是局部变量的值。
1)、如果是数组:
#include
int main()
{
int a[5] = {1, 2, 3, 4, 5};
printf("sizeof 数组名 = % d\n", sizeof(a));
printf("sizeof *数组名 = % d\n", sizeof(*a));
}
运行结果:
sizeof 数组名=20
sizeof *数组名=4
2)、如果是指针,sizeof只会检测到指针的类型,指针都是占用4字节的空间(32位机器)。
sizeof是什么? 是一个操作符,也是关键字,就不是一个函数,这和strlen()不同,strlen()是一个函数。
答:回调是一个函数把非当前函数当做参数传递到自身内部来调用;而递归是自己调用自己。
答:我们对回调函数的使用无非是对函数指针的应用,函数指针的概念本身很简单,但是把函数指针应用于回调函数就体现了一种解决问题的策略,一种设计系统的思想。
系统调用是最底层的应用,是面向硬件的。而库函数的调用是面向开发的,相当于应用程序的API(即预先定义好的函数)接口。
段错误:是指访问的内存超过了系统给程序分配的内存空间
原因:
访问不存在的内存空间
访问只读的内存空间
访问系统保护的内存空间
栈溢出
常见的内存泄漏
(1)内存分配未成功,却使用了它
(2)内存分配成功,但尚未初始化就引用它
(3)内存分配成功且初始化,但操作越过了内存的边界
(4)忘记释放内存,造成内存泄漏
(5)释放了内存却继续使用它
0x123456在内存中的存储方式:
大端模式 低地址—>高地址 0x12 | 0x34 | 0x56
小端模式 低地址—>高地址 0x56 | 0x34 | 0x12
指针方式来判断机器的大小端
#include
// 如果是小端模式则返回1,大端模式则返回0
int is_little_endian2(void)
{
int a = 1;
char b = *((char *)(&a)); // 指针方式其实就是共用体的本质
return b;
}
int main(void)
{
int i = is_little_endian2();
if (i == 1)
{
printf("小端模式\n");
}
else
{
printf("大端模式\n");
}
return 0;
}
注意头文件中不可以放变量的定义!!!一般情况下头文件中只放变量的声明,因为头文件要被其他文件包含(即#include),如果把定义放到头文件的话,就不能避免多次定义变量,C++不允许多次定义变量,一个程序中对指定变量的定义只有一次,声明可以无数次。
(1)int类型数据和0比较
if (a == 0) 或者 if (a != 0)
(2)float类型与0比较
const float N = 0.0001;
if ((a >= N) && (a <= N))
不建议写成:
if (a == 0) 或者 if (a != 0)
(3)bool类型与0比较
建议写成:if (a) 或者 if (!a)
(4)指针类型与0比较
if (p == NULL) 或者 if (p != NULL)
位段(bit-field)是以位为单位来定义结构体(或联合体)中的成员变量所占的空间。含有位段的结构体(联合体)称为位段结构。采用位段结构既能够节省空间,又方便于操作。
值传递只是将变量的内容复制一份而已,函数进行操作的其实是另一个变量,只是另一个变量的值和传递的变量值是相同的。
而地址传递是直接把变量的地址传递给函数,这时函数是直接对原来的变量进行操作的。所以值会变化。
通过使用指针,在函数调用时,传递带有地址的参数,并使用指针更改其值;这样,修改后的值就会变成原始参数。
一般来说,函数是可以返回局部变量的。局部变量的作用域只在函数内部,在函数返回后,局部变量的内存已经释放了。
栈的特点是先进后出。栈是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据。
define只在当前文件有效,如果是在头文件中定义的,则引用过头文件的文件都有效
定义常量PI的两种方式:
#define PI 3.1415926f;
const float pi 3.1415926f;
程序的局部变量存在于栈区;
全局变量存在于静态区;
动态申请数据存在于堆区。
do……while是先循环再判断,while是先判断再循环。
Linux内核主要由五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。
Linux系统一般有4个主要部分:内核、shell、文件系统和应用程序。
1)、系统调用:
· 使用INT和IRET指令,内核和应用程核态,从而可以使用特权指令操控设备
· 依赖于内核,不保证移植性
· 在用户空间和内核上下文环境间切换
· 是操作系统的一个入口点
2)、普通函数调用:
· 使用CALL和RET指令,调用时没有堆
· 平台移植性好
· 属于过程调用,调用开销较小
· 一个普通功能函数的调用
内核态,操作系统在内核态运行—运行操作系统程序
用户态,应用程序只能再用户态运行—运行用户程序
启动顺序:bootloader→linux kernel→rootfile→app
Stage1:汇编语言
Stage2:C语言
查看进程:top
查看内存:free
cat/proc/meminfo
vmstat
预处理(Pre-Processing)、编译(Compiling)、汇编(Assembling)、链接(Linking)
栈由系统操作,程序员不可以操作。
所以内存泄漏是指堆内存的泄漏。
堆内存是指程序从堆中分配的,大小任意的,使用完后必须显示释放的内存。应用程序一般使用malloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用。
内存溢出:你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
内存越界:向系统申请了一块内存,而在使用内存时,超出了申请的范围(常见的有使用定大小数组时发生内存越界)
内存溢出问题是C语言或者C++语言所固有的缺陷,它们既不检查数组边界,又不检查类型可靠性(type-safety)。
产生死锁的原因主要是:
1)、因为系统资源不足
2)、进程运行推进的顺序不合适
3)、资源分配不当等
1)、硬链接
硬链接只能引用同一文件系统中的文件。它引用的是文件在文件系统中的物理索引(也称为inode)。当你移动或删除原始文件时,硬链接不会被破坏,因为它所引用的是文件的物理数据而不是文件在文件结构中的位置。
硬链接的文件不需要用户有访问原始文件的权限,也不会显示原始文件的位置,这样有助于文件的安全。如果你删除的文件有相应的硬链接,那么这个文件依然会保留,直到所有对它的引用都被删除。
2)、软链接
软链接,其实就是新建立的一个文件,这个文件就是专门用来指向别的文件的(那就和Windows下的快捷方式的那个文件有很接近的意味)
软链接产生的是一个新的文件,但这个文件的作用就是专门指向某个文件的,删除了这个软链接文件,那就等于不需要这个链接,和原来的存在的实体原文没有任何关系,但删除原来的文件,则相应的软链接不可用。
内中断:同步中断(异常)是由CPU内部的电信号产生的中断,其特点为当前执行的指令结束后才转而产生中断,由于有CPU主动产生,其执行点必然是可控的。
外中断:异步中断是由CPU的外设产生的电信号引起的中断,其发生的时间点不可预期。
请求中断→响应中断→关闭中断→保留断点→中断源识别→保护现场→中断服务子程序→恢复现场→中断返回。
加: -o
1)、改变文件属性的命令:chmod
2)、查找文件中匹配字符串的命令: grep
3)、查找当前目录:pwd
4)、删除目录:rm -rf 目录名
5)、删除文件:rm 文件名
6)、创建目录(文件夹):mkdir
7)、创建文件:touch
8)、vi和vim文件名 也可以直接创建
9)、解压:tar -xzvf 压缩包
10)、打包:tar -cvzf 目录(文件夹)
11)、查看进程对应的端口号
1、先查看进程pid
ps -ef | grep 进程名
2、通过pid查看占用端口
netstat -nap | grep 进程pid
软实时系统:
Windows、Linux系统通常为软实时,当然有补丁可以将内核做成硬实时的系统的。
硬实时系统:
对时间要求很高,限定时间内不管做没做完必须返回
VxWorks,uCOS,FreeRTOS,WinCE,RT-thread等实时系统。
1、冒泡排序; 2、选择排序; 3、插入排序; 4、希尔排序; 5、归并排序; 6、快速排序; 7、堆排序; 8、计数排序; 9、桶排序; 10、基数排序
对于这十大排序的考察主要有两点:
1、考察时间复杂度、空间复杂度、稳定与否
2、手写某种排序
第一点:对于时间复杂度的考察,可能会考察插入排序的平均复杂度是多少?最坏和最好复杂度又是多少?有时候也会从别的角度来对你进行考察,直接会问你了解到的排序中哪些排序是稳定的?哪些不是稳定的?
第二点:快速排序手写次数绝对占据第一名,因为现在企业招聘基本都是有代码要求的,有时候面试官可能也拿不准让你写什么算法题,“算了,写个快排吧”,快排的频率就是这样被拉高的; 第二高频率的应该就是归并排序了。
归并是刚好比快排难度大一点,但也不是那种特别难的排序方法。让手写的还可能有堆排序。
十大排序中考察最多的就是冒泡排序、快速排序、归并排序、堆排序以及可能会出现的桶排序和基数排序了,其余排序出现的概率稍小一点。
1.0 十大经典排序算法 | 菜鸟教程 (runoob.com)
嵌入式考察算法大多都是双向链表、二叉树、字符串翻转和复制这些普通题目。刷题可以在LeetCode、牛客网、杭电OJ等。
十种常见排序算法可以分为两大类:
非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn), 因此称为非线性时间比较类排序。
线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。
栈:栈(stack)又名堆栈,线性表。仅允许在表的一端进行插入和删除运算。一端为栈顶,另一端为栈底,先进后出。
队列:队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
线性表存储数据,需要预先申请一块内存空间,然后将数据按照次序逐一存储,数据之间紧密贴合,不留一丝空隙。
链表的存储方式与顺序表截然相反,什么时候存储数据,什么时候才申请存储空间,数据之间的逻辑关系依靠每个数据元素携带的指针维持。
如果二叉树中除去最后一层节点为满二叉树,且最后一层的节点依次从左到右分布,则此二叉树被称为完全二叉树。
树有很多种,每个节点最多只能有两个子节点的叫二叉树
二叉树的子节点分为左节点和右节点
顺序查找; 二分查找; 插值查找; 斐波那契查找; 树表查找; 分块查找; 哈希查找。
1、冒泡排序; 2、选择排序; 3、插入排序; 4、希尔排序; 5、归并排序; 6、快速排序; 7、堆排序; 8、计数排序; 9、桶排序; 10、基数排序
时间复杂度;空间复杂度
一个算法应该具有以下五个重要的特征:
1、有穷性(Finiteness)
2、确切性(Definiteness)
3、输入项(Input)
4、输出项(Output)
5、可行性(Effectiveness)
根据关键码值而直接进行访问的数据结构
方法一、穷举遍历
方法二、哈希表缓存
方法三、快慢指针
方法四、Set集合大小变化
要删除倒数第n个节点,我们就要找到其前面一个节点,也就是倒数第n+1个节点,找到这个节点就可以进行删除;
定义两个指针,p和cur,cur指针向前走,走了n+1步之后,p指针开始走,当cur指针走到链表结尾的时候,p指针刚好走到倒数第n+1个节点处。
为充分利用向量空间,克服“假溢出”现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。
进程是资源(CPU、内存等)分配的基本单位,线程是CPU调度和分配的基本单位(程序执行的最小单位)。
同一时间,如果CPU是单核,只有一个进程在执行,所谓的并发执行,也是顺序执行,只不过由于切换太快,误以为这些进程同步执行。多核CPU可以同一时间点多个进程在执行。
1)、一个进程死了不影响其他进程,一个线程崩溃很可能影响到其他线程
2)、创建多进程的系统花销大于创建多线程。
3)、多进程通讯因为需要跨越进程边界,不适合大量的数据传送,适合小数据或者密集的数据传送。多线程无需跨越进程边界,适合各线程间的大量传送。并且多线程跨越共享同一进程的共享内存和变量。
进程间通讯:
(1)、有名管道、无名管道; (2)、信号灯集; (3)、共享内存; (4)、消息队列; (5)、信号量; (6)socket
线程通讯(锁):
(1)、信号量; (2)、读写锁; (3)、条件变量; (4)、互斥锁; (5)、自旋锁
父进程调用fork()以后,克隆出一个子进程,子进程和父进程拥有相同内容的代码段、数据段和用户堆栈。
父进程和子进程谁先执行不一定,看CPU。所以我们一般会设置父进程等待子进程执行完毕。
在Linux下产生新的进程的系统调用就是fork函数
· fork(): 子进程拷贝父进程的数据段,代码段
vfork(): 子进程与父进程共享数据段
· fork() 父子进程的执行次序不确定
vfork() 保证子进程先运行,在调用exec或exit之前与父进程数据是共享的,在它调用exec或exit之后父进程才可能被调度运行。
创建进程需要为进程划分出一块完整的内存空间,有大量的初始化操作,比如要把内存分段(堆栈,正文区等)。
创建线程则简单得多,只需要确定PC指针和寄存器的值,并且给线程分配一个栈用于执行程序,同一个进程的多个线程间可以复用堆栈。
因此,创建进程比创建线程慢,而且进程的内存开销更大。
阻塞调用是指调用结果返回之前,当前线程就会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
互斥:是指散布在不同任务之间的若干程序片段,当某个任务运行其中一个程序片段时,其他任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。
同步:是指散布在不同任务之间的若干程序片段,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。
// 头文件
#include
1)初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
2)上锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
3)解锁
int pthread_mutex_unlock(pthread_mutex_t * mutex);
4)销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
(1)创建子进程,父进程退出
(2)子进程下创建新的会话
(3)设置当前目录为根目录
(4)设置文件权限掩码
(5)退出文件描述符
静态库编译时程序较大,可以独立运行。
动态库编译时程序较小,不可独立运行。
I/O通常指网络I/O,多路指多个Socket连接,复用指操作系统进程运算调度的最小单位线程。整体意思也就是多个网络I/O复用一个或少量的线程来处理Socket。
I/O多路复用有多种实现模式:select、poll、epoll、kqueue
线程依赖于进程,同一进程的多个线程共享这一进程的资源。所以进程间的切换会比线程更加耗时。
一个线程的栈要预留1M的内存空间,而一个进程中可用的内存空间只有2G,所以理论上一个进程中最多可以开2048个线程,但是内存当然不可能完全拿来作线程的栈,所以实际数目要比这个值要小。
对于Linux而言,所有对设备或文件的操作都是通过文件描述符进行的。
当使用多个线程来访问同一个数据时,非常容易出现线程安全问题(比如多个线程都在操作同一数据导致数据不一致),所以我们用同步机制来解决这些问题。
消息队列与命名管道有许多类似之处,但少了在打开和关闭管道时的复杂性。但使用消息队列并未解决我们在使用命名管道时遇到的问题,比如管道满时的阻塞问题。
与命名管道相比,消息队列的优势在于,它独立于发送和接收进程而存在,这消除了再同步命名管道的打开和关闭时可能产生的一些困难。
进程池的作用是在多个客户端并发请求时提高服务器的处理效率。
进程间通讯方式有:管道,信号灯集,信号量,消息队列,共享内存,套接字共六种。
系统资源有限。
进程推进顺序不合理。
进程是资源分配的最小单位。
线程是程序执行的最小单位。
TCP→传输控制协议,提供面向对象连接、可靠的字节流服务。当客户端和服务器交换数据前,必须在双方之间建立一个TCP连接,之后才能传输数据。
UDP→用户数据报协议,是一个简单的面向数据报的运算层协议。UDP不提供可靠性,它是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。
TCP优点:可靠稳定
TCP的可靠体现在TCP在传输数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、阻塞控制机制,在数据传完之后,还会断开连接用来节约系统资源。
TCP缺点:慢、效率低、占用系统资源高、容易被攻击
在传递数据之前要建立连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、阻塞机制等会消耗大量时间,而且每台设备上维护所有的传递连接。
UDP优点:快,比TCP稍安全
UDP没有TCP拥有各种机制,是一种无状态的传输协议,所以传输数据非常快,没有TCP整型机制,被攻击的机会就会少一些,但是也是无法避免被攻击。
UDP缺点:不可靠、不稳定
因为没有TCP的这些机制,UDP在传输数据时,如果网络质量不好,就会容易丢包,造成数据的缺失。
TCP:传输一些对信号完整性,信号质量有要求的信息。
UDP:对网络通信质量要求不高时,要求网络通信速度要快的场景。
因为TCP传输的数据满足3大条件,不丢失,不重复,按顺序到达。
1)、三次握手过程
· 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)
· 第二次握手:服务器收到SYN包,必须确认客户端的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
· 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
TCP/IP建立连接时三次握手过程:
第一次:建立连接时,客户端向服务器发送连接请求,进入SYN_END状态,并等待服务器确认。
第二次:服务器收到客户端连接请求,并向客户端发送允许连接应答,进入SYN_RECV状态。
第三次:客户端收到服务器允许连接应答,并向服务器发送确认连接,此时客户端和服务器进入通信状态,三次握手完成。
2)、四次挥手过程
第一次:客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
第二次:服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
第三次:客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
第四次:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
【问题1】为什么连接的时候是三次握手,关闭的时候却是四次挥手?
答:
三次握手时,服务器同时把ACK和SYN放在一起发送到了客户端那里。
四次挥手时,当收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方 ACK 和 FIN 一般都会分开发送
【问题2】为什么客户端最后还要等待2MSL?
答:客户端需要保证最后一次发送的ACK报文到服务器,如果服务器未收到,可以请求客户端重发,这样客户端还有时间再发,重启2MSL计时。
【问题3】为什么不能用两次握手进行连接?
答:三次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
TCP/IP协议
NetBEUI
IPX/SPX协议
解决粘包问题:就是转换成发送数据和接收数据的格式
发送数据:发送数据,先发送数据的长度,然后再发送真是数据的字节数
接收数据:接收真实数据的长度,然后再按字节长度接收数据;
A类:0~127.255.255.255
B类:128 ~ 191.255.255.255
C类:192 ~ 223.255.255.255
D类:224 ~ 239.255.255.255
E类:240 ~ 255.255.255.255
最喜欢问的莫过于strlen与sizeof的区别、explicit关键字、mutable关键字、指针和引用、public、protected、private三者在继承情况下的一些访问权限、菱形继承、友元函数等。
构造函数只是起初始化值的作用,但实例化一个对象的时候,可以通过实例去传递参数,从主函数传递到其他的函数里面,这样就使其他的函数里面有值了。
析构函数与构造函数的作用相反,用于撤销对象的一些特殊任务处理,可以是释放对象分配的内存空间。
特点:析构函数与构造函数同名,但该函数前面加~。 析构函数没有参数,也没有返回值,而且不能重载
继承是指一个对象直接使用另一对象的属性和方法
多态首先是建立在继承的基础上的,先有继承才有多态。多态是指不同的子类在继承父类后分别都重写覆盖了父类的方法,即父类同一方法,在继承的子类中表现出不同的形式。
就是实例化了的类。类是属性和方法的封装。对象是这个类的具体实现。
对象是类实例化出来的,对象中含有类的属性,类是对象的抽象。
class
C public的类成员
可以作为虚函数的是普通函数,析构函数
友元函数就是以friend开头的一种破坏类的封装性的一种用法
友元类的私有和保护成员在类外不可以使用
继承是指在已存在的类的基础上扩展产生新的类。
意义:继承是面向对象程序设计的三大特征(封装、继承和多态)之一
组合和继承是面向对象中两种代码复用的方式。组合是指在新类中创建原有类的对象,重复利用已有类的功能。继承是面向对象的主要特性之一,它允许设计人员根据其他类的实现来定义一个类的实现。
多态首先是建立在继承的基础上的,先有继承才能有多态。多态是指不同的子类在继承父类后分别都重写覆盖了父类的方法,即父类同一个方法,在继承的子类中表现出不同的形式。多态成立的另一个条件是在创建子类时候必须使用父类new子类的方式。
抽象类是特殊的类,只是不能被实例化;除此以外,具有类的其他特性;重要的是抽象类可以包括抽象方法,这是普通类所不能的。
如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单的方法来控制组件版本。
如果创建的功能将在大范围的全异对象间使用,则使用接口。如果要设计小而简练的功能块,则使用接口。
如果要设计大的功能单元,则使用抽象类.如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。
抽象类主要用于关系密切的对象;而接口适合为不相关的类提供通用功能。
malloc/free是标准库函数,new/delete是C++运算符
malloc失败返回空,new失败抛异常
new/delete会调用构造、析构函数,malloc/free不会,所以他们无法满足动态对象的要求。
new返回有类型的指针,malloc返回无类型的指针
浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,
深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。
冯诺依曼结构:
程序和数据都放在内存中,且不彼此分离 的结构称为冯诺依曼结构。譬如Intel的 CPU均采用冯诺依曼结构。
哈佛结构:
程序和数据分开独立放在不同的内存块中, 彼此完全分离的结构称为哈佛结构。譬如 大部分的单(MCS51、 ARM9等)均采用哈佛结构。
ARM体系结构包含有:
(1)结构
• 不同ARM体系采用不同指令集;
• 哈佛结构是将数据与指令分开存储并行;
• 冯诺依曼结构是混合存储的
(2)ARM工作模式
User 、FIQ、IRQ、System、Supervisor、Abort、Undef
(3)寄存器
ARM有37个寄存器
(4)ARM指令机器码
正常工作之外的流程都叫异常
设备树:用树型结构描述设备节点
控制硬件的软件
驱动是专门为系统编写的配置文件,没有驱动电脑便无法正常运行,简单来说,驱动是连接电脑和其他硬件的必备程序。
上拉输入、下拉输入、推挽输出、开漏输出。
串/并、速度、全/半双工、总线拓扑等
栈顶
它们分别是:开始信号、结束信号和应答信号。
FIQ和IRQ是两种不同类型的中断,ARM为了支持这两种不同的中断,提供了对应的叫做FIQ和IRQ处理器模式(ARM有7种处理模式)。
FIQ的中断向量地址在0x0000001C,而IRQ的在0x00000018。
spi四种模式SPI的相位(CPHA)和极性(CPOL)分别可以为0或1,对应的4种组合构成了SPI的4种模式(mode)
Mode 0 CPOL=0, CPHA=0
Mode 1 CPOL=0, CPHA=1
Mode 2 CPOL=1, CPHA=0
Mode 3 CPOL=1, CPHA=1
原文地址:SPI总线传输的4种模式 - 广漠飘羽 - 博客园 (cnblogs.com)
一、概述
在芯片的资料上,有两个非常特殊的寄存器配置位,分别是 CPOL (Clock POlarity)和 CPHA (Clock PHAse)。
CPOL配置SPI总线的极性
CPHA配置SPI总线的相位
极性和相位,这么专业的名词,非常难理解。我们不妨从时序图入手,了解极性和相位的效果。
二、SPI总线的极性
极性,会直接影响SPI总线空闲时的时钟信号是高电平还是低电平。
CPOL = 1:表示空闲时是高电平
CPOL = 0:表示空闲时是低电平
由于数据传输往往是从跳变沿开始的,也就表示开始传输数据的时候,是下降沿还是上升沿。
如下图:
三、SPI总线的相位
一个时钟周期会有2个跳变沿。而相位,直接决定SPI总线从那个跳变沿开始采样数据。
CPHA = 0:表示从第一个跳变沿开始采样
CPHA = 1:表示从第二个跳变沿开始采样
至于跳变沿究竟是上升沿还是下降沿,这取决于 CPOL。记住, CPHA 只决定是哪个跳变沿采样。
四、4种模式
CPOL 和 CPHA 的不同组合,形成了SPI总线的不同模式。
模式0 (CPOL=0; CPHA=0)
特性:
CPOL = 0:空闲时是低电平,第1个跳变沿是上升沿,第2个跳变沿是下降沿
CPHA = 0:数据在第1个跳变沿(上升沿)采样
效果图:
模式1 (CPOL=0; CPHA=1)
特性:
CPOL = 0:空闲时是低电平,第1个跳变沿是上升沿,第2个跳变沿是下降沿
CPHA = 1:数据在第2个跳变沿(下降沿)采样
效果图:
模式2 (CPOL=1; CPHA=0)
特性:
CPOL = 1:空闲时是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿
CPHA = 1:数据在第2个跳变沿(上升沿)采样
效果图: