杭州天丽笔试题
1.volatile关键字的作用,举三个应用实例。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;
2.定义一个返回值和参数都是函数指针的函数
typedef void (*p)(void)
p func(p p1);
或是:void (*) (void) func( void (*) (void))
void (*func(void (*p) (void))(void);
3.内存中的大小端模式?
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
4.排序算法有哪几种?用C实现冒泡排序
void sort(int arr[],int size){
//1.外层循环控制比较的轮数
//2.内层循环控制每轮比较的下标范围
int i=0,j=0;
for(i=0;i<size-1;i++){
for(j=0;j<size-i-1;j++){
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
常用的排序算法
2.1冒泡排序
相邻位置之间元素的比较
(1)算法流程
a.比较相邻位置的元素,如果左边的比右边的大,则交换两个元素的位置
b.针对每一对相邻位置的元素都重复上一步,从第一对到最后一对比较完毕
经过这一步,最后的元素就是这组数中的最大值
c.针对所有的元素重复以上步骤,每次对越来越少的元素进行比较,直到没有
可以交换的元素为止
(2)算法评价
平均时间复杂度o(n^2),稳定,对样本的有序性比较敏感
2.2插入排序
(1)算法的流程
a.从第一个元素起,假定这个元素已经有序
b.从第二个元素起,与已经有序的元素从后向前进行比较
c.如果左边的元素大于取出的元素,则将左边的元素赋值到下一个位置上
继续与有序的元素进行比较
d.如果左边的元素小于取出的元素,则将取出的元素插入到左边元素之后
e.重复以上步骤,直到处理完所有的元素
(2)算法评价
平均时间复杂度o(n^2),稳定,对样本的有序性比较敏感,但是赋值的次数
比冒泡排序少,所以略优于冒泡排序
2.3选择排序算法
(1)算法的流程
a.从第一数起依次取出所有元素,假定取出的元素是最小值,记录下标
b.使用假定的最小元素和后续元素依次进行比较,如果后续元素中有比
假定最小元素还小的元素,则重新记录下标,后续元素成为了假定的最小数
c.直到假定的最小元素和后续所有元素比较完毕,交互最新的最小元素和
开始假定的最小元素
d.重复上述过程,直到所有元素排序完毕
(2)算法评价
平均时间复杂度o(n^2),不稳定,对样本的有序性不敏感,比较的次数的比较多
交换的次数比较少,一般情况下略优于冒泡排序
2.4快速排序
(1)算法的流程
a.选择一个中间元素作为基准值,单独存储起来
b.依次分别使用左边元素和右边元素和基准值比较,将比基准值小的元素放在左边
将比基准值大或者相等的元素放在右边,
c.重复以上过程,直到两边元素的下标重合为止,将基准值放到重合的位置上,此时
比基准值小的元素已经到了基准值左边,比基准值大的元素已经在基准值的右边
d.用递归的方式对基准值左边和右边的元素分别进行分组排序
(2)算法评价
平均时间复杂度o(NlogN)不稳定,如果每次分配都能做到均匀划分,这种情况的排序速度最快
5.ISR中断服务子程序的错误:
ISR不能有返回值;
ISR不能传递参数;
ISR应该是短而高效的,在ISR中做浮点运算是不明智的;
ISR中不应该有重入和性能上的问题,因此不应该使用printf()函数。
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}
1) ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
2) ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。
3) 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额外的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。
此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4) 与第三点一脉相承,printf经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。
6.while(1) ,for(; ;),goto死循环用哪个?为什么
一般for(;;)性能更优
for(;;)
{}
这两个;; 空语句,编译器一般会优掉的,直接进入死循环
while(1)
{}
每循环一次都要判断常量1是不是等于零,在这里while比for多做了这点事
不过从汇编的角度来说,都是一样的代码。
goto语句通常不用,主要因为它将使程序层次不清,且不易读,但在多层嵌套退出时,用goto语句则比较合理。
7.抢占式内核和非抢占式内核?
内核抢占(可抢占式内核):
即当进程位于内核空间时,有一个更高优先级的任务出现时,如果当前内核允许抢占,则可以将当前任务挂起,执行优先级更高的进程。
非抢占式内核:
高优先级的进程不能中止正在内核中运行的低优先级的进程而抢占CPU运行。进程一旦处于核心态(例如用户进程执行系统调用),则除非进程自愿放弃CPU,否则该进程将一直运行下去,直至完成或退出内核。
抢占式内核的意义:
首先,这是将Linux应用于实时系统所必需的。实时系统对响应时间有严格的限定,当一个实时进程被实时设备的硬件中断唤醒后,它应在限定的时间内被调度执行。而Linux不能满足这一要求,因为Linux的内核是不可抢占的,不能确定系统在内核中的停留时间。事实上当内核执行长的系统调用时,实时进程要等到内核中运行的进程退出内核才能被调度,由此产生的响应延迟,在如今的硬件条件下,会长达100ms级。这对于那些要求高实时响应的系统是不能接受的。而可抢占的内核不仅对Linux的实时应用至关重要,而且能解决Linux对多媒体(video, audio)等要求低延迟的应用支持不够好的缺陷。