嵌入式软件工程师笔试面试必备(很有帮助)

Posted 中国思想史

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式软件工程师笔试面试必备(很有帮助)相关的知识,希望对你有一定的参考价值。

数组指针与指针数组:

数组指针定义 int (*p)[n];

()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。(从而指向下一个一维数组)

指针数组定义 int *p[n];

[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,,而且它们分别是指针变量可以用来存放变量地址。可以这样赋值 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。

用变量a给出下面的定义:

a)     一个整型数(An integer)int a;

b) 一个指向整型数的指针(Apointer to an integer) int *a;  

c) 一个指向指针的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)int **a;

d) 一个有10个整型数的数组(An array of 10 integers) int a[10];

e) 一个有10个指针的数组,每个指针是指向一个整型数(An array of 10 pointers to integers)int *a[10];

f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers) int (*a)[10];

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

h) 一个有10个函数指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argumentand return an integer )int (*a[10])(int);

定义一个宏

请定义一个宏用于表示每一年有多少秒,忽视闰年的情况

#define SECOND_PER_YEAR (365 * 24 * 60 * 60UL)

给一整型变量a,写两段代码,第一个设置a的bit3,第二个清除a的bit3.

|操作,当操作数为1时,结果为1,当操作数为0时,结果不变;

&操作,当操作数为1时,结果不变,当操作数为0时,结果为0

(第三个bit位在最低字节)

因此,设置a的bit 3为1的方法就是将a和00001000作|操作,这样其他位都不变,只有第3位变成1

清除a的bit3的方法就是将a和11110111作&操作,这样第3位变成0,其他位不变

设置a的bit3代码如下:

#include <stdio.h>

int main()

     int a;

     printf("请输入一个整数");

     scanf("%d",&a);

     a=a|00000100;

     printf("%d",a);

     return 1;

 

清除a的bit3代码如下:

#include <stdio.h>

int main()

      int a;

     printf("请输入一个整数");

     scanf("%d",&a);

     a=a&11111011;

     printf("%d",a);

     return 1;

 

两种方法用宏定义写出swap(x,y)

方法一:算术运算法

a=a+b; 

b=a-b; 

a=a-b; 

 

方法二:逻辑运算法

a=a^b; 

b=a^b; 

a=a^b; 

 

宏定义swap(x, y),使之完成交换x,y的值 

#defineswap1(x, y) \\ 

    (y) = (x) + (y); \\ 

    (x) = (y) - (x); \\ 

    (y) = (y) - (x); 

 

#defineswap2(x, y)\\ 

    x = x^y;\\ 

    y = x^y;\\ 

    x = x^y;

 

 异或运算符

参与运算的两个值,如果两个相应位相同,则结果为0,否则为1。即:0^0=0, 1^0=1, 0^1=1, 1^1=0

例如:10100001^00010001=10110000

任何数异或自己,等于把自己置0

 

delete 和 delete [] 的区别

当调用delete的时候,系统会自动调用已分配的对象的析构函数。当我们用new [] 分配的对象是基本数据类型时,用delete和delete [] 没有区别。但是,当分配的对象是自定义对象时,二者不能通用。一般来说使用new分配的对象,用delete来释放。用new[] 分配的内存用delete [] 来逐个释放。

 

大小值对比宏定义

#define __max(a,b) (((a) > (b)) ? (a) : (b))

#define __min(a,b) (((a) < (b)) ? (a) : (b))

 

C++调用C函数,为什么要加extern "C"?

C++语言支持函数重载,C语言不支持函数重载,函数被C++编译器编译后在

库中的名字与C语言的不同,假设某个函数原型为:

void foo(int x,inty);

该函数被C编译器编译后在库中的名字为:

_foo

而C++编译器则会产生像:

_foo_int_int

之类的名字。为了解决此类名字匹配的问题,C++提供了C链接交换指定符号extern "C"。加上extern "C"表示该函数的编译与调用规则是C的规则

 

写一段用来判断内存存储方式是大端还是小端的代码

