嵌入式软件工程师面试遇到的经典题目_int ptr; ptr= (int )0x67a9; ptr = 0xaa55;(3)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

#define SECONDS_PER_YEAR (606024*365)UL

一定要加括号,宏定义只是替换,不加括号会出错。。。。

10、实现把字符串转化成整数

int my\_atoi(char \*str)
{
	int sum=0,status=1;
	if(str == NULL)
	{
		return 0;
	}
	if(\*str == '-')
	{
		status = -1;
		str++;
	}
	while((\*str)!='\0')
	{
		sum=sum\*10+ ((\*str)-'0');
		str++;
	}
	return sum\*status;
	
}


11、写一个程序验证系统的大小端存储格式

/\*方法1\*/
typedef union {
	int i;
	char c;
}my_union;
 
int checkSystem1(void)
{
	my_union u;
	u.i = 1;
	return (u.i == u.c);
}
/\*方法2\*/
int checkSystem2(void)
{
	int i = 0x12345678;
	char \*c = &i;
	return ((c[0] == 0x78) && (c[1] == 0x56) && (c[2] == 0x34) && (c[3] == 0x12));
}


12、如何判断一个byte数据中有多少bit为1?

int select(unsigned char data)
{
	int count=0,i=1;
	while(data!=0)
	{
		count +=  (data & i);
		data >>= 1;
	}
	return count;	
}


13、 C语言中关键字volatile的含义

volatile 的意思是“易失的,易改变的”。这个限定词的含义是向编译器指明变量的内容可能会由于其他程序的修改而变化。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份

一般说来,volatile用在如下的几个地方:

1、中断服务程序中修改的供其它程序检测的变量需要加volatile;

2、多任务环境下各任务间共享的标志应该加volatile;

3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;

一个参数既可以是const还可以是volatile吗?解释为什么。
是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

14、进程间通信的方式有哪些?

1.无名管道( pipe ): 管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

2.有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

3.消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

4.信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

5.信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

6.共享内存( shared memory ) : 共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

7.套接字( socket ) : 套解字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

15、堆和栈的区别

①、申请方式不同:栈由系统自动分配,堆是由人为自行开辟(malloc,new)
② 、申请的大小不同:栈是从高地址像低地址分配的,分配空间较小,堆是由地址向高地分配的,空间较大
③ 、申请效率不同:栈由系统分配,分配速度较快,堆一般较慢
④ 、栈是连续的地址空间,堆不是连续的地址空间,很容易产生内存碎片,浪费内存。

16、分别给出bool,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var)

	Bool: if(!var)
	Int : if(var==0)
	Float:  const float val=0.00000001
			If((var >= -val) && (var <= val))


17、 进程和线程的区别

进程和线程的根本区别是进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。另外区别还有资源开销、包含关系、内存分配、影响关系、执行过程等。

  • 资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
  • 包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
  • 内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的。
  • 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
  • 执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。

进程和线程的根本区别是进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

18.写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。

#define MIN(A, B) ((A) <= (B)? (A) : (B))
这个测试是为下面的目的而设的:
(1)标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符 变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为 了能达到要求的性能,嵌入代码经常是必须的方法。
(2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if -then-else更优化的代码,了解这个用法是很重要的。
(3)懂得在宏中小心地把参数用括号括起来。
(4)我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?
least = MIN(*p++, b);
宏定义#define MIN(A, B) ((A) <= (B) ? (A) : (B))对MIN(*p++, b)的作用结果是:
((*p++) <= (b) ? (*p++) : (b))这个表达式会产生副作用,指针p会作两次++自增操作。

19.预处理器标识#error的目的是什么?

这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。
编译程序时,只要遇到 #error 就会跳出一个编译错误当程序比较大时,往往有些宏定义是在外部指定的(如makefile),或是在系统头文件中指定的,当你不太确定当前是否定义了 XXX 时,就可以改成如下这样进行编译:
#ifdef XXX
#error “XXX has been defined”
#else

#endif
这样,如果编译时出现错误,输出了XXX has been defined,表明宏XXX已经被定义了。

20. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?

这个问题用几个解决方案。我首选的方案是:

while(1) 
{ 
} 

一些程序员更喜欢如下方案:

for(;;) 
{ 
} 

这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。
第三个方案是用 goto

Loop: 
... 
goto Loop; 

应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。

21. 用变量a给出下面的定义

(1)一个整型数(An integer): int a;
(2)一个指向整型数的指针(A pointer to an integer): int *a;
(3)一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an
integer): int **a;
(4)一个有10个整型数的数组(An array of 10 integers) :int a[10];
(5)一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to
integers): int *a[10];
(6)一个指向有10个整型数数组的指针(数组指针):int (*a)[10];int *p[n](指针数组)
(7)指向函数的指针,该函数有一个整型参数并返回一个整型数(函数指针):int (*a)(int);
(8)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整
型数( An array of ten pointers to functions that take an integer argument and return an
integer ): int (*a[10])(int)。
人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。 但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么做准备呢?

22. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码 ,第一个设置a的bit 3,第二个清除a的bit 3。在以上两个操作中,要保持其它位不变。

对这个问题有三种基本的反应
(1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。
(2)用bit fields。bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间
是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙沾实际硬件的边。
(3)用#define和bit masks操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:

#define BIT3 (0x1 < <3) 
static int a; 
void set\_bit3(void) 
{ 
a |= BIT3; 
} 
void clear\_bit3(void) 
{ 
a &= ~BIT3; 
} 

一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的我希望看到几个要点:说明常数、|=和&=~操作。

23. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa55。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。

这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:

int \*ptr; 
ptr = (int \*)0x67a9; 
\*ptr = 0xaa55; 

一个较晦涩的方法是:

\*(int \* const)(0x67a9) = 0xaa55; 

即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。

24. C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?

int a = 5, b = 7, c;
c = a+++b;
这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:
c = a++ + b;
因此, 这段代码持行后a = 6, b = 7, c = 12。
如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是:这是一个关于代码编写风格,代码的可读性,代码的可修改性。

25. 整型数组作为参数传递时,无法在子函数中获得其长度!只有字符串可以,因为它有一个尾巴标识(‘\0’)!所以,整型的数组长度,必须与数组名一同传递到子函数才可以!

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

到子函数才可以!

[外链图片转存中…(img-Lgf8xwVl-1715549400363)]
[外链图片转存中…(img-tUhO7dzD-1715549400363)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值