更多课程 选择中心

嵌入式培训
达内IT学院

400-996-5531

想做嵌入式工程师,先过笔、面试关!

  • 发布:嵌入式培训
  • 来源:嵌入式ARM
  • 时间:2018-04-28 15:26

嵌入式工程师笔、面试题

你想要成为一名嵌入式工程师吗?那你得先过嵌入式面试关,今天嵌入式培训班带来的是嵌入式软件工程师的笔、面试,主要针对初次找嵌入式工作的人群,并且有对面试和笔试中遇到的一些问题进行分析。快来小试牛刀吧:

1、sizeof与strlen的区别

(1)、sizeof是运算符,strlen是函数

(2)、strlen只能用char*做参数,且必须是以''\0''结尾的,而sizeof可用类型做参数,还可用函数做参数,如:

int sum();

printf("%d\n", sizeof(sum()));//输出的结果是sizeof(int),即4。

(3)、数组做sizeof的参数不退化,传递给strlen就退化为指针了。

(4)、大部分编译程序,sizeof在编译时确定,因此sizeof(x)可以用来定义数组维数;而strlen要在运行时才能计算出来,用来计算字符串的长度,而不是类型占内存的大小;

char str[20]="0123456789";

int len1=strlen(str); //len1=10;

int len2=sizeof(str); //len2=20;

(5)、sizeof后,若为类型必须加括弧,若为变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。

(6)、当适用于一个结构类型时或变量,sizeof 返回实际的大小;当适用一静态地空间数组,sizeof归还全部数组的尺寸。

sizeof 操作符不能返回动态地被分派了的数组或外部的数组的尺寸;

(7)、数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,

如:

fun(char [8])

fun(char [])

都等价于 fun(char *)

在C++里参数传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小,如果想在函数内知道数组的大小, 需要这样做:

进入函数后用memcpy拷贝出来,长度由另一个形参传进去

fun(unsiged char *p1, int len)

{

unsigned char* buf = new unsigned char[len+1]

memcpy(buf, p1, len);

}

常在用到 sizeof和 strlen的时候,通常是计算字符串数组的长度,如果是对指针,结果则会不一样的:

char* str = "abacd";

sizeof(str)//结果 4 --->str是指向字符串常量的字符指针,sizeof 获得的是一个指针所占的空间,应该是长整型的,所以是4;

sizeof(*str) //结果 1 --->*str是第一个字符,其实就是字符串的第一位'a' 所占的内存空间,是char类型的,占了 1 位;

strlen(str)= 5 //--->若要获得这个字符串的长度,则一定要使用strlen

2、使用C语言的宏定义

(1)、宏定义给某个数bit3置位和清零

#define SET(a,b) a|(0x1<<b);

#define CLR(a,b) a&(~(0x1<<b));

(2)、宏定义一个求两个数最小值的函数

#define MIN(A,B) ((A)>(B))?B:A

不能使用大于、小于、if语句

#definemax(a,b) (((a)-(b))&(1<<31))?(b):(a)

#define max(a,b) (a-b)==abs(a-b)?a:b

#define max(a,b) ((((long)((a)-(b)))&0x80000000)?(b):(a))

(3)、宏定义交换两个数:

#define SWAP (a,b) {a=a+b; b=a-b; a=a-b; }

3、程序编译的过程

预处理:预处理相当于根据预处理命令组装成新的C程 序,不过常以i为扩展名。

编译: 将得到的i文件翻译成汇编代码。s文件。

汇编:将汇编文件翻译成机器指令,并打包成可重定位目标程序的O文件。该文件是二进制文件,字节编码是机器指令。

链接:将引用的其他O文件并入到我们程序所在的o文件中,处理得到最终的可执行文件。

4、++i和i++的区别

(1)、整形数++i和i++的区别

[cpp] view plain copy

1. #include <stdio.h>

2. int main(void)

3. {

4. int a, b, i = 7;

5. i++; //等价于i = i + 1;

6. ++i; //等价于i = i + 1;

7. a = i++; //等价于a = i; i = i + 1;

8. b = ++i; //等价于i = i + 1; b = i;

9. printf("a = %d, b = %d\n", a, b);

10. return 0;

11. }

12. a = 9, b = 11

在例子中,第7和第8行的作用一样,仅仅是为变量i加1,这时i的值已经增加为9,接下来第10行变量a先获得i的值(即9),然后i加1,第11行变量i 先再加1,然后把得到的值赋给b,所以b的值为11。

(2)、(*p)++和++(*p)的区别

[cpp] view plain copy

1. #include <stdio.h>

2. int main(void)