联合体union的存放顺序是所有成员都从低地址开始存放,利用该特性就可以轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。代码如下:

intcheckCPUendian()

union

unsigned int a;

unsigned char b;

c;

c.a = 1;

return (c.b = =1);

/*return 1 :little-endian, return 0:big-endian*/

 

定义一个返回值是指向函数的指针且有一个指向函数的指针作参数的函数

通用形式如下:

typedef int(*P)( ); // 定义一个函数指针P类型

P function( int(*p)( ) ); // 定义一个函数返回值P类型,且定义一个指向函数的指针p作参数

 

关键字const含意

声明一个变量为只读。

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

1)const int a; 2)int const a; 3)const int *a; 4)int * const a; 5) int const * const a ;

1,2一样a为只读整形变量;3 指向一个只读整形变量的指针;4 指向整形的只读指针; 5 指向只读整形的只读指针

 

C语言实现设置一绝对地址为0x1234的整型变量的值为0xaa55

int *p;

p=(int *) 0x1234; // 把整型数0x1234强制转换(typecast)为一指针

*p=0xaa55;

 

中断服务程序(Interrupt Service Routines,ISR)注意事项

1、ISR不能有返回值;

2、ISR不能传递参数;

3、ISR应该是短而高效的,在ISR中做浮点运算是不明智的

4、ISR中不应该有重入和性能上的问题,因此不应该使用pintf()函数。

 

typedef和#define的区别

#define在预编译的时候处理作机械的字符替换。typedef在编译的时候处理,并不是作简单的字符替换。而是如同定义变量一样声明一个类型。然后用它去定义这种类型的变量。

 

反码、补码、原码

负数的反码:对原码除符号位外的其余各位逐位取反

负数的补码:对反码加1

正数的原码、反码、补码都一样

 

字符串转整数

#include <stdio.h>

char str[6]="12345";

int string_to_int(char s[])

int i;

int sum=0;

for(i=0;s[i]!='\\0';i++)

 sum=sum*10+s[i]-'0';

return sum;

 

int main(void)

printf("%d\\n",string_to_int(str));

return 0;

整数转字符串

#include <stdio.h>

#include <math.h>

#define LEN 4

char str[]=" ";

char *int_to_string(int given)

int i;

int temp;

for(i=0;i<LEN;i++)

temp=given/pow(10,LEN-1-i); // 从最高位开始

given=given%((int) pow(10,LEN-1-i));

str[i]=temp+'0';

return str;

 

int main(void)

printf("%s\\n",int_to_string(9876));

return 0;

 

用嵌套循环求1-100的素数

#include <stdio.h>

int main(void)

int i, j;

for (i = 1; i <= 100; i++)

for (j = 2; j < i; j++)

if (i % j == 0)

break;

if (j == i)

printf("%d\\n", i);

return 0;

 

计算一个数的阶乘

递归实现:

int factorial(int n)

if (n == 0)

return 1;

else

int recurse = factorial(n-1);

int result = n * recurse;

return result;

 

循环实现:

int factorial(int n)

int result = 1;

while (n > 0)

result = result * n;

n = n - 1;

return result;

 

C语言实现冒泡排序算法(从大到小,从小到大)

#include<stdio.h>

#define LEN 5

int a[LEN]=5,4,3,2,1;

void bubble_sort(void)

int i,j,flag=1;

int temp;

for(i=1;(i<LEN)&&(flag==1);i++)

flag=0;

for(j=0;j<LEN-i;j++)

if( a[j]>a[j+1] ) // a[j]>a[j+1]从小到大;a[j]<a[j+1] 从大到小

flag=1;

temp=a[j];

a[j]=a[j+1];

a[j+1]=temp;

printf("%d,%d,%d,%d,%d\\n",a[0],a[1],a[2],a[3],a[4]);

//--------

int main(void)

bubble_sort();

return 0;

 

C语言实现插入排序算法(从大到小,从小到大).

#include<stdio.h>

