1.static的作用:
(1)在函数体内,一个被声明为静态的变量在这一函数被调用过程中维持其值不变(该变量存放在静态变量区)。
--static局部变量只初始化一次,下一次依据上一次的结果值
(2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
--static全局变量只初始化一次,防止在其他文件单元被引用
(3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
--static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
2.引用与指针有什么区别
(1) 引用必须被初始化,指针不必。
(2) 引用初始化以后不能被改变,指针可以改变所指的对象。
(3) 不存在指向空值的引用,但是存在指向空值的指针。
3. 描述实时系统的基本特性
——在特定时间内完成特定的任务,实时性与可靠性
4.全局变量和局部变量的区别:
全局变量储存在静态区,进入main函数之前就被创建,生命周期为整个源程序;局部变量在堆栈(动态分配的在堆上),在函数被调用时才创建,生命周期为函数内。
5.什么是平衡二叉树:
左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。
6.堆栈溢出一般是由什么原因造成的:
(1)没有回收垃圾资源:存储空间垃圾太多 内存已满
(2)层次太深的递归调用:重复调用自己导致内存不足
7.常用排序的时间及空间复杂度
排序法 |
最差时间分析 |
平均时间复杂度 |
稳定度 |
空间复杂度 |
冒泡排序 |
O(n2) |
O(n2) |
稳定 |
O(1) |
快速排序 |
O(n2) |
O(n*log2n) |
不稳定 |
O(log2n)~O(n) |
选择排序 |
O(n2) |
O(n2) |
稳定 |
O(1) |
二叉树排序 |
O(n2) |
O(n*log2n) |
不一顶 |
O(n) |
插入排序 |
O(n2) |
O(n2) |
稳定 |
O(1) |
堆排序 |
O(n*log2n) |
O(n*log2n) |
不稳定 |
O(1) |
希尔排序 |
O |
O |
不稳定 |
O(1) |
二者不可兼得:以时间换空间,以空间换时间(想节省空间就要耗费时间,想节省时间必要耗费空间)
8. 各类型与零值的比较
写出floatx 与零值比较的if语句:if(x > 0.000001 && x <-0.000001)(6~7位)
写出bool flag 与“零值”比较的if 语句:
if ( flag )//表示flag为真 或 if ( !flag )//表示flag为假
写出char *p 与“零值”比较的if 语句 : if (p == NULL) 或 if (p != NULL)
写出整形变量x与零值的比较的if语句:if(x == 0) 或 if(x != 0)
9.不能做switch()的参数类型是:实型
因为switch后面只能带自动转换为整形(包括整形)的类型,比如字符型char,unsigned int等,实数型不能自动转换为整形.可以手动强转实数型(int)double,但是会导致精度的丢失。
10.局部变量能否和全局变量重名?
能。局部变量会自动屏蔽全局变量,要用全局变量,需要使用"::"
局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。
11.全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
可以。在不同的.c文件中以static形式来声明同名全局变量。
12.进程和线程的区别:
——线程是指进程内的一个执行单元,也是进程内的可调度实体.
与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
13.嵌入式系统中经常要用到无限循环:while(1){}或者for(;;)或者Loop:...gotoLoop;
14.volatile
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
(1) 并行设备的硬件寄存器(如:状态寄存器)
(2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
(3) 多线程应用中被几个任务共享的变量
15.数组和链表的区别
数组:数据顺序存储,固定大小(插入需要移动很多值)
链表:数据可以随机存储,大小可动态改变(访问需要遍历)
16.在嵌入式开发中,为什么能通过C语言去直接操作硬件:
因为有指针,指针是c语言的灵魂,它可以直接访问内存
17.链表的作用
用于内存管理,链表节点中的指针域可以将不连续的内存彼此关联起来,从而实现内存的动态管理。
18.什么变量不能用指针指向?
寄存器变量(register修饰),因为这个变量会优先存放到CPU寄存器中,而指针只能指向内存的任务区域,但不能指向寄存器。
19.头文件中ifndef/define/endif的作用:防止头文件被重复引用
20.#include<filename.h>和#include“filename.h”的区别:
<filename.h>告诉预处理器在标准系统目录下寻找文件;
“filename.h”告诉预处理器在当前目录中寻找文件,找不到则在标准目录下寻找文件。
21.const有什么用途:
(1) 定义const常量
(2)const可以修饰函数的参数、返回值、甚至函数的定义体,被const修饰的东西都受到强制保护,可以预防意外的变动。
//指向长整型的指针变量
const char *p1 = name;//指针所指向的内容不能变,指针指向的地址可变
//指向整形数的长指针
char *const p2 = name;//地址不可变,内容可变
//指向常量的长指针
const char *const name = “chen”;//地址不变,内容不变
22.内存的分配:
(1)栈区(stack)--由编译器自动分配释放,存放函数的参数值,局部变量等。其操作方式类似于数据结构中的栈。
(2)堆区(heap)--一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式类似于链表
(3)全局区(静态区)(static)--全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量在一起区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放
(4)文字常量区--常量字符串就是放在这里,程序结束后由系统释放
(5)程序代码区--存放函数体的二进制代码
23.sizeof和 strlen()的区别
(1)sizeof是运算符,计算数据所占的内存空间,strlen()是一个函数,计算字符数组的字符数。
(2)sizeof可以用类型作参数;strlen()只能用char*作参数,必须是以‘\0’结束。
(3)数组作sizeof参数不退化,传递给strlen就退化为指针了
(4)sizeof操作符的结果类型是size_t,它在头文件typedef为unsigned int类型,该类型保证能容纳实现建立的最大对象的字节大小。
24. 头文件的作用是什么:(1)通过头文件来调用库功能(2)头文件能加强类型安全检查
25. 简述数组和指针的区别:数组要么在静态存储区被创建(全局数组),要么在栈上被创建,指针可以随时指向任意类型的内存块。
26. 变量的定义和声明的区别:声明变量不分配空间,定义变量分配空间。声明告诉编译器后面的引用都按声明的格式。定义其实包含了声明的意思,同时要分配内存空间。
27. 简述什么是值传递,什么是地址传递
值传递主调函数传递给被调函数的是值拷贝,不是原值;地址传递主调函数传递给被调函数的值的地址。区别是值传递被调函数中的操作不改变主调函数的值,而地址传递则不同。
28.结构体和共用体的区别:
(1)结构体和共用体都是由多个不同数据类型成员组成,但在任何同一时刻,共用体只存放了一个被选中的成员(所有成员共用一块地址空间),而结构体的所有成员都存在(不同成员的存放地址不同)
(2)对于共用体的不同成员赋值,将会对其他成员重写,原来的值就不存在了,而结构体的不同成员赋值是互不影响的。
29.用变量a给出下面的定义:
(1)一个整型数:int a;
(2)一个指向整型数的指针:int *a;
(3)一个指向指针的指针,它指向的指针是指向一个整型数:int **a;
(4)一个有10个整型数的数组:int a[10];
(5)一个有10个指针的数组,指针都是指向整型数:int *a[10];
(6)一个指向数组的指针,该数组有10个整型数:int (*a)[10];
(7)一个指向函数的指针,该函数有一个整形参数并返回一个整型数:int (*a)(intb);
(8)一个有10个指针的数组,指针指向一个函数,函数有一个整形参数并返回一个整型数:
int (*a[10])(int b);
30.(void *)ptr和(*(void**))ptr的结果是否相同?其中ptr为同一个指针
——(void *)ptr 和(*(void**))ptr 值是相同的
31.带参宏与带参函数的区别(至少说出5点)?
32.用两个栈实现一个队列的功能?要求给出算法和思路!
设2个栈为A,B, 一开始均为空 入队:将新元素push入栈A; 出队:(1)判断栈B 是否为空;(2)如果不为空,则将栈A中所有元素依次pop 出并push到栈B;(3)将栈B 的栈顶元素pop 出;
33.在C++程序中调用被C编译器编译后的函数,为什么要加extern “C”?
C++ 语言支持函数重载,C 语言不支持函数重载。函数被C++ 编译后在库中的名字与 C 语言的不同。假设某个函数的原型为:void foo(intx,int y),该函数被c编译器编译后在库中的名字为_foo,而c++编译器则会产生像_foo_int_int之类的名字。
34..main(){int a[5]={1,2,3,4,5};int * ptr=(int*)(&a+1);printf(“%d,%d”,*(a+1),*(ptr-1));}请问输出:2,5 &a是数组指针,其类型为 int (*)[5]; 而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。 a是长度为5的int数组指针,所以要加5*sizeof(int) 所以ptr实际是a[5] 但是prt与(&a+1)类型是不一样的(这点很重要) 所以prt-1只会减去sizeof(int*)
35.一语句实现x是否为2的若干次幂的判断。
return x&(x-1)?1:0;;//返回0,则是2的若干次方
36.编写一个检查系统的大小端
intchecksys(){ union check{ int i; char c; }ret; ret.i=1; return ret.c }
37.computer”中长度为3的子串有多少个:字串是字符串中任意长度的连续字符构成的序列— —6个(com/omp/mpu/put/ute/ter)
38.用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题) ——#defineSECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
39.char* const p; char const * p const char *p 上述三个有什么区别:
(考察const就近原则)char * const p; //常量指针,p的值不可以修改
char const * p;//指向常量的指针,指向的常量值*p不可以改
const char *p; //和char const *p
--问题归结为 <type> const *p 和 <type>* const p 区别;只需要看const 后是什么类型;后者const 修饰的是*p,他是p所指向的内容为read-only; 而前者const 修饰的是p, 即指针变量p本身为 read-only;
--从内存的角度来看(本人已测试验证):
<type>*const p 变量p存放在read-only数据段
p为常量指针,p只用于读操作, 告诉编译器,p仅用做右值;
const<type> *p 变量p存放在栈区;
*p不一定为常量区,但*p只用于读操作,告诉编译器*p仅用作右值;
40.每个变量都有两个值与其相关联:
(1)变量的数据值,储存在某个内存地址中,又叫做右值(可认为是被读取的值)。(2)变量的地址值,即储存数据值的内存地址,又叫做对象的左值。
41.Internet采用哪种网络协议?该协议的主要层次结构?
TCP/IP,应用层/传输层/网络层/数据链路层/物理层
42.Internet物理地址和IP地址转换采用什么协议?
ARP (Address Resolution Protocol)(地址解析协议)
43.IP地址的编码分为哪俩部分?
IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按位与上之后才能区分哪些是网络位哪些是主机位。
44.-1,2,7,28,,126请问28和126中间那个数是什么?为什么?
规律是n^3-1(当n为偶数0,2,4)
n^3+1(当n为奇数1,3,5)——63
45.对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现? ——c用宏定义,c++用inline
46..软件测试都有那些种类?
——黑盒:针对系统功能的测试 白盒:测试函数功能,各函数接口
47.确定模块的功能和模块的接口是在软件设计的那个队段完成的?
概要设计阶段
48.交换两个变量的值,不使用第三个变量:有两种解法,一种用算术算法,一种用^(异或)
a = a + b;b = a - b;a = a - b(这种缺点就是a,b都较大时,a=a+b越界); or a= a^b;// 只能对int,char..b = a^b;a = a^b;或a ^= b ^= a;
49.c和c++中的struct有什么不同?
c和c++中struct的主要区别是c中的struct不可以含有成员函数,而c++中的struct可以。c++中struct和class的主要区别在于默认的存取权限不同,struct默认为public,而class默认为private
50.进程间通信的途径:共享存储系统 消息传递系统 管道:以文件系统为基础
51.进程死锁的原因:资源竞争及进程推进顺序非法
52.死锁的四个必要条件:互斥、请求保持、不可剥夺、环路
53.死锁的处理:鸵鸟策略、预防策略、避免策略、检测与解除死锁
54.操作系统中进程调度策略有哪几种:
——FCFS(先来先服务),优先级,时间片轮转,多级反馈
55.ISO的七层参考模型是什么?tcp/udp是属于哪一层?tcp/udp有何优缺点?
应用层 表示层 会话层 运输层 网络层 物理链路层 物理层
tcp /udp属于运输层
TCP 服务提供了数据流传输、可靠性、有效流控制、全双工操作和多路复用技术等。
与 TCP 不同, UDP 并不提供对 IP 协议的可靠机制、流控制以及错误恢复功能等。由于 UDP 比较简单, UDP 头包含很少的字节,比 TCP 负载消耗少。
tcp: 提供稳定的传输服务,有流量控制,缺点是包头大,冗余性不好
udp: 不提供稳定的服务,包头小,开销小
56.如何引用一个已经定义过的全局变量?
可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。(不要问我什么是编译期间,什么是连接期间,请自己查资料)
57.测试方法:
人工测试:个人复查,抽查和会审
机器测试:白盒测试,黑盒测试
58.下面代码里,假设在32位系统下,各sizeof计算的结果分别是多少?
int *p=NULL;
sizeof(p)的值是__________(1)
sizeof(*p)的值是__________(2)
int a[100);
sizeof(a)的值是_________(3)
sizeof(a[100])的值是__________(4)
sizeof(&a)的值是_________(5)
sizeof(&a[0])的值是________(6)
int b[100];
void fun(int b[100])
{
sizeof(b);
}
sizeof(b)的值是________(7)
答:除(3)答案为400以外,别的答案都为4。
59.下面两段代码有什么区别?什么时候需要使用代码(2)?
代码(1):
inti=10;
it j=i;
int k=i;
代码(2):
volatile int i=10;
int j=i;
int k=i;
答:代码(1):这时候编译器对代码进行优化,因为在代码(1)的两条语句中,i没有被用作左值(没有被赋值)。这时候编译器认为i的值没有发生改变,所以在第1条语句时从内存中取出i的值赋给j之后,这个值并没有被丢掉,而是在第2条语句时继续用这个值给k赋值。编译器不会生成出汇编代码重新从内存里取i的值(不会编译生成装载内存的汇编指令,比如ARM的LDM指令),这样提高了效率。但要注意:两条语句之间i没有被用作左值(没有被赋值)才行。
代码(2):volatile关键字告诉编译器i是随时可能发生变化的,每次使用它的时候必须从内存中取出i的值,因而编译器生成的汇编代码会重新从i的地址处读取数据放在k中。
代码(2)的使用时机:如果i是一个寄存器变量、表示一个端口数据或者是多个线程的共享数据,那么就容易出错,所以说volatile可以保证对特殊地址的稳定访问。
60.0x01<<2+3的值是多少?为什么?
——32。因为加号的优先级比移位运算符的优先级高。
61、定义一个函数宏,求x的平方。
#define SQR(x) ((x)*(x))
该语句有缺陷,传入++a这样的内容则函数宏失效。
62.如果在一个程序中调用了printf函数却不包含头文件,例如int main(void) {printf("\n"); },编译时会报警告:warning: incompatible implicit declaration of built-in function‘printf’。请分析错误原因。
在C语言标准中有些库函数被定义为内建函数(bulit-int-function),不需要包含相应头文件,比如printf等。个人理解为,编译器编译时,没有看到函数原型,就根据调用形式作了隐式申明。但是结果隐式声明的函数原型和库文件里的不对应,所以会报imcompatible警告。
63.定义变量时可以把相同类型的变量列在一起,而定义参数却不可以?
因为在函数原型声明时,只需要参数类型,不需要参数名称。如果一个参数类型后有多个参数,编译器只会认为只有一个参数。而不是多个参数。
64. 写两个表达式,分别取整型变量x的个位和十位:x=x%10 x=x%100/10
65.x=x+1,x+=1,x++哪个效率最高?
——x=x+1最低,因为它的执行过程如下:(1)读取右x的地址(2)x+1 (3)读取左x的地址(4)将右值传给左边的x
——x+=1其次,其执行过程如下:(1)读取右x的地址 (2)x+1 (3)将得到的值传给x(因为x的地址已经读出)
——x++效率最高,其执行过程如下:(1)读取右x的地址 (2)x自增1
66.有两个变量a,b不用判断语句找出两个数中间比较大的?
——方案一:
intmax = ((a+b)+ abs(a-b))/ 2
——方案二:
int c= a - b;
char*str[2] = {“a大”,“b大”};
c =unsigned(c) >> (sizeof(int)*8 - 1);
67.如何判断一段程序是由c编译程序还是c++编译程序编译的?
——c编译时定义了_STDC_,c++编译时定义了_cplusplus
68.#includeSQR(x) (x*x)—— SQR(b+2)=b+2*b+2
69.sizeof的使用场合:
(1)主要用途是与存储分配和I/O系统那样的例程进行通信
(2)可以看某种类型的对象在内存中所占的单元字节
(3)在动态分配一对象时,可以让系统指导要分配多少内存
(4)便于一些类型的扩充
(5)如果操作数是函数中的数组形参或函数类型形参,sizeof给出其指针大小
70. 指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
71.简述嵌入式处理器的特点
(1)实时性强,能完成多任务并且有较短的中断响应时间,从而使内部的代码和实时内核心的执行时间减少到最低限度。
(2)具有功能很强的存储区保护功能。这是由于嵌入式系统的软件结构已模块化,而为了避免在软件模块之间出现错误的交叉作用,需要设计强大的存储区保护功能,同时也有利于软件诊断。
(3)扩展性好,以能最迅速地开展出满足应用的最高性能的嵌入式微处理器。
(4)功耗低,尤其是用于便携式的无线及移动的计算和通信设备中靠电池供电的嵌入式系统更是如此,如需要功耗只有mW甚至μW级。
72.简述嵌入式系统的概念、组成及特点
概念:以应用为中心、软硬件裁剪好的计算机系统。
组成分为硬件组成和软件组成。
硬件组成:处理器、内存、总线、I/O端口、外设。
软件组成:Bootloader、OS、APP。
特点:
1.面向特定应用
1.嵌入式CPU与通用型的最大不同就是嵌入式CPU大多工作在为特定用户群设计的系统中,它通常都具有低功耗、体积小、集成度高等特点,能够把通用CPU中许多由板卡完成的任务集成在芯片内部,从而有利于嵌入式系统设计趋于小型化,移动能力大大增强,跟网络的耦合也越来越紧密。
1.2.技术含量高。嵌入式系统是将先进的计算机技术、半导体技术和电子技术与各个行业的具体应用相结合后的产物。这一点就决定了它必然是一个技术密集、资金密集、高度分散、不断创新的知识集成系统。
1.3.软硬件裁剪。嵌入式系统的硬件和软件都必须高效率地设计,量体裁衣、去除冗余,力争在同样的硅片面积上实现更高的性能,这样才能在具体应用中对处理器的选择更具有竞争力。
1.4.嵌入式系统和具体应用有机地结合在一起,它的升级换代也是和具体产品同步进行,因此嵌入式系统产品一旦进入市场,具有较长的生命周期。
1.5.为了提高执行速度和系统可靠性,嵌入式系统中的软件一般都固化在存储器芯片或单片机本身中,而不是存贮于磁盘等载体中。
1.6.嵌入式系统本身不具备自举开发能力,即使设计完成以后用户通常也是不能对其中的程序功能进行修改的,必须有一套开发工具和环境才能进行开发。
73.BootLoader在嵌入式系统中主要起什么作用?完成哪些主要的工作?(8分)
Boot Loader是在嵌入式系统启动时,操作系统内核运行前,执行的一段程序。Bootloader主要的作用只有一个:就是把操作系统映像文件拷贝到RAM中去,然后跳转到它的入口处去执行,我们称之为启动加载模式,该过程没有用户的介入,是它正常工作的模式。它的步骤如下:
Stage1:
1. 硬件设备初始化。为stage2的执行及随后内核的执行准备好基本的硬件环境
2. 为加载stage2 准备ram空间。为了获得更好的执行速度,通常吧stage2加载到ram中执行
3. 复制stage2的代码到ram中
4. 设置好堆栈
5. 跳转到stage2的c程序入口
Stage2:
1. 初始化本阶段要使用的硬件设备
2. 检测系统内存映射
3. 将内核映像和根文件系统映像从flash读到ram中
4. 为内核设置启动参数
5. 调用内核
74.搭建嵌入式开发环境,连接目标板,一般使用什么通信接口连接?在Windows主机上使用什么软件建立连接?在Linux主机上使用什么软件建立连接?(7分)
一般使用eth、串口、USB。在Windows主机上使用DNW、CRT。在Linux主机上使用minicom。
75.linux内核里面,内存申请有哪几个函数,各自的区别?(8分)
答: kmalloc 和 get_free_page 、 get_free_pages 。
malloc 函数申请的内存可以任意大小,但是 kmalloc 最大只能开辟 128k-16 , 16 个字节是被页描述符结构占用了。
而 get_free_page 或 get_free_pages 是申请的内存以页的大小为基准,大小为页的整数倍。
get_dma_pages, 这个函数除了以上功能外还能支持 DMA 传输。
76.ARM-linux启动分几部分,简述流程(9分)
ARM-Linux启动分为2部分。
(1)板子上电,核心版上的BL0自动执行,初始化硬件后,从存储器中拷贝BL1到内存并运行,BL1同样初始化硬件,设置堆栈,拷贝BL2到内存并执行。
(2)BL2再次初始化硬件,检测内存映射,拷贝内核文件镜像、内核镜像到内存,设置内核启动的参数,且跳转到内核的第一条指令。
77.PING命令的功能是什么,通过的是哪个层的什么协议;
主要的功能是用来检测网络的连通情况和分析网络速度
属于网络层协议,主要用于在主机与路由器之间传递控制信息,包括报告错误、交换受限控制和状态信息等。