嵌入式系统工程师笔试/面试常考题梳理

嵌入式系统工程师笔试/面试常考题梳理

题 1:用find写出查找 /opt/kernel 目录下(包括子目录)文件名为Kconfig的命令。

  • find /opt/kernel -name Kconfig

题 2:变量的声明和定义有什么区别?

  • 为变量分配地址和存储空间称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入extern修饰的是变量的声明,说明此变量将在文件以外或文件后面部分定义。很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,例如外部变量。

题 3 :sizeof是库函数吗?sizeof 和 strlen 的区别?

  • sizeof 是一个操作符,strlen 是库函数。
    • sizeof 的参数可以是数据的类型,也可以是变量,而 strlen 只能以结尾为 ‘\0‘ 的字符串作参数。编译器在编译时就计算出了 sizeof 的结果。而 strlen 函数必须在运行时才能计算出来。并且 sizeof计算的是数据类型占内存的大小,而 strlen 计算的是字符串实际的长度。
    • 数组做 sizeof 的参数不退化,传递给 strlen 就退化为指针了。

例题 1 :

char str[] = "Hello";
char *p = str;
int n = 10;
请计算:
sizeof( str ) = 6
sizeof( p ) = 4
sizeof( n ) = 4

例题 2 :

void Func(char str1[100])
{
	sizeof( str1 ); 
}
 void *p = malloc( 100 ); 
 char *str2[]={"Hello","HI", "\x0"};
请计算:
sizeof( str1)  = 4
sizeof( p ) = 4
sizeof( str2 ) = 12
sizeof( str[0] ) = 4

题 4:简述队列和栈的异同。

  • 队列和栈都是线性存储结构,但是两者的插入和删除数据的操作不同,队列是“先进先出”,栈是“后进先出”。这里我们要区别栈区和堆区。堆区的存取是“随意存取”,而栈区是“后进先出”。栈由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。堆一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配方式类似于链表。它们与本题的堆和栈是两回事。堆栈只是一种数据结构,而堆区和栈区是程序的不同内存的存储区域。

题 5:假设某个函数的原型为:void foo( int x, int y );该函数分别被C编译器和C++编译器编译后在符号库中的名字是什么?

  • 该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像 _foo_int_int 之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新的名字称为“mangled name”。_foo_int_int 这样的名字包含了函数名、函数参数数量以及类型信息,C++就是靠这种机制来实现函数重载的。)

题 6:全局变量和局部变量是否可以同名?他们是否有什么区别?

  • 可以同名,从生命周期来看,全局变量存活于整个程序的运行周期,而局部变量只存在所在函数的运行周期,当函数返回后,局部变量也消失,全局变量存储在静态数据区,其中bss段存放程序中未初始化的全局变量,data段存放程序中已初始化的全局变量。局部变量存放在栈区。

题 7 :static有什么用途? static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?

1)修饰全局变量,保证此全局变量只在本文件内可见,其他文件中用不了。
2)修饰函数作用与1)相同。
3)修饰局部变量,将一个局部变量从栈里分配空间改为在数据段里分配空间,整个生命周期和全局变量相同。
  • 全局变量(外部变量)的说明之前再冠以 static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。 两者在存储方式上并无不同,区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

题 8:什么是预编译?何时需要预编译?

  • 预编译又称为预处理,是做一些代码文本的替换工作。处理#开头的指令,比如拷贝 #include 包含的文件代码,#define 宏定义的替换,条件编译等,是在为编译做预备工作。
  • C 编译系统在对程序进行编译之前,先进行预处理。C 提供的预处理功能主要有以下三种:1)宏定义 2)文件包含 3)条件编译。