3. {

4. int a = 5;

5. int *p = &a;

6. int b = (*p)++; //等价于b = a++; 即b = a; a = a + 1;

7. int c = ++(*p); //等价于c = ++a; 即a = a + 1; c = a;

8. printf("b = %d, c = %d\n", b, c);

9. printf("(*p)++ = %d, ++(*p) = %d\n", (*p)++, ++(*p));

10. return 0;

11. }

12. 例子输出结果:

13. b = 5, c = 7 ;

14. (*p)++ = 8, ++(*p) = 8

(3)、*p++和*++p的区别:

[cpp] view plain copy

1. #include <stdio.h>

2. int main(void)

3. {

4. int arr[] = {1, 2, 3, 4};

5. int *p = arr;

6. int a = *p++; //等价于a = *(p++); 即a = *p; p = p + 1;

7. int b = *++p; //等价于b = *(++p); 即p = p + 1; b = *p;

8. printf("a = %d, b = %d\n", a, b);

9. return 0;

10. }

11. a = 1, b = 3

对于第8行的操作数p而言,*和++的优先级相同,但根据它们的右结合性可知,在这个表达式里可认为++的优先级高于*,即*p++等价于*(p++)。

而对于第10行的操作数p而言,它只有一个运算符++,所以先计算++p得出结果,然后间接运算。

5、二分法的实现过程和代码分析

二分查找算法是在有序数组中用到的较为频繁的一种算法,在未接触二分查找算法时,最通用的一种做法是,对数组进 行遍 历,跟每个元素进行比较,其时间为O(n).但二分查找算法则更优,因为其查找时间为 O(lgn),譬如数组{1,2,3,4 ,5,6,7, 8,9},查找元素6,用二分查找的算法执行的话,其顺序为:

(1)、第一步查找中间元素,即5,由于5<6,则6必然在5之后的数组元素中,那么就在{6, 7, 8, 9}中查找,

(2)、寻找{6, 7, 8, 9}的中位数,为7,7>6,则6应该在7左边的数组元素中,那么只剩下6,即找到了。

代码:

[cpp] view plain copy

1. int binary_search(int* a, int len, int goal)

2. {

3. int low =0;

4. int high = len -1;

5. while(low <= high)

6. {

7. int middle = (low + high)/2;

8. if(a[middle] == goal)

9. return middle;

10. //在左半边

11. elseif(a[middle] > goal)

12. high = middle -1;

13. //在右半边

14. else

15. low = middle +1;

16. }

17. //没找到

18. return-1;

19. }

6、大端小端的转换

0x3132(0x32是低位,0x31是高位),把它赋值给一个short变量,那么它在内存中的存储可能有如下两种情况:

(1)、大端字节(Big-endian):

----------------->>>>>>>>内存地址增大方向

short变量地址

0x1000 0x1001

_____________________________

| |

| 0x31 | 0x32

|_______________|________________

高位字节在低位字节的前面,也就是高位在内存地址低的一端.可以这样记住(大端->高位->在前->正常的逻辑顺序)

(2)、小端字节(little-endian):

----------------->>>>>>>>内存地址增大方向

short变量地址

0x1000 0x1001

_____________________________

| |

| 0x32 | 0x31

|________________|________________

低位字节在高位字节的前面,也就是低位在内存地址低的一端.可以这样记住(小端->低位->在前->与正常逻辑顺序相反)

7、栈和堆的区别

栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

堆(操作系统):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

8、Volatile与Register修饰符的作用

Volatile与Register修饰符的作用volatile的作用是:

作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值,

volatile表示这个变量会被意想不到的改变,每次用他的时候都会小心的重新读取一遍,不适用寄存器保存的副本

1) 并行设备的硬件寄存器(如:状态寄存器)

2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3) 多线程应用中被几个任务共享的变量

register

建议编译器使用寄存器来优化对变量的存取

register修饰符暗示编译程序相应的变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其 存储速度

9、char *a 与char a[] 的区别

char *a存放在常量区,是无法修的。而char a[] 是存放在栈中,是可以修改的

10、要求设置一绝对地址为0x67a9 的整型变量的值为0xaa66

int *ptr = (int *)0xaa66; *ptr = 0x67a9;

11、信号量与互斥锁的区别

(1)、互斥量用于线程的互斥,信号量用于线程的同步。

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访 问顺序,即访问是无 序的。

同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已 经实现了互斥,特别 是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

(2)、互斥量值只能为0/1,信号量值可以为非负整数。

也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个 同类资源的多线程 互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。

(3)、互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。

12、用C语言实现字符串倒序

[cpp] view plain copy

1. int fun(char *w)

