(1)计算机操作系统的目录:全书共分12章
处理器管理,进程调度,存储管理,设备管理,文件管理
(2)进程和线程有什么关系?它们的区别又是什么?
定义:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
关系:一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
区别:进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
(3)进程的三种基本状态
进程在运行中不断地改变其运行状态。通常,一个运行进程必须具有以下三种基本状态。
就绪(Ready)状态 :当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
执行(Running)状态:当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。
阻塞(Blocked)状态:正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
进程三种状态间的转换:一个进程在运行期间,不断地从一种状态转换到另一种状态,它可以多次处于就绪状态和执行状态,也可以多次处于阻塞状态。
1就绪→执行
处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成执行状态。
2 执行→就绪
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。
3执行→阻塞
正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。
4 阻塞→就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
进程中程序关于一个操作数据集上的一次运行活动,同时,进程也是资源和内存分配的基本单位。
进程调度是调度谁:因为程序在CPU中是走走停停的,为了使CPU能够达到最大的使用效率,当某一个进程在运行过程中需要停止等待,比如说,需要等待I/O,而这时,访问外设的时候,时间就比较长,这时,该进程就会让出CPU的使用权,按照所规定的调度算法,选择在就绪队列的中的其中一个进程,将当前的CPU进程保存在PCB块中,以linux操作系统为例,就是调用shedul()中的switch_to()进程切换进程。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免死锁的方法
1、死锁预防 ----- 确保系统永远不会进入死锁状态
2、避免死锁 ----- 在使用前进行判断,只允许不会产生死锁的进程申请资源
两种避免办法:
1、如果一个进程的请求会导致死锁,则不启动该进程
2、如果一个进程的增加资源请求会导致死锁 ,则拒绝该申请。
避免死锁的具体实现通常利用银行家算法
银行家算法
3、死锁检测与解除 ----- 在检测到运行系统进入死锁,进行恢复。
允许系统进入到死锁状态
死锁检测
如果利用死锁检测算法检测出系统已经出现了死锁 ,那么,此时就需要对系统采取相应的措施。常用的解除死锁的方法:
1、抢占资源:从一个或多个进程中抢占足够数量的资源分配给死锁进程,以解除死锁状态。
2、终止(或撤销)进程:终止或撤销系统中的一个或多个死锁进程,直至打破死锁状态。
a、终止所有的死锁进程。这种方式简单粗暴,但是代价很大,很有可能会导致一些已经运行了很久的进程前功尽弃。
b、逐个终止进程,直至死锁状态解除。该方法的代价也很大,因为每终止一个进程就需要使用死锁检测来检测系统当前是否处于死锁状态。另外,每次终止进程的时候终止那个进程呢?每次都应该采用最优策略来选择一个“代价最小”的进程来解除死锁状态。一般根据如下几个方面来决定终止哪个进程:
进程的优先级
进程已运行时间以及运行完成还需要的时间
进程已占用系统资源
进程运行完成还需要的资源
终止进程数目
进程是交互还是批处理
尽量避免同时获得多个锁,如果一定有必要这么做,则有一个原则:如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死锁。比如一个程序中用到锁1、锁2、锁3,它们所对应的Mutex变量的地址是锁1<锁2<锁3,那么所有线程在需要同时获得2个或3个锁时都应该按锁1、锁2、锁3的顺序获得。如果要为所有的锁确定一个先后顺序比较困难,则应pthread_mutex_trylock调用代替pthread_mutex_lock 调用,以免死锁
(1)顺序存储方式
简单的说,顺序存储方式就是在一块连续的存储区域;
一个接着一个的存放数据。顺序存储方式把逻辑上相连的结点存储在物理位置上相邻的存储单元里,结点间的逻辑关系由存储单元的邻接挂安息来体现。顺序存储方式也称为顺序存储结构(sequentialstorage structure),一般采用数组或者结构数组来描述。线性存储方式主要用于线性逻辑结构的数据存放,而对于图和树等非线性逻辑结构则不适用。
(2)链接存储方式
链接存储方式比较灵活,其不要求逻辑上相邻的结点
在物理位置上相邻,结点间的逻辑关系由附加的引用字段表示。一个结点的引用字段往往指导下一个结点的存放位置。链接存储方式也称为链接式存储结构(LinkedStorage Structure),一般在原数据项中增加应用类型来表示结点之间的位置关系。
(3)索引存储方式
索引存储方式是采用附加索引表的方式来存储结点信息的一种存储方式。索引表由若干个索引项组成。索引存储方式中索引项的一般形式为:(关键字、地址)。其中,关键字是能够唯一标识一个结点的数据项。
索引存储方式还可以细分为如下两类:
(4)散列存储方式
散列存储方式是根据结点的关键字直接计算出该结点的存储地址的一种存储的方式。在实际应用中,往往需要根据具体数据结构来决定采用哪一种存储方式。同一逻辑结构采用不同额存储方法,可以得到不同的存储结构。而且这四种节本存储方法,既可以单独使用,也可以组合起来对数据结构进行存储描述。
(1)正在执行的进程执行完毕。这时,如果不选择新的就绪进程执行,将浪费处理机资源。
(2)执行中进程自己调用阻塞原语将自己阻塞起来进入睡眠等状态。
(3)执行中进程调用了P原语操作,从而因资源不足而被阻塞;或调用了v原语操作激活了等待资源的进程队列。
(4)执行中进程提出I/O请求后被阻塞。
(5)在分时系统中时间片已经用完。
(6)在执行完系统调用等系统程序后返回用户进程时,这时可看作系统进程执行完毕,从而可调度选择一新的用户进程执行。
以上都是在可剥夺方式下的引起进程调度的原因。在CPU执行方式是可剥夺时.还有
(7)就绪队列中的某进程的优先级变得高于当前执行进程的优先级,从而也将引发进程调度。
应用层,表示层,会话层,传输层,网络层,这五层不完全依赖于硬件实现。
数据链路层,物理层,这两层常见的涉及到相关硬件设计
物理层是OSI的第一层,它虽然处于最底层,却是整个开放系统的基础。物理层为设备之间的数据通信提供传输媒体及互连设备,为数据传输提供可靠的环境。
RARP: Reverse Address Resolution Protocal,逆地址解析协议。
允许局域网的物理机器从网关服务器的ARP表或缓存上请求IP地址。
比如局域网中有一台主机只知道自己的物理地址而不知道自己的IP地址,那么可以通RARA协议发出征求自身IP地址的广播请求,然后由RARP服务器负责回答。
RARP协议广泛应用于无盘工作站引导时获取IP地址。RARP允许局域网的物理机器从网关服务器ARP表或者缓存上请求其IP地址。
RARP协议的工作过程
1 主机发送一个本地RARP广播,在广播包中,声明自己的MAC地址并且请求任何收到此请求的RARP服务器分配一个IP地址。
2 本地网段的RARP服务器收到此请求后,检查器RARP列表,查找该MAC地址对应的IP地址。
3 如果存在,RARP服务器就给源主机发送一个响应数据包,并将IP地址提供给对方主机使用。
4 如果不存在,RARP服务器对此不做任何响应。
5 源主机收到从RARP服务的响应信息,就利用得到的IP地址进行通信。如果一直没收到RARA服务器的响应信息,表示初始化失败。
微信使用的是TCP(视频是UDP,因为要实时性)
微信通讯中使用了HTTP短连接和TCP长连接,并没有用到UDP,其中登陆bai验证和头像身份信息及日志等功能采用的HTTP,文本消息、语音消息、视频消息、图片消息这些使用的是TCP长连接。通过心跳包来维护长连接状态,300S一个心跳
QQ既有UDP也有TCP!
不管UDP还是TCP,最终登陆成功之后,QQ都会有一个TCP连接来保持在线状态。这个TCP连接的远程端口一般是80,采用UDP方式登陆的时候,端口是8000。
UDP协议是无连接方式的协议,它的效率高,速度快,占资源少,但是其传输机制为不可靠传送,必须依靠辅助的算法来完成传输控制。QQ采用的通信协议以UDP为主,辅以TCP协议。由于QQ的服务器设计容量是海量级的应用,一台服务器要同时容纳十几万的并发连接,因此服务器端只有采用UDP协议与客户端进行通讯才能保证这种超大规模的服务。
QQ客户端之间的消息传送也采用了UDP模式,因为国内的网络环境非常复杂,而且很多用户采用的方式是通过代理服务器共享一条线路上网的方式,在这些复杂的情况下,客户端之间能彼此建立起来TCP连接的概率较小,严重影响传送信息的效率。而UDP包能够穿透大部分的代理服务器,因此QQ选择了UDP作为客户之间的主要通信协议。
采用UDP协议,通过服务器中转方式。因此,现在的IP侦探在你仅仅跟对方发送聊天消息的时候是无法获取到IP的。大家都知道,UDP 协议是不可靠协议,它只管发送,不管对方是否收到的,但它的传输很高效。但是,作为聊天软件,怎么可以采用这样的不可靠方式来传输消息呢?于是,腾讯采用了上层协议来保证可靠传输:如果客户端使用UDP协议发出消息后,服务器收到该包,需要使用UDP协议发回一个应答包。如此来保证消息可以无遗漏传输。之所以会发生在客户端明明看到“消息发送失败”但对方又收到了这个消息的情况,就是因为客户端发出的消息服务器已经收到并转发成功,但客户端由于网络原因没有收到服务器的应答包引起的。
线性表 二叉树
实际
设计思路:
要想设计CPU,就要设计一个的简易指令系统,在根据指令搭建对应的数据通路,在数据通路的基础上实现控制逻辑,下一步是加上流水线,划分流水线,紧接着解决流水线中冲突的问题,提高流水线的效率,最后解决CPU异常的问题。
嵌入式系统一般指系统软件嵌入在系统硬件内的、用户无法自行修改的系统,嵌入式系统最大优点就是安全性、稳定性好。
数组:数组是用于储存多个相同类型数据的集合。
指针:指针相当于一个变量,但是它和不同变量不一样,它存放的是其它变量在内存中的地址。
赋值、存储方式、求sizeof、初始化等
1.赋值
同类型指针变量可以相互赋值,数组不行,只能一个一个元素的赋值或拷贝
2.存储方式
数组:数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下进行访问的,多维数组在内存中是按照一维数组存储的,只是在逻辑上是多维的。
数组的存储空间,不是在静态区就是在栈上。
指针:指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。
指针:由于指针本身就是一个变量,再加上它所存放的也是变量,所以指针的存储空间不能确定。
3.求sizeof
数组:
数组所占存储空间的内存:sizeof(数组名)
数组的大小:sizeof(数组名)/sizeof(数据类型)
指针:
在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8。
4.初始化
1、汇编语言
使用汇编语言的特点是程序运行的结果很直观,每一条汇编指令执行之后的结果都可以立即看到,便于对程序的跟踪和调试。从这个角度讲。可以认为汇编是一种最简单的语言。同时,用汇编语言开发的程序执行效率很高,不会产生冗余代码,节省内存,并且运行速度很快,这些都是汇编的优点。但是,汇编有一个最大的缺点就是语法的逻辑性不够直观,直接导致了开发效率低下,只用一条高级语言语句就可以实现的一个功能也许用多条汇编指令才能实现,所以除非必要,目前一般都使用高级语言来编写嵌入式程序,汇编仅仅用在系统初始化,或者是严格要求时序的场合。
2、C语言
高级语言中。最常用的是C语言。C语言是一种十分优秀的语言,也是一种通用的程序设计语言,它可以用来进行底层系统程序设计,具有高效、灵活、功能丰富、表达力强和移植性好等特点,在程序员中备受青睐。C语言的设计者是丹尼斯·里奇(Dennis Ritchie)和肯·汤普逊(Ken Thompson),自其1970年诞生之后,就广泛应用于不同的操作系统设计中。例如UNlX、MS—DOS、Microsoft windows及Linux等。C语言是面向过程的高级语言,同时具有汇编语言的优点,可以用来直接控制硬件。C语言诞生之后,出现了很多细节不同的C编译器,为了统一C语言的标准,美国国家标准委员会(American National Standards InsTItute,ANSl)在1983年对C语言进行了标准化,颁布了第一个C语言标准草案(83 ANSI C。
3、Java语言
Java语言是由Sun公司的green小组设计的一种非常优秀的程序设计语言,其设计初衷是希望能够开发一种高度可移植、平台无关的程序设计语言。通过虚拟机的支持使Java程序在任何体系结构上都可以运行。Java语言在嵌入式系统中也有应用。它继承了C/C++语言的很多特点。开发效率高。很容易学习和理解。但是Java语言不支持直接对内存的操作,虽然提高了安全性,可以避免很多系统漏洞。但是在经常需要和硬件打交道的嵌入式领域不能不说是一个遗憾,因此Java语言并不能完全取代C/C++语言和汇编语言。
4、ObjecTIve—C语言
ObjecTIve—C,又称为()bject—C或ObjC,是一种将C语言扩充后实现面向对象编程的语言,使用于MacOS x和GNustep系统中,南布莱德·考克斯发明于20世纪80年代初。ObjecTIve—C可以采用GCC编译,现主要用于编写iOS操作系统应用程序和Mac OS X操作系统应用程序。Objective—C的流行很大程度上应归功于ipone的成功。
频分多址就bai是:fdma,以频率还划分的。时分多址bai就是:tdma,是时du隙来划分的;码分多址就是:cdma,是重新编码划分的
一、输入输出关系
组合逻辑电路是任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。而时序逻辑电路不仅仅取决于当前的输入信号,而且还取决于电路原来的状态,或者说,还与以前的输入有关。
二、结构特点
组合逻辑电路只包含门电路。而时序逻辑电路是组合逻辑电路+存储电路结合;输出状态必须反馈到组合电路的输入端,与输入信号共同决定组合逻辑的输出…
三、分析方法
组合逻辑电路是从电路的输入到输出逐级写出逻辑函数式,最后得到表示输出与输入关系的逻辑函数式。然后用公式化简法或者卡诺图化简法得到函数式的化简或变换,以使逻辑关系简单明了。有时还可以将逻辑函数式转换为真值表的形式。
时序逻辑电路:
1、写出每个触发器的驱动方程;
2、将驱动方程带入触发器的特性方程得到状态方程组;
3、根据逻辑图写出电路的输出方程;
状态转换过程描述:状态转换表、状态转换图、状态机流程图、时序图。
四、设计方法
组合逻辑电路:1、逻辑抽象;2、写出逻辑函数式;3、选定器件类型;4、将逻辑函数式化简或者变换成适当的形式;5、画出逻辑电路的连接图;6、工艺设计。
时序逻辑电路:1、逻辑抽象得到状态转换图或者状态转换表;2、状态化简;3、状态分配(状态编码);4、选触发器求出状态方程、驱动方程和输出方程;5、根据方程式画出逻辑图;6、检查设计的电路能否自启动。
静态内存是指在程序开始运行时由编译器分配的内存,它的分配是在程序开始编译时完成bai的,不占用CPU资源。
程序中的各种变量,在编译时系统已经为其分配了所需的内存空间,当该变量在作用域内使用完毕时,系统会
自动释放所占用的内存空间。
变量的分配与释放,都无须程序员自行考虑。
基本类型,数组
用户无法确定空间大小,或者空间太大,栈上无法分配时,会采用动态内存分配。
处理器不工作,电脑什么都做不了。
处理器的工作就是处理指令(多条指令就构成一个程序)。
处理器从内存中取指令集(程序)。
问题是如果断电的话,内存中的指令就会丢失。因而内存归类为“易失性”介质。
所以我们要把程序、数据存储在不易失性的介质中,比如硬盘和光盘。
确切的说,应该是“bai大信号图解分析法du和小信号模型分zhi析法”。
大信号图解分析dao法:就是zhuan当输入信号的能量很大时,或者通俗的说就是,当输入电压的幅值很大时,三极管有可能会进入饱和状态而不是放大状态,此时三极管的输出电压的幅值就会失真(和输入波形不一样),所以针对会出现这种情况,我们选用宏观的分析方法,即,大信号图解分析法。
小信号模型分析法:顾名思义,输入信号的电压的幅值很小,你用宏观的分析方法大信号图解分析法,显然结果会很不精确,所以就用小信号模型分析法。小信号模型,就是把抽象元件三极管等效成由电阻、受控电流源等元件组成的二端口网络。这些等效元件有具体数值,可直接数字计算。从而在这个基础上对信号进行分析。
功率放大电路应该是大信号下工作比较常见。
递归的效率问题及递归与循环比较
1.所谓的递归慢到底是什么原因呢?
大家都知道递归的实现是通过调用函数本身,函数调用的时候,每次调用时要做地址保存,参数传递等,这是通过一个递归工作栈实现的。具体是每次调用函数本身要保存的内容包括:局部变量、形参、调用函数地址、返回值。那么,如果递归调用N次,就要分配N局部变量、N形参、N调用函数地址、N返回值。这势必是影响效率的。
2.用循环效率会比递归效率高吗?
递归与循环是两种不同的解决问题的典型思路。当然也并不是说循环效率就一定比递归高,递归和循环是两码事,递归带有栈操作,循环则不一定,两个概念不是一个层次,不同场景做不同的尝试。
2.1递归算法:
优点:代码简洁、清晰,并且容易验证正确性。(如果你真的理解了算法的话,否则你更晕)
缺点:它的运行需要较多次数的函数调用,如果调用层数比较深,需要增加额外的堆栈处理(还有可能出现堆栈溢出的情况),比如参数传递需要压栈等操作,会对执行效率有一定影响。但是,对于某些问题,如果不使用递归,那将是极端难看的代码。
2.2循环算法:
优点:速度快,结构简单。
缺点:并不能解决所有的问题。有的问题适合使用递归而不是循环。如果使用循环并不困难的话,最好使用循环。
2.3递归算法和循环算法总结:
\1. 一般递归调用可以处理的算法,也通过循环去解决常需要额外的低效处理。
\2. 现在的编译器在优化后,对于多次调用的函数处理会有非常好的效率优化,效率未必低于循环。
3.递归和循环两者完全可以互换。如果用到递归的地方可以很方便使用循环替换,而不影响程序的阅读,那么替换成递归往往是好的。(例如:求阶乘的递归实现与循环实现。)
3.那么递归使用的栈是什么样的一个栈呢?
首先,看一下系统栈和用户栈的用途。
3.1系统栈(也叫核心栈、内核栈)是内存中属于操作系统空间的一块区域,其主要用途为: (1)保存中断现场,对于嵌套中断,被中断程序的现场信息依次压入系统栈,中断返回时逆序弹出; (2)保存操作系统子程序间相互调用的参数、返回值、返回点以及子程序(函数)的局部变量。
3.2用户栈是用户进程空间中的一块区域,用于保存用户进程的子程序间相互调用的参数、返回值、返回点以及子程序(函数)的局部变量。
我们编写的递归程序属于用户程序,因此使用的是用户栈
中断嵌套是指中断系统正在执行一个中断服务时,有另一个优先级更高的中断提出中断请求,这时会暂时终止当前正在执行的级别较低的中断源的服务程序,去处理级别更高的中断源,待处理完毕,再返回到被中断了的中断服务程序继续执行的过程。
printf() 的具体实现方法,可以跟踪或反汇编你这个程序进入到 printf() 函数里去看看,也可以反汇编看看你的 c 库里的那个 lib 文件里该函数的定义。大致上,就是先解析你的输出格式串成最终的显示字符串内容,然后将这个字符串输出到显示。
------解决方案--------------------
printf算什么呀。这只是C运行时库的一个函数,他所做的也只不过是调用操作系统的系统调用,系统调用又涉及到显示驱动程序
printf()函数
作用:向终端输出若干个任意类型的数据(putchar 只能输出字符,而且只能是一个字符,而printf可
以输出多个数据,且为任意类型)
一、 printf()的一般格式
printf(格式控制,输出列表);
例:
int i = 3;
double f = 4.56;
printf("i = %d, f = %f/n", i,f);
printf()是函数,“格式控制”和“输出列表”是其参数。可以表示为:printf(参数1,参数2,参数3,....,参数n);
其中“参数1”表示“格式控制”;其余参数表示“输出 列表。
机器人(Robot)是一种能够半自主或全自主工作的智能机器。
人工智能(Artificial Intelligence),英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新技术科学。
人工智能是计算机科学的一个分支,它企图了解智能的实质,可以产出一种新的可以和人类智能相似的方式做出反应的智能机器,该领域的研究主要有机器人、语言识别、图像识别、自然语言处理和专家系统等。
区别于单板机,把后者都是独立芯片的CPU、ROM、RAM、IO等都集中到了单个芯片里,所以叫“单片”机
Bug算法
向量势直方图法
动态窗口法
曲率速度法
单片机的启动过程是加电后,先运行芯片内部固有程序(这个程序是用户访问不到也改写不了的),即启动代码。启动代码程序建立完运行环境后,会去读串口状态,就是用户下载程序用到的各个端口,判断用户是否正在使用端口准备下载程序,如果是,就按用户要求,把用户程序下载到指定地址上。如果不是,就跳转到已经下载过的用户程序入口,从而把芯片控制权交给用户程序。如果是新的芯片还没有下载过,那么就停留在读取串口状态的循环中。
启动代码通常都烧写在flash中,它是系统一上电就执行的一段程序,它运行在任何用户c代码之前。上电后,arm处理器处于arm态,运行于管理模式,同时系统所有中断被禁止,pc到地址0处取指令执行。一个可执行映像文件必须有个入口点,而能放在rom起始处的映像文件的入口地址也必须设置为0.在汇编语言中,我们已经说过怎样定义一个程序的入口点,当工程中有多个入口点时,需要在连接器中使用-entry指出程序的入口点。如果用户创建的程序中,包含了main函数,则与c库初始化代码对应的也会有个入口点。
总的来说,启动代码主要完成两方面的工作,一是初始化执行环境,例如中断向量表、堆栈、i/o等;二是初始化c库和用户应用程序。在第一阶段,启动代码的人物可以描述为:
(1)建立中断向量表;
(2)初始化存储器;
(3)初始化堆栈寄存器;
(4)初始化i/o以及其他必要的设备;
(5)根据需要改变处理器的状态。
–>建立中断向量表
初始化代码必须建立好中断向量表,以备应用程序后续使用。如果系统的地址0处是rom,则中断向量表直接是一些跳转指令就可以了,他们转到相应的中断处理函数执行。如果系统的0地址处不是rom,则中断向量表是通过动态的方式创建的,这主要是通过存储器映射的方式来实现:即上电后,rom中的地址被映射到地址0,它首先开始执行以便完成环境的初始化,最重要的它会将中断向量表拷贝到ram中,然后通过地址映射将ram地址映射为0,这样ram中的中断向量就可以使用了。
–>初始化存储系统
对于有mmu的处理器,需要正确初始化mmu,没有的只需正确初始化存储控制器,为每个bank配置正确的参数就可以了。
–>初始化堆栈指针
初始化代码必须初始化处理器各个模式下的堆栈指针,所有系统或用户程序会涉及的处理器模式对应的堆栈指针都应该初始化。通常未定义指令和预取指终止异常对应模式的堆栈指针不需要配置,除非用户需要使用它们作为调试使用。
–>初始化堆栈指针
初始化代码必须初始化处理器各种模式下的堆栈指针,所有系统或用户程序会涉及的处理器模式对应的堆栈指针都应该被初始化。通常未定义指令和预取指终止异常对应模式的堆栈指针不需要配置,除非用户需要使用它们作为调试使用。
–>初始化i/o以及其他必要设备
关键的输入输出模块必须在中断打开之前被配置,例如看门狗,否则它们会在系统启动后产生复位信号。
–>改变处理器状态和模式
启动代码运行时,处理器状态认为管理模式,如果用户程序需要运行在用户模式,可以切换转入用户模式;所有处理器上电后是处于arm状态的,如果需要改变处理器状态,也可以在启动代码里切换到thumb态。
在执行环境建立起来后,接下来就是应用程序的初始化,简单点就是讲用户程序加载到他们相应的运行地址,初始化数据区等,这个阶段完成后,才能进入用户最终的c代码区域。用户应用程序的初始化过程包括:将rw段的数据拷贝到他们的运行地址处,同时在rw段后面初始化相应大小的zi段数据,把他们初始化为0,使用了库函数的程序(工程中有main函数)是在库函数_main中自动完成这些工作的。
有的时候是主设备,有的时候是从设备,
当CPU给DMA发送数据,字节数,此时他就是从设备。
当DMA控制总线,让外设与存储器之间传输数据时,就是主设备。
8259A有两种模式,一种是操作模式,一种是中断模式
单片机可通过端口的方式,CPU可发送in,out指令控制发送给8259进入中断模式,8259便接收中断向量号,通过中断判优选择结构选择紧急程度最高的中断进行相应,通过int端口给CPU发送中断请求,CPU收到后响应中断,8259给CPU发送中断向量号,CPU收到中断向量后进行执行。
和买五个一块钱的面包选哪种等
有一个人把饼分成两份的权利,另一个人有优先的挑选权利
1.树上有十只死的小鸟,打了一个,还剩十只。
2.树上有十只活的小鸟,打了一只,其他全吓跑了,一只也没有了。
3 4 5
3沿正方形对角线切开
4找一边的中点,让它与对面任意一角的顶点连接
5找一边的中点,找与其相邻任意一边的中点,连接
操作系统的定义:是管理计算机du系统的全部zhi硬件资源包括dao软件资源及数据资源;控制程zhuan序运行;改善人机shu界面;为其它应用软件提供支持等,使计算机系统所有资源最大限度地发挥作用,为用户提供方便的、有效的、友善的服务界面。
操作系统通常是最靠近硬件的一层系统软件,它把硬件裸机改造成为功能完善的一台虚拟机,使得计算机系统的使用和管理更加方便,计算机资源的利用效率更高,上层的应用程序可以获得比硬件提供的功能更多的支持。
一、定义
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
二、关系
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。
相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
三、区别
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1、简而言之,一个程序至少有一个进程,一个进程至少有一个线程。
2、线程的划分尺度小于进程,使得多线程程序的并发性高。
3、另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4、线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5、从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
四、优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
缺页中断
或者主动调用int 19H中断,将磁盘的数据读入到内存
当程序执行的指令或访问到的数据在内存中时可顺利执行;如果在磁盘中,需要系统自动将这部分信息装入,这称“部分装入”;如此刻没有足够的空闲内存空间,便把内存中不用的信息暂时移到磁盘上,这称部分替换。所以只要“部分装入”和“部分替换能够实现”,实现从磁盘中读入数据,来让CPU执行。
根据FCB汇集和组织形成的文件目录,文件目录将文件名称转化为磁盘中的存储位置,然后通过访问道磁盘中的数据就可以访问到了。
常见的内存管理方式有块式管理,业式管理, 段式管理, 段业式管理。
最长用的是段业式管理。
(1) 块式管理:把主存分为一块一块的,当所需的程序片段不再主存时就分配一块主存空间,把程序load入主存,就算所需的程序片段只有几个字节也只能把这一块都分给他,造成很大的浪费,但易于管理。
(2) 业式管理:把主存分为一页一页的,每一页的空间要比一块小很多,显然这种分法的空间利用率要比块式管理高很多。
(3) 段式管理:把主存分为一段一段的,每一段的空间要比一页小很多,这种方法在空间利用率上比业式管理高很多,但有另外一个缺点:一个程序片段可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上。
(4) 段业式管理:结合了段式和业式的优点。把主存先分为若干段,每个段又分成若干业。
段业式管理每取一数据要访问3次内存:
第一次是由段表地址寄存器得段表始址后访问段表,由此取出对应段的页表在内存中的地址。
第二次则是访问页表得到所要访问的物理地址。
第三次才能访问真正需要访问的物理单元
虚拟存储器别称虚拟内存。是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。
实际上是一种逻辑存储器,实质是对物理存储设备进行逻辑化的处理,并将统一的逻辑视图呈现给用户。因此,用户在使用时,操作的是虚拟设备,无需关心底层的物理环境。因而,可以充分利用基于异构平台的存储空间,达到最优化的使用效率
平衡存储器速度、容量及成本矛盾
存储器在CPU外,bai一般指硬盘,U盘等可以在切断电源后保存资料的设备,容量一般比较大,缺点是读写速度都很慢,普通的机械硬盘读写速度一般是50MB/S左右。
内存和寄存器就是为了解决存储器读写速度慢而产生的多级存储机制,从20世纪50年代开始,磁芯存储器曾一度成为主存的主要存储介质,但从20世纪70年代开始,逐步被半导体存储器所取代,目前的计算机都是用半导体存储器。现在的DDR2内存的读写速度一般为6~8GB/S,跟机器性能也有关系。
2、寄存器(又称缓存)一般是指由基本的RS触发器结构衍生出来的D触发,就是一些与非门构成的结构,一般整合在CPU内,其读写速度跟CPU的运行速度基本匹配,但因为性能优越,所以造价昂贵,一般好的CPU也就只有几MB的2级缓存,1级缓存更小。使用寄存器可以缩短至零长度、节省存储空间,提高指令的执行速度。
3、不同的寄存器有不同的作用,如:通用寄存器(GR)用以存放操作数、操作数的地址或中间结果;指令寄存器(IR)用以存放当前正在执行的指令,以便在指令执行的过程中,控制完成一条指令的全部功能。
CPU计算时,先预先把要用的数据从硬盘读到内存,然后再把即将要用的数据读到寄存器。最理想的情况就是CPU所有的数据都能从寄存器里读到,这样读写速度就快,如果寄存器里没有要用的数据,就要从内存甚至硬盘里面读,那样读写数据占的时间就比CPU运算的时间还多的多。
所以评价一款CPU的性能除了频率,缓存也是很重要的指标。
局部变量(Local Variable):定义在函数体内部的变量,作用域仅限于函数体内部。离开函数体就会无效。再调用就是出错。
**全局变量(Global Variable)*定义:所有的函数外部定义的变量,它的作用域是整个程序,也就是所有的源文件,包括.c和.h文件。
一般原则是尽量少定义全局变量,因为全局变量会占用更多的内存,而且存在时间长;当然定义也没有问题,能解决这样的问题……但是我提议你:定义在主函数中,然后用指针型的参数传到两个函数进行调用。
编译:源程序(高级/汇编)→编译器 →目标机器代码程序(二进制)
方法一:首先从头节点开始,依次遍历单链表的每一个节点。每遍历到一个新节点,就从头节点重新遍历新节点之前的所有节点,用新节点ID和此节点之前所有节点ID依次作比较。如果发现新节点之前的所有节点当中存在相同节点ID,则说明该节点被遍历过两次,链表有环;如果之前的所有节点当中不存在相同的节点,就继续遍历下一个新节点,继续重复刚才的操作。
例如这样的链表:A->B->C->D->B->C->D, 当遍历到节点D的时候,我们需要比较的是之前的节点A、B、C,不存在相同节点。这时候要遍历的下一个新节点是B,B之前的节点A、B、C、D中恰好也存在B,因此B出现了两次,判断出链表有环。
假设从链表头节点到入环点的距离是D,链表的环长是S。那么算法的时间复杂度是0+1+2+3+….+(D+S-1) = (D+S-1)(D+S)/2 , 可以简单地理解成 O(NN)。而此算法没有创建额外存储空间,空间复杂度可以简单地理解成为O(1)。
****首先创建一个以节点ID为键的HashSet集合,用来存储曾经遍历过的节点。然后同样是从头节点开始,依次遍历单链表的每一个节点。每遍历到一个新节点,就用新节点和HashSet集合当中存储的节点作比较,如果发现HashSet当中存在相同节点ID,则说明链表有环,如果HashSet当中不存在相同的节点ID,就把这个新节点ID存入HashSet,之后进入下一节点,继续重复刚才的操作。
这个方法在流程上和方法一类似,本质的区别是使用了HashSet作为额外的缓存。
假设从链表头节点到入环点的距离是D,链表的环长是S。而每一次HashSet查找元素的时间复杂度是O(1), 所以总体的时间复杂度是1*(D+S)=D+S,可以简单理解为O(N)。而算法的空间复杂度还是D+S-1,可以简单地理解成O(N)。
首先创建两个指针1和2(在java里就是两个对象引用),同时指向这个链表的头节点。然后开始一个大循环,在循环体中,让指针1每次向下移动一个节点,让指针2每次向下移动两个节点,然后比较两个指针指向的节点是否相同。如果相同,则判断出链表有环,如果不同,则继续下一次循环。
例如链表A->B->C->D->B->C->D,两个指针最初都指向节点A,进入第一轮循环,指针1移动到了节点B,指针2移动到了C。第二轮循环,指针1移动到了节点C,指针2移动到了节点B。第三轮循环,指针1移动到了节点D,指针2移动到了节点D,此时两指针指向同一节点,判断出链表有环。
此方法也可以用一个更生动的例子来形容:在一个环形跑道上,两个运动员在同一地点起跑,一个运动员速度快,一个运动员速度慢。当两人跑了一段时间,速度快的运动员必然会从速度慢的运动员身后再次追上并超过,原因很简单,因为跑道是环形的。
线性表是最基本、最简单、也是最常用的一种数据结构。线性表*(linear list)*是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表(存储层次上属于链式存储,但是把最后一个数据元素的尾指针指向了首位结点)
位运算:
int Sum1ByBin(int num)
{
int sum = 0;
while (num)
{
sum += num&1;
num>>1;
}
return sum;
}
一位一位的去判断,是1计数器+1,否则不操作,跳到下一位,十分容易,编程初学者就可以做得到!
于是很容易得到这样的程序:
int Sum1ByBin(int num)
{
int sum = 0;
while (num)
{
if(sum %2 ==1)
{
sum ++;
}
sum/=2;
}
return sum;
}
每次找到从最低位开始遇到的第一个1,计数器加1然后把它清零,然后继续找下一个1。方法就是n&(n-1),这个操作对比当前位高的位没有任何影响,
对低位完全清零。举个栗子吧!5。 5是101,第一次运算101&100 == 100,并且计数器加一,第二次运算100&011 == 0,计数器加一。循环结束啦!
所以5有2个1
int Sum1byBin(int num)
{
int sum = 0;
while(num)
{
num &= num-1;
sum++;
}
return 0;
}
用斐波那契数列来说明递归和迭代的区别
递归:自己调用自己
迭代:反复替换的意思
递归(recursion):递归常被用来描述以自相似方法重复事物的过程,在数学和计算机科学中,指的是在函数定义中使用函数自身的方法。(A调用A)
迭代(iteration):重复反馈过程的活动,每一次迭代的结果会作为下一次迭代的初始值。(A重复调用B)
递归是一个树结构,从字面可以其理解为重复“递推”和“回归”的过程,当“递推”到达底部时就会开始“回归”,其过程相当于树的深度优先遍历。
迭代是一个环结构,从初始状态开始,每次迭代都遍历这个环,并更新状态,多次迭代直到到达结束状态。
理论上递归和迭代时间复杂度方面是一样的,但实际应用中(函数调用和函数调用堆栈的开销)递归比迭代效率要低。
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)
1、第一范式(1NF):
所谓第一范式(1NF)是指在关系模型中,对于添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分。简而言之,第一范式就是无重复的域。
说明:在任何一个关系数据库中,第一范式(1NF)是对关系模式的设计基本要求,一般设计中都必须满足第一范式(1NF)。不过有些关系模型中突破了1NF的限制,这种称为非1NF的关系模型。换句话说,是否必须满足1NF的最低要求,主要依赖于所使用的关系模型。
2、第二范式(2NF)
在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。例如在员工表中的身份证号码即可实现每个一员工的区分,该身份证号码即为候选键,任何一个候选键都可以被选作主键。在找不到候选键时,可额外增加属性以实现区分,如果在员工关系中,没有对其身份证号进行存储,而姓名可能会在数据库运行的某个时间重复,无法区分出实体时,设计辟如ID等不重复的编号以实现区分,被添加的编号或ID选作主键。(该主键的添加是在ER设计时添加,不是建库时随意添加)
第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。
3、第三范式(3NF)
在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)
第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性,也就是在满足2NF的基础上,任何非主属性不得传递依赖于主属性。
4、巴斯-科德范式(BCNF)
Boyce-Codd Normal Form(巴斯-科德范式)
在3NF基础上,任何非主属性不能对主键子集依赖(在3NF基础上消除对主码子集的依赖)
巴斯-科德范式(BCNF)是第三范式(3NF)的一个子集,即满足巴斯-科德范式(BCNF)必须满足第三范式(3NF)。通常情况下,巴斯-科德范式被认为没有新的设计规范加入,只是对第二范式与第三范式中设计规范要求更强,因而被认为是修正第三范式,也就是说,它事实上是对第三范式的修正,使数据库冗余度更小。这也是BCNF不被称为第四范式的原因。某些书上,根据范式要求的递增性将其称之为第四范式是不规范,也是更让人不容易理解的地方。而真正的第四范式,则是在设计规范中添加了对多值及依赖的要求。
数据挖掘的应用bai非常广泛,只要该产业有分析价值与需求的数据库,皆可利用数据挖掘工具进行有目的的发掘分析。常见的应用案例多发生在零售业、制造业、财务金融保险、通讯及医疗服务:
(1)商场从顾客购买商品中发现一定的关联规则,提供打折、购物券等促销手段,提高销售额;
(2)保险公司通过数据挖掘建立预测模型,辨别出可能的欺诈行为,避免道德风险,减少成本,提高利润;
(3)在制造业中,半导体的生产和测试中都产生大量的数据,就必须对这些数据进行分析,找出存在的问题,提高质量;
(4)电子商务的作用越来越大,可以用数据挖掘对网站进行分析,识别用户的行为模式,保留客户,提供个性化服务,优化网站设计;
锁的类型有三种:
共享(S)锁:多个事务可封锁一个共享页;任何事务都不能修改该页; 通常是该页被读取完毕,S锁立即被释放。
排它(X)锁:仅允许一个事务封锁此页;其他任何事务必须等到X锁被释放才能对该页进行访问;X锁一直到事务结束才能被释放。
更新(U)锁:用来预定要对此页施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的页将要被更新时,则升级为X锁;U锁一直到事务结束时才能被释放。
主码 ,外码也可以称为主键,外键。
什么是主码,主码是一个能唯一标识一个元组的属性。在一个关系(表)中,主码不一定只有一个,但是一定要有的。因为元组,是对世界中某种事物的数据描述,而世界中各种事物,都具有唯一性,都是可以区分的。在计算机中为了描述事物的唯一性,引入了主码的概念。在一个关系中,不同元组的主码一定不能为空,且值一定要不同。因为如果为空说明这个事物是不可标识的,而这个世界的任何事物都是可以标识的,说以主码为空显然不合法;如果两个主码值相同则说明两个事物是相同的,而这个世界的任何事物都是不同的,所以两个主码值相同,显然也是不合法的。
什么是外码,如果一个关系中的一个属性是另外一个关系中的主码则这个属性为外码。外码的值要嘛为空,要嘛要为其对应的主码中的一个值。
**数据库事务(简称:事务)**是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操bai作序列构成。
一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:
为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
mysql、oracle、sqlserver、sqlite、postgreSQL等
主成分分析是把各变量之间互相关联的复杂关系进行简化分析的方法。
第一框拿出1颗苹果,第二框拿出2颗苹果,第三框拿出3颗苹果,以此类推,第九框拿出9颗苹果.
若总重量少1钱的,那就是第一框4两9;
总重量少2钱的,那就是第二框4两9;
以此类推
总重量少9钱的,那就是第九框4两9;
总重量正好的,那就是第十框4两9.
数据结构分为数据的逻辑bai结构、数据的物理结构、数据的存储结构
1、数据的逻辑结构,指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关。
2、数据的物理结构,指数据的逻辑结构在计算机存储空间的存放形式。数据的物理结构是数据结构在计算机中的表示(又称映像),它包括数据元素的机内表示和关系的机内表示。由于具体实现的方法有顺序、链接、索引、散列等多种,所以,一种数据结构可表示成一种或多种存储结构。
3、数据存储结构,在计算机存储空问中的存放形式称为数据的物理结构(也称为存储结构)。一般来说,一种数据结构的逻辑结构根据需要可以表示成多种存储结构,常用的存储结构有顺序存储、链式存储、索引存储和哈希存储等。
机器学习是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。
它是人工智能核心,是使计算机具有智能的根本途径。
面向对象的三个基本特征是:封装、继承、多态。其中,封装 可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!
封装、继承和多态是面向对象程序设计的三个核心特性。
一、封装
二、继承
继承是一种由已有类创建新类的机制。利用继承,我们可以先创建一个共有属性的一般类,根据该一般类再创建具有特殊属性的新类。新类继承一般类的状态和行为,并根据需要增加它自己的状态和行为。
从现有类出发定义一个新类,称为新类继承了现有的类,其中被继承的现有类叫做超类(superclass)或父类,由继承而得到的类称为子类(subclass)。
Java中规定,一个父类可以同时拥有多个子类,但一个子类只能有一个父类,即单重继承,而且允许多层继承,即子类还可以有它自己的子类,在下一层的继承关系中原先的子类就变成了父类。这样的继承关系就形成了继承树。
三、什么是多态?
“一个接口,多种方法”,同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
多态的三个条件?
a. 继承的存在(继承是多态的基础,没有继承就没有多态).
b. 子类重写父类的方法(多态下调用子类重写的方法).
c. 父类引用变量指向子类对象(子类到父类的类型转换).
重载(overload)和重写(override)是实现多态的两种主要方式。
实现多态?
接口多态性
继承多态性
通过抽象类实现的多态性
思路1:双指针
第一步,找环中相汇点。分别用p1,p2指向链表头部,
p1每次走一步,p2每次走二步,直到p1==p2找到在环中的相汇点。
那么我们可以知道fast指针走过a+b+c+b
slow指针走过a+b
那么2*(a+b) = a+b+c+b
所以a = c
那么此时让slow回到起点,fast依然停在z,两个同时开始走,一次走一步
那么它们最终会相遇在y点,正是环的起始点
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead==null || pHead.next==null){
return null;
}
ListNode fast=pHead;
ListNode slow=pHead;
while(fast!=null && fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
fast=pHead;
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}
return slow;
}
}
return null;
}
思路2:哈希法
遍历单链表的每个结点
如果当前结点地址没有出现在set中,则存入set中
否则,出现在set中,则当前结点就是环的入口结点
整个单链表遍历完,若没出现在set中,则不存在环
public ListNode EntryNodeOfLoop(ListNode pHead) {
if(pHead==null){
return null;
}
HashSet set=new HashSet();
ListNode cur=pHead;
while(cur!=null){
if(set.contains(cur)){
return cur;
}
else{
set.add(cur);
}
cur=cur.next;
}
return null;
}
1、什么是优先级反转?
优先级反转是指一个低优先级的任务持有一个被高优先级任务所需要的共享资源。高优先任务由于因资源缺乏而处于受阻状态,一直等到低优先级任务释放资源为止。而低优先级获得的CPU时间少,如果此时有优先级处于两者之间的任务,并且不需要那个共享资源,则该中优先级的任务反而超过这两个任务而获得CPU时间。如果高优先级等待资源时不是阻塞等待,而是忙循环,则可能永远无法获得资源,因为此时低优先级进程无法与高优先级进程争夺CPU时间,从而无法执行,进而无法释放资源,造成的后果就是高优先级任务无法获得资源而继续推进。
2、解决方案:
(1)设置优先级上限,给临界区一个高优先级,进入临界区的进程都将获得这个高优先级,如果其他试图进入临界区的进程的优先级都低于这个高优先级,那么优先级反转就不会发生。
(2)优先级继承,当一个高优先级进程等待一个低优先级进程持有的资源时,低优先级进程将暂时获得高优先级进程的优先级别,在释放共享资源后,低优先级进程回到原来的优先级别。嵌入式系统VxWorks就是采用这种策略。
这里还有一个八卦,1997年的美国的火星探测器(使用的就是vxworks)就遇到一个优先级反转问题引起的故障。简单说下,火星探测器有一个信息总线,有一个高优先级的总线任务负责总线数据的存取,访问总线都需要通过一个互斥锁(共享资源出现了);还有一个低优先级的,运行不是很频繁的气象搜集任务,它需要对总线写数据,也就同样需要访问互斥锁;最后还有一个中优先级的通信任务,它的运行时间比较长。平常这个系统运行毫无问题,但是有一天,在气象任务获得互斥锁往总线写数据的时候,一个中断发生导致通信任务被调度就绪,通信任务抢占了低优先级的气象任务,而无巧不成书的是,此时高优先级的总线任务正在等待气象任务写完数据归还互斥锁,但是由于通信任务抢占了CPU并且运行时间比较长,导致气象任务得不到CPU时间也无法释放互斥锁,本来是高优先级的总线任务也无法执行,总线任务无法及时执行的后果被探路者认为是一个严重错误,最后就是整个系统被重启。Vxworks允许优先级继承,然而遗憾的工程师们将这个选项关闭了。
(3)第三种方法就是使用中断禁止,通过禁止中断来保护临界区,采用此种策略的系统只有两种优先级:可抢占优先级和中断禁止优先级。前者为一般进程运行时的优先级,后者为运行于临界区的优先级。火星探路者正是由于在临界区中运行的气象任务被中断发生的通信任务所抢占才导致故障,如果有临界区的禁止中断保护,此一问题也不会发生。
是为了用bai多项式逼近原函数。
高数里zhi面求极限很多都可以用泰勒展开
物理zhuan里面分析求解的时候可以泰勒展开省略掉无穷小量,分析主要影响因素
数值计算的时候也可以用泰勒展开来做近似计算
数学这东西是门工具学科,你先学着,到时候要用的时候就知道了。就我的经验来看,数学里面很多公式书上都会讲有什么用,但是我们一般都看不懂,只有学习别的学科开始用的时候才知道以前学的有什么用。
为什么要有泰勒展开式?对计算机学科的意义是什么?
可以使用泰勒展开式的无限个多项式对某个函数求近似,计算机科学中在openCV中进行模拟曲线可以用的到
线性代数中坐标变化的意义是什么?能解决什么问题?
线性代数可以表示图像中的数值,可以对矩阵进行变化,例如对图像进行二值化,灰度化等。
sin0.1=sin[(180º/π)*0.1]≈sin5.73º≈0.0998334
可以根据极大似然函数可以求得极大似然估计值,即用已知的总体和样本结果,反求样本最有可能发生的概率。
什么是数据挖掘?
数据挖掘就是从海量的信息中利用有效的算法提去需要的信息。
应用:就我们生活中熟知的网上购物,用户可以根据以往其他用户大量评价,来计算机决定该物品是否值得购买~
展开到直到抵消不了为止
比如一张图,有不规则的图形,可以用散点法,也就是蒙特卡洛,比如在上面撒芝麻,看看有多少芝麻在图上,然后找比例求面积
特征值是指设bai A 是n阶方阵du,如果存在数m和非零zhin维列向量 x,使得 Ax=mx 成立,则dao称 m 是A的一zhuan个特征值(characteristic value)或本shu征值(eigenvalue)。
非零n维列向量x称为矩阵A的属于(对应于)特征值m的特征向量或本征向量,简称A的特征向量或A的本征向量。
双聚类算法:
光谱联合聚类:
在矩阵中,找到的值高于其它行和列中的值,每行和每列只属于一个双聚类,因此重新排列行和列中的这些高值,使这些分区沿着矩阵对角线连续显示。
启发式搜索:
利用当前与问题有关的信息作为启发式信息,这些信息是能够提升查找效率以及减少查找次数的。
如何利用这些信息?
定义一个估价函数h(x)。h(x)是对当前状态x的一个估计,表示x状态到目标状态的距离。
1.h(x)>=0
2.h(x)越小表示越接近目标状态;
3.如果h(x) == 0,说明达到目标状态
k均值聚类:
K均值聚类算法:是一种迭代求解的聚类分析算法,其步骤是,欲将数据分为k组,则随机选取K个对象作为初始聚类的中心,然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。重复以上步骤,直到
①没有对象被重新分配给不同的聚类
②没有聚类中心在发生变化
③误差平方和局部最小
将蚁群算法应用于解决优化问题的基本思路为:用蚂蚁的行走路径表示待优化问题的可行解,整个蚂蚁群体的所有路径构成待优化问题的解空间。路径较短的蚂蚁释放的信息素量较多,随着时间的推进,较短的路径上累积的信息素浓度逐渐增高,选择该路径的蚂蚁个数也愈来愈多。最终,整个蚂蚁会在正反馈的作用下集中到最佳的路径上,此时对应的便是待优化问题的最优解。
答:用正反馈机制寻求最优解,这个最优解是全局最优解,可以和运筹学结合起来用,比如运筹学里的指派问题、旅行商问题。
把装8斤桶油的桶bai倒如5斤的空油瓶du,然后把zhi5斤油瓶里的油倒入dao3斤油到zhuan能装3斤油的空油瓶里,那个shu5斤油瓶里就剩2斤油了,再把3斤油瓶里的油倒入到8斤油桶里,这样8斤油桶里就有6斤油了。
然后把5斤油瓶里的那2斤油倒入能装3斤油的油瓶里,把8斤油瓶里的6斤油倒5斤到能装5斤油的油瓶里,在把5斤油瓶里的油倒1斤到能装3斤油的油瓶里,这样5斤油瓶里就剩4斤油了。
一、指代不同bai
1、路由器du:是连接两个或多个zhi网络的dao硬件设备,在网络间起zhuan网关的作用,shu是读取每一个数据包中的地址然后决定如何传送的专用智能性的网络设备。
2、交换机:是一种用于电(光)信号转发的网络设备。
二、功能不同
1、路由器:最主要的功能可以理解为实现信息的转送。把这个过程称之为寻址过程。因为在路由器处在不同网络之间,但并不一定是信息的最终接收地址。所以在路由器中, 通常存在着一张路由表。
2、交换机:交换机有带宽很高的内部交换矩阵和背部总线,并且这个背部总线上挂接了所有的端口,通过内部交换矩阵,就能够把数据包直接而迅速地传送到目的节点而非所有节点, 这样就不会浪费网络资源,从而产生非常高的效率
三、特点不同
1、路由器:核心是背板,高效率的背板有助于提高路由器的性能。由于传统的共享总线式背板无法满足路由器的需要,所以采用结构可以用不同技术实现的交换式背板。
2、交换机:交换机在同一时刻可进行多个端口对之间的数据传输。每一端口都可视为独立的物理网段(注:非IP网段),连接在其上的网络设备独自享有全部的带宽,无须同其他设备竞争使用。
什么是拥塞控制
网络拥塞现象是指到达通信网络中某一部分的分组数量过多,使得该部分网络来不及处理,以致引起这部分乃至整个网络性能下降的现象,严重时甚至会导致网络通信业务陷入停顿,即出现死锁现象。拥塞控制是处理网络拥塞现象的一种机制。
拥塞控制是一种用来调整传输控制协议(TCP)连接上单次发送的分组数量的算法,通过增减单次发送量逐步调整,使之逼近当前网络的承载量。如果单次发送量为1,此协议就退化为停等协议。单次发送量是以字节来做单位的;但是如果假设TCP每次传输都是按照最大报文段(MSS)来发送数据的,那么也可以把数据包个数当作单次发送量的单位,所以有时我们说单次发送量增加1也就是增加相当于1个最大报文段的字节数。
拥塞控制的算法
拥塞控制假设分组的丢失都是由网络繁忙造成的。拥塞控制有三种动作,分别对应到源主机感受到的情况:
收到一条新确认。表明当前的单次发送量小于网络的承载量。此时可以增加单次发送量。若当前单次发送量小于慢启动阈值(ssthreash),则单次发送量加倍(乘以2),即指数增长;否则单次发送量加1,即线性增长。
收到三条对同一分组的确认,即三条重复的确认。说明网络有一点儿繁忙。此时单次发送量减半,慢启动阈值(ssthreash)约等于单次发送量,进入线性增长阶段。
对某一个分组的确认迟迟未到,即超时。说明网络比上一情况中的更加繁忙。此时慢启动阈值=单次发送量÷2,单次发送量=1,进入慢启动阶段(指数增长阶段)。
拥塞控制的四个阶段
慢启动
慢启动,是传输控制协议(TCP)使用的一种阻塞控制机制。慢启动也叫做指数增长期。 慢启动算法通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。
慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestion window),记为cwnd。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。算法描述如下:
当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小)。
每收到一个ACK,拥塞窗口就翻倍(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。这是一种指数增长的关系。
发送方取拥塞窗口与通告窗口中的最小值作为发送上限。
拥塞避免
慢启动算法是在一个连接上发起数据流的方法,其指数级增长很快就会使网络出现拥塞现象,因为某些点上可能达到了互联网的容量,于是中间路由器开始丢弃分组。拥塞避免算法是一种处理丢失分组的方法。有两种分组丢失的指示:发生超时和接收到重复的确认。发生超时,指源主机在超时定时器溢出时没有收到目的主机对某一分组的ACK;接收到重复确认,指在源主机的超时定时器溢出前,连续收到3个或3个以上收对某一分组的ACK。
当发现超时或接收到3次重复确认时,则表示有丢包事件,此时网络已发生拥塞现象,要进行相应的拥塞控制。算法描述如下:
将慢启动阈值(ssthreash)设置为当前窗口的一半(cwnd 和通告窗口大小的最小值,但最小为2个报文)。
如果是超时引起的拥塞,则拥塞窗口(cwmd)被置为1,进入慢启动过程。如果是重复确认引起的拥塞,则进入快速重传和快速恢复过程。
进入慢启动阶段后,拥塞窗口会指数级增长,如果拥塞窗口大于慢启动阈值(ssthreash),执行拥塞避免算法。执行拥塞避免算法时,由于慢启动阈值(ssthreash)已经存在,拥塞窗口大小不再翻倍增长,而是线性增加。
拥塞避免算法和慢启动算法是两个目的不同、独立的算法。但是当拥塞发生时,我们希望降低分组进入网络的传输速率,于是可以调用慢启动来作到这一点。在实际中这两个算法通常在一起实现。1990年出现的TCPReno版本增加了 “快速重传”算法、”快速恢复”算法,避免了当网络拥塞不够严重时采用”慢启动”算法而造成过大地减小发送窗口尺寸的现象。
快速重传
目的主机在收到一个失序的报文段时,会立即产生一个ACK(重复的ACK),这个重复的ACK不应该被延迟,目的在于让源主机知道目的主机收到了一个失序的报文段,并告诉源主机自己希望收到的序号。
由于我们不知道一个重复的ACK是由一个丢失的报文段引起的,还是由于仅仅出现了几个报文段的重新排序,因此我们等待少量重复的ACK到来。假如这只是一些报文段的重新排序,则在重新排序的报文段被处理并产生一个新的ACK之前,只可能产生1到2个重复的ACK。如果一连串收到3个或3个以上的重复ACK,就非常可能是一个报文段丢失了,进入快速重传过程,描述如下:
将慢启动阈值(ssthreash)设置为当前拥塞窗口(cwnd)的一半,设置拥塞窗口(cwnd)为慢启动阈值(ssthreash)加上3倍的报文段大小。重传丢失的数据报文段,而无需等待超时定时器溢出。
每次收到另一个重复的ACK时,cwnd增加一个报文段大小并发送1个分组(如果新的cwnd允许发送)。收到另一个重复的ACK,说明网络中传输的一个分组到达了目的主机,网络中可再容纳一个分组,故cwnd增加一个报文段大小并发送一个分组。
快速恢复
丢失的分组通过快速重传过程发送完,并被目的主机接受后,目的主机就不再发送重复的ACK通知源主机发送丢失的分组了,而是发送确认新数据的ACK通知源主机发送新的分组。这个ACK应该是在进行重传后的一个往返时间内对重传分组的确认,也应该是对丢失的分组和收到的第1个重复的ACK之间的所有中间报文段的确认。此时为了快速的恢复到较高的传输速度,就会进入快速恢复阶段,算法描述如下:
当确认新数据的ACK到达时,设置拥塞窗口(cwnd)为慢启动阈值(ssthreash)(快速重传时步骤1中设置的值),进入拥塞避免过程。
在谢希仁著《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。在另一部经典的《计算机网络》一书中讲“三次握手”的目的是为了解决“网络中存在延迟的重复分组”的问题。这两种不用的表述其实阐明的是同一个问题。
** 为什么不能是两次握手**
因为对于客户端和服务端来说,双方对对方的序列号的确认是可靠传输的关键。
两次握手的过程如下:
当第二步的动作完成时,我们可以保证B已知晓A的序列号,因为第二步只有在第一步成功后才执行。但不能保证A知晓B的序列号,因为第二步的传输可能失败。
图中省略了这两次握手中,发送了SYN=1的细节
两次握手完成后,由于我们假设了,tcp只有二次握手,那么二次握手完成时,B就得自认为连接已经建立,不管第二次握手的传输是否成功。
那么,假如第二次握手的传输失败了,A就不会收到B的序列号,也就无法确定B的数据传输起始于第几号。
这时,B向A发送了一些数据(TCP是全双工通信,所以服务端B可以主动向客户端A发信息),并附上了序列号20000,被A收到:
此时A就面临着两个尴尬的选择:
所以,不管哪个选择,都是不妥当的。这正是因为A没有确认B的序列号。
而在tcp中,通过三次握手,和丢包的处理机制,A和B都会确定自己的序列号被对方接收。
为什么不能是四次握手
把四次握手中的第二和第三步合并起来,就是三次握手了。为了提高效率,是可以合并第2、3步的。
为什么需要系统调用?主要有以下两方面原因。
(1)系统调用可以为用户空间提供访问硬件资源的统一接口,以至于应用程序不必去关注具体的硬件访问操作。比如,读写文件时,应用程序不用去管磁盘类型,甚至于不用关心是哪种文件系统。
(2)系统调用可以对系统进行保护,保证系统的稳定和安全。系统调用的存在规定了用户进程进入内核的具体方式,换句话说,用户访问内核的路径是事先规定好的,只能从规定位置进入内核,而不准许肆意跳入内核。有了这样的进入内核的统一访问路径限制才能保证内核的安全。
讲过
讲过
进程是系统进行分配资源和内存管理的基本单位,是一段程序在某一个数据集上面的一次运算,运行时,从磁盘中加载到内存。
线程是系统调度的最小单位,是进程的一部分。
区别:一个进程包括多个线程,线程是进程执行的每一一个小任务,进程切换时需要切换资源和内存,而线程切换时不需要切换资源和内存,只需要切换pc指针。
讲过
根据操作系统所使用的存储技术的不同,所使用的数据结构表也不不同?
在固定分区存储管理中,内存分配表
在可变分区存储管理中,使用已分配区表和未分配区表
在分页存储管理中,使用内存物理块表用来记录页框的状态,管理内存物理块分布
在段式存储管理中,使用段表管理
系统调用fork(),vfork() ,clone()
讲过
堆,栈,自由存储区,静态/全局变量存储区,常量存储区
CPU从外部中断源接收到中断后,CPU在满足中断的条件下,发送中断响应,并关中断不再响应其他中断。CPU寻找中断源是哪个设备,找到后,保存当前CPU的状态,以及各种寄存器的状态,将PC转移到中断处理程序的地址,完成中断处理程序之后,恢复现场,打开中断,继续开始的中断点执行。
DHCP(动态主机配置协议)作用:动态分配IP
从我的理解:
首先客户端问附近的服务器有ip地址码
服务器回答,说有啊,服务端就发一一个ip过去
客户端就申请使用这个ip
服务端就确认
简单介绍下DHCP
A 客户端要IP,发送第一个报文discover
B 发送一个offer回应它,可以提供IP
A 就发送一个request请求报文
B 服务器收到后,如果可以,就发送一个ACK确认
当在网站上输入了地址后,浏览器便会向本地域名服务器请求解析,若本地域名服务器没有,则本地域名服务器以客户的方式向根域名服务器发送请求报文解析请求,根域名判断在DNS.abc的域,将权限域名服务器的地址发送给本地域名服务器,本地域名器向权限域名服务器发送连接请求,权限域名服务器找到后,将域名的IP地址发送给本地域名服务器,本地域名服务器收到后,保存下来,并将IP发送给主机,主机便于IP地址所在的从机建立TCP连接请求,经过三次握手,主机便下载到了html,并以图形化的方式呈现给用户一个界面
TCP:传输控制协议,就是为了保证互联网通信的双方能够可靠传输,所以TCP协议就制定了可靠的传输协议,想慢开始,快回复,拥塞控制,快重传
和客户交流,确定好需求分析
系统设计:程序设计+前端UI+数据库设计
3种,static,递归,循环
8个字节,char 定义了一个字节后,再定义一个int 类型,int的为4个字节,要存储再4的倍数的存储单元种,所以编号为1,2,3的字节要填充,int定义的从4开始
1 数据结构中线性表有哪些?
线性表,栈,队列
2 还有快排和冒泡的原理自己他们的空间复杂度是多少
快排㏒2n,冒泡 O(1)
C++方面:
1 几种方法写阶乘;
3种,static,递归,循环
2 定义一个结构体,里面一个int类型成员,一个char,int占四位,char占一位,那么在Linux占几位
8个字节,char 定义了一个字节后,再定义一个int 类型,int的为4个字节,要存储再4的倍数的存储单元种,所以编号为1,2,3的字节要填充,int定义的从4开始。
3 请设计算法统计一个二进制数中1的个数
两种方法:
第一种是:设置一个变量count,将二进制放入string变量中,遍历string变量的每一个字符,当这个字符等于1是,conut++
第二种方法是:C++中有一个bitset,直接用bitset定义的二进制变量调用count就可以得出1的个数
4 递归的含义,递归能不能替代循环,递归需要做什么?
递归其实就是一个函数直接调用或间接调用自己过程。
递归可以替代循环,递归需要压栈,存放下一条指令的地址以及函数的参数
递归可以改写成循环,有些递归只需要一个循环就可以实现,有些递归需要循环+栈,需要辅助空间记录过程中的,某些数据才可以。
5 堆栈需要用的什么
bp,ss
6 printf函数在汇编中怎么实现
在汇编中,先把printf的参数,从右往左压入栈中,再根据栈中,格式,计算对应格式所占的空间,再调用system_call ,来实现输出
7 printf函数的参数格式,长度等等
%d %i %u %f %ld %lf c%G c %X %x %s
8 描述斐波那契序列算法和
算法①:
使用递归,当n<=1返回n
当n>=2时 f(n) = f(n-1)+f(n-2)
调用f(n)
if n==1 return faci(1);
else f(n) = f(n-1)+f(n-2)
算法②:
使用迭代的方法:
循环 1 到 n
{
当前值 = f(i-1) + fi;
f(i-1) = fi;
f(i) = 当前值;
}
9 汉诺塔算法
算法:有三个柱子ABC,n个盘子在A柱子上
将A上面n-1个盘子借助于C,移动到B,
将A上最下面的一个盘子移动到C
再将B上的n-1个盘子借助于A移动到C(此时问题的规模就变小了,但还是同样的问题规模)
10 C语言中你习惯定义全局变量还是局部变量
一般的话,为了方便管理,我会尽量为了模块编程,尽量使各个模块低耦合,定义局部变量使用
如果实在是
11 编译的过程是什么
词法分析, 语法分析, 语义分析及中间代码生成,优化,目标代码生成
12 专业学了啥
编程语言学了:C,C++,java,php,C#
计算机基础课学了:数据结构,操作系统,数据库,计算机组成原理,数据库,计算机网络
应用型课程学了web前端,web服务端,andriod开发,linux 0.11内核注释
硬件学了一点:数字逻辑,模拟电路与电子设计,单片机
自己看过嵌入式系统原理,传感网原理与技术,proteus,DXP,
13 嵌入式课程教了啥
大学没有嵌入式课程
但是自己看过相关的书,什么是嵌入式,家里的冰箱,微波炉点,计算机系统嵌入
14 斐波那契递归和迭代区别是啥
递归:自己调用自己,迭代:反复替换
递归和迭代都是有重复,只是重复的不同,迭代显式使用重复结构,而递归通过重复调用函数实现。
递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构
递归在遇到基本情况停止,迭代在循环条件失效时停止
时间复杂度,用递归来求,时间复杂度O(2n),迭代时间复杂度O(n)
15 递归的定义及优缺点
16 C 语言的 3个基本结构是啥
选择结构,循环结构,顺序结构,
17 全局变量和局部变量分别存储在哪里
堆,栈,自由存储区,静态/全局变量存储区,常量存储区
18 数组和指针的区别是什么
很多区别:
声明一个数组,编译器将根据数组的大小为他分配内存空间
声明一个指针,编译器只为指针本身保存内存空间
另一方面:如果声明一个数组int a[]; 和声明一个指针 int *b;
表达式b++可通过编译,而a++无法通过编译,因为a是一个常量。
19 c++的继承方式
三种继承方式:
public:父类成员在子类中均可使用
protected:父类的公有成员变为保护成员,其他成员保持不变
private:父类所有成员在子类中变为私有成员
20 .请简述C,C++语言有何区别和联系
C先出现,C++是在C上扩充,C是C++的子集,C++是C的超集
C是面向过程,C++是面向对象
C++在C的基础上,增加了很多关键字,比如bool ,const,protected等等
malloc和free替换成了new和delete
C不支持相同参数个数的重载,而c++中支持相同参数个数的重载。
21 .简述面向对象有哪些好处
优点:易于维护,复用,扩展,由于,面向对象有封装,继承,多态的特性,可以设计出低耦合的系统,使系统易于维护,更加灵活。
缺点:性能比比面向过程低
22 .简述类和对象
对象是对客观事物的抽象,类是对对象的抽象,类是一种抽象的数据类型。
它们的关系是,对象是类的实例,类是对象
23 .什么是重载
重载:就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或方法。
重载的作用:
不用为了对不同的参数类型或参数个数,而写多个函数。多个函数用同一个名字,但参数列表,即参数的个数或数据类型可以不同,调用的时候,虽然方法名字相同,但是可以根据参数自动调用相应的函数
24 .C++里什么是friend?
用friend可以声明一个友元函数或者友元类,通过友元函数或或者友元类可以访问一个分装了类中的成员,尽管,friend破坏了类的封装性,但是为了数据共享,提高程序的效率和可读性,这种破坏也是很有必要的。
25 Java和c++的异同
相同点:都是面向对象的思想,都有封装,继承,多态等特性
不同点:
1.java为解释性语言,程序源代码经过java编译器编译成字节码,然后又JVM解释成机器指令,然后执行。
C/C++为编译性语言,源代码经过编译,汇编,链接后生成可执行的二进制代码,可直接执行。
因此java的执行速度比C/C++慢,但java能够跨平台执行,C/C++不同。
2 . java是纯面向对象语言,除了基本数据类型外,其他类型都是类,而C++兼容面向对象和面对过程,可以定义全局变量和全局函数,而java没有
3.java不支持C++中的多继承,但java引入了接口的概念。
4 java不支持运算符重载,而C++语言支持运算符重载。
5 .java中没有指针的概念,C++有指针的概念
26 Java与c++的类、继承有什么相同和不同
重载:C++提供了运算符重载,而java并不提供
继承:C++支持多重继承,这是C++的一个特征,他允许多父类派生一个类。java只能单继承,但是java通过可以实现多接口,来变相实现了多继承。
27 面向对象中什么封装,多态,继承
封装:隐藏对象属性和实现的细节,仅对外公开接口,控制在程序中属性得读和修改得访问级别,将抽象得到的数据和行为相结合形成一个有机得整体类
多态:用同一相同的指令调用不同的方法,这样的称之为多态,需要使用到virtual关键字,使得虚函数在运行时动态绑定。
继承:继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法,对象的一个新类可以从现有的类中派生,这个过程称为类的继承。新类称为原始类的派生类,而原始类称为新类的基类或者父类。
1 数据库有几种锁
加锁是实现数据库并发控制的一个非常重要的技术。
为什么要上锁?
当事务在对某个数据对象进行操作前,先向系统发出请求,对其进行加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其它的事务就不能对此数据对象进行更新操作。
数据库中有两种类型的锁:
排它锁和共享锁。
共享锁的数据对象可以被其他事务读取,但不能修改
排它锁:其他的事务不能对它读取和修改。
2 数据库的事务
什么是数据库事务?
数据库是指操作多个数据项的一组数据库操作序列,这组序列要么全部执行,要么全部失败,是一个不可分割的工作单位。
有四个特性:ACID
a:原子性
c:一致性
i:隔离性
d:持久性
3 常用的数据库有哪些?
MySql,SQL server,Ocacle,,Access,redis,HBase
4 数据库的主码外码?
主码和外码是用来实现参照完整性的,外码的数据项需要参照主码的数据项来操作,具体实现是创建数据库中时表间创建关系
1.参照关系比如学生表的学号时学生表的主键,是成绩表的外键,成绩表的学号参照学生表的学号录入的,也就是,如果学生表没有的学号,成绩表是无法录入的
2.级联操作,当删除主表里面的学号时,从表里面的那个学号会响应的自动删除,修改。外码参照主码修改
5 数据库范式的定义以及区别
第一范式:表中的每一个属性都是一个原子,不可再分。
第二范式:(无重复的行)在满足第一范式的基础上,每一个元组必须可以被唯一地区分,非主属性完全依赖于主键
第三范式:在满足第二范式的基础上 ,要求一个数据库表中不包含已在其他表中亦包含的非主关键字信息,不能存在非关键字字段对一候选关键字段的传递函数依赖。
非主属性不存在传递依赖于码和不存在部分依赖于码。
(巴斯范式):在第三范式的基础上,数据库表中如果不存在任何字段对任一候选关键字段的传递函数依赖。不存在任何字段对任意候选字段传递函数依赖
什么是递归算法
①程序调用自身的编程技巧称之为递归,通常一个过程或者一个方法在定义或说明中直接调用或间接调用自身的一种方法。通常,它把一个大型的问题层层化为相类似的并且规模较小的问题求解,递归策略只需要较少的代码实现就可描述出问题中所需要的多次重复计算,大大减少了代码量。
②递归函数函数需要设置出口,当递归到子问题不能再分解时,就返回。
什么是分治算法?
①把一个大问题划分成K个小问题,如果K个小问题还可以划分,则再把它们分别划分成K个更小的问题,直到问题规模足够小,小到可以直接求解,然后把小问题合并成原问题的解。
②分治算法要注意:
小问题小到一定规模可以求解
划分的小问题应该可以合并成原问题的解
划分的小问题应该具有最优的子结构
划分的小问题应该相互独立性
什么是贪心算法?
贪心算法:把大问题拆分成为同类的更加简单求局部最优解的问题,贪心问题求解的最终可能是整体的最优解。贪心算法不适合对所有问题进行求最优解。贪心算法在每一步上能获得局部最优解,但有时产生的不一定是最优的,所以贪心算法不要回溯。
什么是回溯算法?
回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:按照深度优先搜索的策略,从一条路往前走,能进则则进,不能进则退回来,换一条路往前走。
什么是分支限界算法?
分支限界的基本思想,就是对有约束条件的最优化问题的所有可行解空间进搜索,把全部可行的解空间不断分割为越来越小的分支,并为每一个分支计算一个界,每次分支的时候,对于不满足限界函数的分支不予考虑,从而缩小了搜索范围。
什么是动态规划问题?
动态规划基本思想是,将原问题分解相类似且规模较小的子问题,在求解过程中,记录下子问题的解,用子问题的解,一步步求出原问题的解,相比于分治算法,动态规划算法,减少了相同子问题的计算次数。