下面这些是我在面试和笔试的时候碰到过的比较常见的问题,有些不难,但是就内容太多了,一时难以想起。
1.公有私有保护成员、公有私有保护继承
公有成员:在类外可以通过对象直接访问。
私有成员:对外隐藏,对子类开放,在类内部使用(在类的成员函数中使用)。
保护成员:对外隐藏,只在类内部使用(在类中的成员函数使用)。
公有继承:基类的公有成员和保护成员作为派生类的成员时,都保持原有的状态,但是基类的私有成员仍然是私有的,不能被子类访问。
私有继承:基类的公有成员和保护成员成为了派生类的私有成员,并且不能被派生类的子类访问。
保护继承:基类的公有成员和保护成员都成为了派生了的保护成员,只能被派生类的成员函数或者友元访问,基类的私有成员仍然是私有的。
2.什么是多态?多态的实现原理是什么?
多态就是在基类的函数前面加上virtual关键字,然后在派生类中重写这个函数(覆盖虚表),运行的时候就会根据实际的对象类型来调用相应的函数,如果是派生类,就调用派生类的函数,如果是基类,就调用基类的函数。
3.数组与链表的异同
数组是将元素在内存中连续存放的,每个元素占用的内存相同,所以可以通过数组下标快速访问数组中的元素,如果在数组中增加一个元素,那么就要移动大量的数据,删除也是一样。如果是需要快速访问,但是很少插入和删除的数据,就应该用数组。
链表中的元素在内存中是不按顺序存放的,是通过元素中的指针练习到一起,每个节点包括两部分,元素数据和指向下一个节点地址的指针。如果要访问链表中的元素,需要从第一个元素开始,一直找到元素的位置,插入和删除对于链表十分简单。如果是需要经常插入和删除的数据,用链表就比较方便。
4.什么是线性表?线性表有哪些种类?
线性表是数据结构的一种,一个线性表是n个具有相同特性的数据元素的优先序列。线性表有以下特点:
(1)集合中必然存在唯一的一个“第一个元素”
(2)集合中必然存在唯一的一个"最后一个元素"
(3)除最后一个与元素之外,每个元素都有唯一的后继
(4)除第一个元素之外,每个元素都有唯一的前驱。
线性表分为顺序存储和连式存储。
顺序存储:顺序表,使用数组实现,一组连续的存储单元,数组的大小有两种方式制定,静态分配和动态扩展。
优点:随机访问特性,查找O(l)时间,存储密度高,逻辑上相邻的元素,物理上也相邻。
缺点:插入和删除需要移动大量的元素。
链式存储:单链表、双链表、循环链表、静态链表。
5.队列与栈的异同
栈和队列都是线性表,都是限制了插入删除点的线性表。
共同点:都是只能在线性表的端点插入和删除。
不同的点:栈的插入和删除都是在线性表的同一个端点,俗称栈顶,不能插入和删除的端点称为栈顶,特性是先进后出。
队列是在线性表的表头插入,表位删除,表头一般称为对头,表位称为队尾,特性是先进后出。
5.获取触摸屏信息的函数(项目)
linux系统中有个input_event()函数可以获取触摸屏的触摸数据,可以获取的事件类型有按键事件、同步事件、相对坐标事件、绝对坐标事件。
6.linux系统文件类型
普通文件(-):ELF文件,文本文件
目录文件(d)
字符设备文件(c):访问字符设备
块设备文件(b):访问块设备
链接文件(l):相当于快捷方式
管道文件(p):用于管道通信
套接字文件(s):用于socket通信
7.TCP/UDP通信
TCP的特点:面向连接,只支持一对一,面向字节流,可靠的传输
UDP的特点:无连接,不可靠传输,支持一对一,一对多,面向报文
TCP为什么是可靠传输:
(1)通过TCP连接传输的数据无差错,不丢失,不重复,且按顺序到达。
(2)TCP报文头里面的序号能使TCP的数据按序到达
(3)报文头里的确认序号能保证不丢包,累计确认及超时重传机制。
(4)TCP拥有流量控制及拥塞控制的机制。
8.TCP的三次握手/四次挥手
三次握手:
(1)SYN请求建立连接:客户端向服务端发送请求报文;即SYN=1,ACK=0,seq=x。建立连接时,客户端发送SYN包(syn=j)到服务端,并进入SYN_SENT状态,等待服务器确认;
(2)ACK针对SYN的确认应答,SYN(请求建立连接):服务器收到SYN包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYNC_RECV状态;
(3)ACK针对SYN的确认应答:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务端进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
这样连接建立完成,发送数据
四次握手:
(1)FIN请求切断连接:TCP发送一个FIN(结束),用来关闭客户到服务端的连接。客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
(2)ACK针对FIN的确认应答:服务端收到这个FIN,他发回一个ACK(确认),确认收到序号为收到序号+1,和SYN一样,一个FIN将占用一个序号。服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
(3)FIN请求切断连接:服务端发送一个FIN(结束)到客户端,服务端关闭客户端的连接。服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
(4)ACK针对FIN的确认应答:客户端发送ACK(确认)报文确认,并将确认的序号+1,这样关闭完成。客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
9.CJSON
10.进程与线程
进程是系统资源分配和调度的基本单位,是操作系统结构的基础。实际上就是在系统中正在运行的一个应用程序,程序一旦开始运行就是进程,进程----分配资源的最小单位。
线程是操作系统进行运算调度的基本单位。系统分配处理器时间资源的基本单元,或者说是进程之类独立执行的一个单元执行流。
1、一个进程可以有多个线程,但至少有一个线程;而一个线程只能在一个进程的地址空间内活动。
2、资源分配给进程,同一个进程的所有线程共享该进程所有资源。
3、CPU分配给线程,即真正在处理器运行的是线程。
4、线程在执行过程中需要协作同步,不同进程的线程间要利用消息通信的办法实现同步。
11.算法复杂度
稳定:如果a原本在b的前面,而a=b,排序之后a仍然在b的前面。
不稳定:同上,但是排序后a在b的后面。
时间复杂度:对排序算法的总操作次数,反应出当要排序的数列长度n变化时,操作次数呈现的规律。
空间复杂度:指的是算法在计算机内执行的时候所需的存储空间的度量,也是数据规模为n的函数。
12.什么事内存泄漏?内存泄漏有哪些?怎么避免内存泄漏?
内存泄漏值得就是在程序动态申请的内存在使用完之后,却没有进行释放,导致这片内存没有被系统回收,就会占用着内存,导致程序的内存变大,系统内存不足。
内存泄漏的分类如下:
堆内存泄漏:堆内存指的就是通过new,malloc等函数从堆中申请的堆存,用完之后必须通过delete,free释放掉,如果没有释放,就会导致这片空间没有办法被再次使用。
系统资源泄漏:程序在使用系统分配的资源(套接字、管道、文件描述符等)过后,没有释放,会导致资源浪费,甚至导致系统不稳定。
避免内存泄漏的方法:(1)养成良好的编码规范,申请的空间用完之后记得释放。(2)采用智能指针来管理资源。(3)使用检测工具来检测内存是否泄漏。
13.拷贝构造是什么?为什么要有拷贝构造?拷贝构造分为哪些类型?
对于普通的数据类型来说,他们之间的赋值是很简单的,但是对于有着复杂内部结构的类对象来说,拷贝就很麻烦了。拷贝构造函数是一种特殊的构造函数,函数名必须和类名一致,必须的参数是本类型的一个引用变量。很多时候编译器会默认生产一个“默认的拷贝构造函数”,这个生成的函数很简单,只是对旧对象的数据成员的值进行拷贝,用来给新对象的数据成员进行赋值。
拷贝构造函数分为浅拷贝和深拷贝,浅拷贝值得是在对象复制的时候,只是对对象中的数据成员进行简单的赋值,系统自动生成的拷贝构造函数就是浅拷贝,一般来说,浅拷贝都足够用了,但是当对象中存在动态成员,浅拷贝就会出现问题。这时候就要用到深拷贝了,深拷贝中,对于对象中的动态成员,会重新动态分配空间。当对象成员中有指针的时候,就要用到深拷贝,因为浅拷贝在对象结束的时候,会调用两次析构函数,但是只有一个东西要析构,就会造成指针悬挂(指针指向非法的地址)的现象。类中可以存在多个拷贝构造函数。
14.什么是智能指针?智能指针有哪些?
智能指针是一个类,类中的构造函数传入一个普通指针,析构函数释放传入的指针,这个类是栈上的对象,当程序或者函数结束的时候就会自动释放。
常用的智能指针有:(1)std::suto_ptr 不支持赋值和复制,编译的时候也不会报错(2)unique_ptr 同样不支持赋值和复制,但是编译的时候会报错 (3)shared_ptr 可以随便赋值,当内存的引用计数变成0的时候,会被释放 (4)weak_ptr
15.虚函数
用virtual关键字修饰的成员函数,如果一个类中包含虚函数,那么这个类的对象中就会包含一个虚表指针(存储在对象的最前面)。虚表是用来存储虚函数指针的。虚函数的调用过程:开始从虚表中查找,如果找到就执行,没有找到就去对象代码中找,找到就执行,还是没有找到就报错。
覆盖:如果父类中的虚函数在子类中重新实现(同名同参),虚表中的虚函数就会被子类的虚函数覆盖(只覆盖虚表),如果子类添加新的虚函数,该函数就会添加在虚表的后面。
17.什么是纯虚函数?
定义了但是不实现的虚函数叫做纯虚函数,纯虚函数相当于接口函数,如果一个类中包含了纯虚函数,那么这个类就是抽象类。
18.什么是抽象类?
抽象类不能创建对象(接口),如果基类中的纯虚函数在派生类中没有全部实现,那么派生类还是抽象类。
19.什么是虚析构?
在多态的实现过程中,把子类的指针赋值为父类指针,最后delete父类的指针,这个时候就只会调用父类的析构函数,不会调用子类的自购函数,如果把父类的析构函数设置成虚析构,那么delete就会自动从子类开始析构。
20.static关键字的作用
static修饰的特点:
(1)数据存储在数据段(局部全局)
(2)只分配一次空间,也只初始化一次
(3)修饰的变量或者函数只能在本文件中使用(文件作用域)
static修饰的类型:
(1)修饰成员变量
1.static修饰的成员变量空间不在对象空间中
2.static修饰的成员变量要在类外初始化(只初始化一次)
3.static修饰的成员变量就是为这个类的对象所共用
4.如果成员变量是公有的,就可以通过类名直接调用(先于类对象而存在)
(2)修饰成员函数
1.如果成员函数是公有的,就可以通过类名直接使用(先于类对象而存在)
2.静态成员函数中不能使用非静态的成员
21.const关键字的作用
(1)修饰变量,将这个变量变成常量,取代了C中的宏定义
(2)修指针和常量
(3)修饰函数传入的参数
(4)修饰函数返回值
(5)修饰成员函数和对象,const对象只能访问const成员函数,而非const对象可以访问任意的成员函数。conts对象的成员是不能修改的,但是通过指针维护的对象可以修改。const成员函数不能改变对象的值,不管这个对象是否被const修饰。
22.网络的七层模型、五层模型
OSI参考模型,从上到下是:
应用层:与其他计算机进行通讯的一个应用,解决通信双方数据传输的问题,传输协议有:HTTP FTP TFTP SMTP DNS HTTPS
表示层:定义数据格式以及加密,加密格式有JPEG,ASCII,DECOIC
会话层:定义了如何开始、控制和终止一个会话
传输层:定义传输数据协议的端口号、流控和差错校验,协议有TCP,UDP
网络层:进行逻辑地址寻址,实现不同网络之间的路径选择 协议有ICMP,IGMP,IP(IPV4,IPV6),ARP,RARP
数据链路层:建立逻辑连接,进行硬件地址寻址,用MAC地址访问介质,但是发现错误不能纠正。
物理层:建立、维护和断开连接。
五层模型:
应用层:为应用软件提供服务,屏蔽了网络传输的相关细节。
传输层:向用户提供一个可靠的端到端的服务,屏蔽了下层通信的细节。
网络层:为数据在节点之间传输创建逻辑链路。
数据链路层:在通信的实体之间简历数据链路连接。
物理层:定义物理设备之间如何传输数据。
23.select、pool、epoll的区别与优缺点
I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。select,poll,epoll都是IO多路复用的机制。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
select
原理:select 的核心功能是调用tcp文件系统的poll函数,不停的查询,如果没有想要的数据,主动执行一次调度(防止一直占用cpu),直到有一个连接有想要的消息为止。从这里可以看出select的执行方式基本就是不同的调用poll,直到有需要的消息为止。
缺点:1、每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
2、同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大;
3、select支持的文件描述符数量太小了,默认是1024。
优点:1、select的可移植性更好,在某些Unix系统上不支持poll()。
2、select对于超时值提供了更好的精度:微秒,而poll是毫秒。
poll
原理:poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
缺点:1、大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义;
2、与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符。
优点:1、poll() 不要求开发者计算最大文件描述符加一的大小。
2、poll() 在应付大数目的文件描述符的时候速度更快,相比于select。
3、它没有最大连接数的限制,原因是它是基于链表来存储的。
epoll
原理:epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时, 返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一 个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射技术,这 样便彻底省掉了这些文件描述符在系统调用时复制的开销。
优点:1、支持一个进程打开大数目的socket描述符
2、IO效率不随FD数目增加而线性下降
3、使用mmap加速内核与用户空间的消息传递
三者的对比与区别:
1、select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
2、select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。
24.SQL数据库有哪些数据类型?有哪些特殊的关键字?
char(n) | 存放固定长度的字符串,用户指定长度为n。如果没有使用n个长度则会在末尾添加空格。 |
varchar(n) | 可变长度的字符串,用户指定最大长度n。char的改进版,大多数情况下我们最好使用varchar。 |
int | 整数类型 |
smallint | 小整数类型 |
numeric(p,d) | 定点数,精度由用户指定。这个数有p位数字(包括一个符号位)d位在小数点右边。 |
real ,double precision | 浮点数和双精度浮点数。 |
float(n) | 精度至少位n位的浮点数 |
部分关键字:SELECT、INSERT、DELETE、UPDATE
特殊的关键字:primary key、foreign key references、not null
25.sizeof strlen的使用
sizeof可以用来计算数据在内存中占用的存储空间,以字节为单位进行计数。sizoef(int)=4;sizeof(指针)=系统位数=4(32位系统)
strlen用来计算指定字符的长度,但是不包括结束字符“ ”
26.switch的使用
switch中的case只能用来判断int型数据。
27.与或非
逻辑的与或非
&& 与操作,只有当两者都为真的时候,才会返回true,表达式会先去验证前面的一部分,如果前面一部分为真才回去验证后面的一部分,否则直接返回false。
|| 或操作,只要符号前后有一个为真,就会返回true,否则返回false。也是先验证前面的,前面的为false才会验证后面的,否则直接返回true。
!逻辑非,true变成false,false变成true。
按位与或非
&、|、~ 分别是按位与或非
5&6 = 0101 & 0110 = 4 5&&6 -> 计算原则: 非0即真 真&&真=真 5|6 = 0101 | 0110 = 7 5||6 -> 计算原则: 非0即真 真||真=真
28.数组、指针、数组指针、指针数组、函数指针、二级指针
之前做过一个公司的题目,就40道选择题,20几道指针,指来指去,指的我头皮发麻。
数组:
(1)数组只有初始化的时候可以整体赋值,之后只能一个一个单独赋值了。
(2)与普通变量一样,int数组在定义的时候不初始化,如果是全局变量,就全是0,如果是局部变量,就全是随机值。
(3)数组名 当sizeof()作用于数组名的时候,数组名就是整个数组的内存空间,返回值就是整个数组的大小;当数组名没有被sizeof()修饰的时候,数组名就是数组元素的首地址。
int A[3]; A 数组名 A[0] 数组首元素 &A[0] 数组首元素的地址 结论:A=&A[0]
指针
定义:指针指的是内存上的地址,例如:0x400000,指针是一个唯一的地址,能够确定申请到的内存空间在哪里。
指针变量:专门用来存放地址的变量,例如:int *p;
指针定义的步骤:
(1)先写一个* (2)在*后面写一个指针变量名(*p) (3)确定该指针所指向的内容 例如:int a (4)将第三部的变量名去掉 例:int (5)将第二部的结果放在第四部的结果后面 例:int *p,即指向一个int类型数据的指针变量
结果:int *p;//指针变量,变量名:p;数据类型:int *;
int a=100; int *p = &a;//将a的地址取出来给指针p
已知指针变量的值,如果求出该指针指向的内容?
取址符:& 已知变量a,求地址:&a
解引用:* 已知指针p,求变量:*p
&与*是一对逆运算。
指针运算,加减法:
加法:
int a; int *pa = &a; pa+1=? //+1是指向上移动一个单位,-是指向下移动,由于pa指向的是int型数据,pa+1就是向上移动一个单位
//移动4个字节,移动的字节数是根据数据类型来确定的
减法:
int a,c;
int *pa = &a;
int *pb = &c;
pa-pb=?;//结果就是两个指针在内存地址中相差多少个int型的字节
复杂指针:二级指针、数组指针、函数指针。
(1)二级指针:指向指针的指针(一般用二级指针,一般人不会闲得蛋疼去用三级、四级指针)
int a = 100;//在内存中连续申请4个字节的空间,使用变量a间接访问这片空间,将常量区100赋值给a。 int *pa = &a;//在内存中连续申请四个字节的空间,使用变量pa间接访问这片空间,将整型变量a的地址赋值给pa,即pa所指向的内存地址中存储的内容是变量a 的地址。 int **px = &pa;//在内存中连续申请4个字节的空间,使用变量px间接访问这片内存空间,将指针变量pa的地址赋值给px,即px所指向的内存空间存储的内容是指针pa的地址。
(2)数组指针:指向数组的指针。
int a; int A[3]; int *pa = &a; int (*pa)[3] = A[3];//表示这个指针指向一个具有三个int型数据的数组
解引用:
int a=100; int *pa = &a;//*pa得到100, int A[3] = {100,200,300}; int (*p)[3] = &A;
问题一:*p得到什么?得到数组首元素的地址,*p = *(&A) = A = &A[0]
问题二:p[0]/p[1]得到什么?什么也得不到,p[0] = *(p+0) = *p = &A[0],p[1] = *(p+1) = *(&A+1);//此时p+1已经越界访问了,解引用就是访问未知区域了。
问题三:(*p)[1]得到什么?(*p)[1] = (*&A)[1] = A[1] = 200
做指针运算的时候,记住两个公式:(1)&与*是一对逆运算,可以约掉;
(2)a[n]=*(a+n)
(3)函数指针:指向一个函数的指针。
定义函数指针:
int a; int fun(int x,int y) int *pa = &a; int (*p)(int,int) = &fun;//这个指针指向一个返回值为int类型,形式参数有两个int型数据的函数
数据类型:int (*p)(int,int) 变量名:p
在C语言中,函数的名字实际上就是函数的地址,int fun(int x,int y),函数名fun等价于&fun,即 int(*)(int,int)=&fun;
函数指针很少单独使用(定义一个指针,通过指针来调用函数),一般是用于接收函数的地址,及传递一个函数给另一个函数作为参数,形式参数就是一个函数指针。
29. 指针的用法
(1)在函数调用的时候改变某个变量的值。
经典例子:int *p;func(&p);如果这里想要改变p的值,就必须传p的地址,而p本身就是个指针,就有了二级指针的概念,二级指针一般只会出现在函数的形式参数列表中,不会随意定义。
(2)访问匿名内存的时候。
在C语言中,由于立即寻址空间访问硬件以及资源的原理,很多资源只是有着具体的地址,但是却没有变量名(能够用名字访问内存的只有局部变量或者全局变量(函数名是一个地址)),因此我们经常直接引用地址。例如:某GPIO数据寄存器的内存地址是0x12345678,我们会将该地址转化为指针,然后直接解引用该内存地址。
#define GPIO_DATA *((volatile unsigned int*)0x12345678
(3)优化加速函数调用
30.指针与数组的异同
(1)数组名不能给其赋值(指针常量)
(2)sizeof用来测量数组名,得到的是数组的大小,用来测量指针的话,永远是系统位数那么大
(3)在函数传参的过程中,函数的形式参数就是一个指针
(4)数组是不能通过函数返回出去的,语法不会报错,只是没有什么意义,会被释放掉
31.数据占用的内存空间的大小
数据类型 | 32位系统 | 64位系统 |
void | 0 | 0 |
char | 1 | 1 |
int | 4 | 4 |
short int | 2 | 2 |
float | 4 | 4 |
double | 8 | 8 |
long | 4 | 8 |
long double | 12 | 16 |
long long | 8 | 8 |
指针 | 4 | 8 |
32.大小端存储
大端存储模式:数据的低位保存在内存的高地址中,数据的高位,保存在内存的低地址中。0x78563412
小端存储模式:数据的低位保存在内存的低地址中,数据的高位,保存在内存的高地址中。0x12345678
33.字符串处理函数的底层实现
char *strcpy(char *dest,const *src);//把后面的src字符串的内容拷贝到dest字符串当中,拷贝结束是以遇到" "才停下。
char *strcat(char *dest,char *src);//将src字符串的内容合并到dest字符串的后面。
int strcmp(const char *str1,const char *str2);//比较两个字符串是否相等,原理是用字符相减的形式,如果相等,返回值为0,;如果不相等,返回值根据ASCII原理,返回正值(str1>str2),负值(str1<str2)。
size_t strlen(const char *s);//测量字符串的长度,不包括" "结束符,该函数还可以用来测量指针。
strcpy以及strncpy的实现:
char *strcpy(char *s1,char *s2) { char *tmp=s1; while(*s1++=*s2++); return tmp; } char *strncpy(char *s1,char *s2,size_t n) { char *tmp=s1; while(n) { if(!(*s1++=*s2++))break;//遇到' '就结束循环 n--; } while(n--)*s1++='