题 9:参数传递有哪些形式?寄存器和堆栈传递各有什么优缺点?

  • 每种体系结构及对应的编译器对参数传递都有自己的规定。参数传递并非总是通过堆栈进行,参数入栈和出栈要耗费时间,编译器总是尽量优化利用寄存器来传递参数,因为寄存器的访问效率要高,但是当参数过多时,将放弃优化而用栈传递参数。因此为了提高调用性能,应尽量减少参数个数,太多时可以将所有参数重新定义为一个结构体,利用结构体指针来传递参数。在函数接口设计时应考虑硬件平台和编译器的特性,以灵活定义参数形式。

题 10:编写一个宏,实现判断数组 a 元素的个数。

#define CNT( a ) sizeof(a)/sizeof(*a) 

题 11:关于动态申请内存的问题

void GetMemory(char *p)
{
	p = (char *)malloc(100);
}
void Test(void) 
{
	char *str = NULL;
	GetMemory(str);   
	strcpy(str, "hello world");
	printf("%s\n", str);
}

  • 请问运行Test函数会有什么样的结果?
  • 传入 GetMemory(str) 函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,因此执行完GetMemory(str); 后,str仍未NULL。

题 12:请问运行Test函数会有什么样的结果?

char *GetMemory(void)
{  
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char *str = NULL;
	str = GetMemory();
	printf(str);
}

  • 可能时乱码。因为char p[] = “hello world”; return p; p[]数组为函数内的局部变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。

题 13:请问运行Test函数会有什么样的结果?

void GetMemory(char **p)
{
	*p = (char *)malloc(100);
}
void Test(void)
{
	char *str = NULL;
	GetMemory(&str);
	strcpy(str, "hello"); 
	printf(str);
}

  • GetMemory(&str); 传入的参数为字符串指针的指针。因此 str 指向了malloc(100);在堆区申请内存的起始地址。但是需要注意的是,malloc(100),为判断内存是否申请成功。应加上if ( *p == NULL ) { ...//进行申请内存失败处理 }

题 14:请问运行Test函数会有什么样的结果?

void Test(void)
{
	char *str = (char *) malloc(100);
	strcpy(str, "hello");
	free(str); 
	if(str != NULL) 
	{
		strcpy(str, “world”); 
		printf(str);
	}
}

运行成功,输出可能是乱码,char *str = (char *) malloc(100); 后未进行内存是否申请成功判断;另外,在free(str); 后为将str 指向空,导致可能变成一个野指针,应加上:str = NULL;

题 15:简述数组和指针的区别。

  • 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
  • 修改内容上的差别:
char a[] =  "hello";
a[0] =  'X';
char *p =  "world"; //  注意 p 指向常量字符串 
p[0] =  'X'; //  编译器不能发现该错误,运行时错误

  • 使用 sizeof( a ) 可以计算出数组的容量(字节数)。使用sizeof( p ) ,得到的是一个指针变量的字节数,而不是 p 所指的内存容量。 C/C++语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数传递时,该数组自动退化为同类型的指针。
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12 字节
cout<< sizeof(p) << endl; // 4 字节
// 计算数组和指针的内存容量
void Func(char a[100])
{
	cout<< sizeof(a) << endl; // 4 字节而不是 100 字节
}

题 17:如何引用一个已经定义过的全局变量?

  • 可以用引用头文件的方式,也可以使用 extern 关键字,如果用引用头文件的方式来引用某个在头文件中声明的全局变量,假定你将变量名写错了,那么在编译期间会报错,如果你用exten 方式引用,假定你犯了同样的错误,那么在编译期间不会报错,而是在链接期间报错。

题 18:typedef 和 define 有什么区别?

  • 用法不同:typedef 用来定义一种数据类型的别名,增强程序的可读性。define 主要用来定义常量,以及书写复杂使用频繁的宏。
  • 执行时间不同:typedef 是编译过程的一部分,有类型检查的功能。define 是宏定义,是预处理的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。
  • 作用域不同:typedef 有作用域的限定。define 不受作用域约束。

题 19:int (*s[10]) (int) 表示什么?

  • 函数指针数组,每个指针指向一个 int func(int ) 的函数。

题 20:设置地址为 0x67a9 的整型变量的值为 0xaa66

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值