2. {

3. char t, *s1,*s2;

4. int n=strlen(w);

5. s1=w;

6. s2=w+n-1;

7. while(s1<s2)

8. {

9. t=*s1;

10. *s1=*s2;

11. *s2=t;

12. s1++;

13. s2--;

14. }

15. }

13、选择排序和冒泡排序

(1)、选择法

[cpp] view plain copy

1. void selectsort(int k[],int n) /*选择排序*/

2. {

3. int i,j;

4. int temp;

5. for(i=0;i<n;i++)// “ i ” 就是起始值

6. {

7. for(j=i+1;j<n;j++)

8. {

9. if(k[j] < k[i])

10. {

11. temp=k[j];

12. k[j]=k[i];

13. k[i]=temp;

14. }

15. }

16. }

17. }

(2)、冒泡法

[cpp] view plain copy

1. void bubblesort(int k[],int n)

2. { /*冒泡排序*/

3. int i,j,tmp;

4. for(i=0;i<n ;i++) //控制每趟往前推一个,即少比较一次

5. {

6. for(j=0;j<n-i-1;j++) //从第一个开始,不断与相邻的值比较,并交换最值,一直推到最后,形如冒泡

7. {

8. if(k[j]<k[j+1])

9. {

10. temp=k[j];

11. k[j]=k[j+1]

12. k[j+1]=temp;

13. }

14. }

15. }

16.

17. }

14、memcpy和strcpy的区别

(1)、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。

(2)、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是 根据其第3个参数决定复制 的长度。

(3)、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

15、进程与线程的区别

(1)、地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。

(2)、通信:进程间通信IPC,通过管道、信号量、消息队列、信号、套接字、共享内存通信,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。

(3)、一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

16、TCP和UDP的区别

TCP的可靠性:

首先,为了防止数据在传输的过程中被损坏,每个信息包都包含一个校验码,这个校验码头就是一个用来保证信息包在传输过程中没有被更改的 代码,当信息到达目的地的时候,接收方会对比较验码和收到的信息中的数据,如果校验码不对,则被信息包将被省略。

第二,为了防止信息包丢失,TCP会要求接收方每收到一个信息包都反馈一下,如果接收方没有提供反馈,发送方会自动重发一次,一直到接收方 收到为止,或者它会判断网络链接断开了,就会在程序中返回一个错误的提示。

第三,为了防止信息包重复或顺序错误,TCP每传送一个信息包都会传送一个序号,接收方会检查这个序号,确保收到该信息包,并把全部信息包 按顺序重新合并,同时,如果接收方看到一个已接收了的序号,则这个信息包就会被丢弃。

17、用变量a给出下面的定义

a) 一个整型数(An integer)

b)一个指向整型数的指针( A pointer to an integer)

c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r

d)一个有10个整型数的数组( An array of 10 integers)

e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)

f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)

h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )

答案是:

a) int a; // An integer

b) int *a; // A pointer to an integer

c) int **a; // A pointer to a pointer to an integer

d) int a[10]; // An array of 10 integers

e) int *a[10]; // An array of 10 pointers to integers

f) int (*a)[10]; // A pointer to an array of 10 integers

g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer

h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer

18、关键字static的作用是什么?

1) 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

19、关键字const有什么含意?

下面的声明都是什么意思?

1、const int a;

2、int const a;

3、const int *a;

4、int * const a;

5、int const * a const;

前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:

1) 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)

2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

文末问下大家,中兴被美国“制裁”,华为被调查,你对中美这两大经济体之间的斗争怎么看?恭喜你做完了嵌入式工程师的笔试和面试题,通过了嵌入式工程师的笔试和面试考试,但这只是你嵌入式工程师前进道路上的一小步,如果你还有关于嵌入式的问题或者想入行嵌入式,欢迎到达内嵌入式培训机构进行了解或者进行嵌入式培训学习!

免责声明:内容和图片源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

预约申请免费试听课

填写下面表单即可预约申请免费试听!怕钱不够?可就业挣钱后再付学费! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!

上一篇:这十个建议送给想要零基础入门嵌入式的同学
下一篇:看你所学的专业适合嵌入式系统四层的哪一层?

想要在金九银十找到嵌入式相关的工作?先看这三关你能过吗?

嵌入式系统测试题其实很简单,不信你来做做看!

嵌入式Linux面试题3道,助你过关斩将!

嵌入式面试题二十道,有解析哦!

Copyright © 2023 Tedu.cn All Rights Reserved 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有

选择城市和中心
黑龙江省

吉林省

河北省

湖南省

贵州省

云南省

广西省

海南省