#define LEN 5

int a[LEN]=7,4,8,4,5;

void insertion_sort(void)

int i,j,key;

for(j=1;j<LEN;j++)

printf("%d,%d,%d,%d,%d\\n",a[0],a[1],a[2],a[3],a[4]);

key=a[j];

i=j-1;

while( (i>=0) && (a[i]>key) ) //a[i]>key 从小到大; a[i]<key 从大到小

a[i+1]=a[i];

i--;

a[i+1]=key;

printf("%d,%d,%d,%d,%d\\n",a[0],a[1],a[2],a[3],a[4]);

//-----

int main(void)

insertion_sort();

return 0;

 

TCP/IP协议集包括应用层,传输层,网络层,网络访问层。

网络层包括:    Internet协议(IP)     Internet控制信息协议(ICMP)    地址解析协议(ARP)    反向地址解析协议(RARP) 

网络访问层又称作主机到网络层(host-to-network).网络访问层的功能包括IP地址与物理地址硬件的映射,以及将IP封装成帧.基于不同硬件类型的网络接口,网络访问层定义了和物理介质的连接。

 

TCP与UDP区别

基本区别

1.基于连接与无连接

2.TCP要求系统资源较多,UDP较少;

3.UDP程序结构较简单

4.流模式(TCP)与数据报模式(UDP);

5.TCP保证数据正确性,UDP可能丢包

6.TCP保证数据顺序,UDP不保证

编程区别

通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),这表示建立一个socket用于流式网络通讯。

SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制保证它是可靠的、有序的,即每个包按照发送的顺序到达接收方。

而SOCK_DGRAM这种是User DatagramProtocol协议的网络通讯,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据。任何一方建立一个socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存在,是否发送了数据。它的特点是通讯速度比较快。大家都知道TCP是要经过三次握手的,而UDP没有。

 

进程与线程的区别

通常操作系统把进程作为分配资源的基本单位,而把线程作为独立运行和CPU独立调度的基本单位。

内核锁

多核处理器下,会存在多个进程处于内核态的情况,而在内核态下,进程是可以访问所有内核数据的,因此要对共享数据进行保护,即互斥处理。

信号量mutex是sleep-waiting。就是说当没有获得mutex时,会有上下文切换,将自己、加到忙等待队列中,直到另外一个线程释放mutex并唤醒它,而这时CPU是空闲的,可以调度别的任务处理。

而自旋锁spin lock是busy-waiting。就是说当没有可用的锁时,就一直忙等待并不停的进行锁请求,直到得到这个锁为止。这个过程中cpu始终处于忙状态,不能做别的任务。

例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0 和Core1上。用spin-lock,coer0上的线程就会始终占用CPU。

另外一个值得注意的细节是spin lock耗费了更多的usertime。这就是因为两个线程分别运行在两个核上,大部分时间只有一个线程能拿到锁,所以另一个线程就一直在它运行的core上进行忙等待,CPU占用率一直是100%;而mutex则不同,当对锁的请求失败后上下文切换就会发生,这样就能空出一个核来进行别的运算任务了。

 volatile关键字

简单地说作用就是防止编译器对代码进行优化。比如如下程序:

XBYTE[2]=0x55;

XBYTE[2]=0x56;

XBYTE[2]=0x57;

XBYTE[2]=0x58;

对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入volatile,则编译器会逐一地进行编译并产生相应的机器代码(产生四条代码)。

精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

 

以上是关于嵌入式软件工程师笔试面试必备(很有帮助)的主要内容,如果未能解决你的问题,请参考以下文章

2021最新秋招Java软件工程师面试笔试必备面试题及答案汇总

嵌入式软件工程师笔试面试指南-数据结构与算法

嵌入式软件工程师笔试面试指南-ARM体系与架构

软件实施工程师面试或者笔试时候该注意啥问题?

嵌入式软件开发 C语言笔试面试题

嵌入式软件开发 笔试和面试笔记 (最近更新:2022-02-17)