嵌入式、c/c++试题

本文整理了两份嵌入式系统及C/C++编程的面试试题,涵盖了预处理指令、变量定义、静态与const关键字、volatile的作用、位操作、中断处理、内存分配问题、指针运算等多个方面。通过对这些问题的解答,深入理解C/C++的关键特性和嵌入式系统中的常见概念。
摘要由CSDN通过智能技术生成

第一份

参考链接: 面试积累.

1. 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
 #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 

注意点:
1). #define 标识符 字符串 --基本知识

凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等
宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。
宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令
注意括号的使用

2). 懂得预处理器将为你计算常数表达式的值,可直接写出你是如何计算一年中有多少秒而不是计算出实际的值
3). 意识到这个表达式将使一个16位机的整型数溢出,因此要用到长整型符号L,最好为UL
4). 提一手防止重复定义(防止头文件重复的定义申明):#ifndef…#define…#else…#endif
5).注意宏定义中#error的使用,用#error 可以用于检查一个宏是否已经被定义。#error表示预处理器遇到这个命令时,将停止编译,并打印用户自定义的错误信息

用宏定义写出  swap(x,y),即交换两数,不使用第三变量的情况下
#define swap(x,y) (	(x)=(x)+(y); (y)=(x)-(y); (x)=(x)-(y);	)
//任何变量一定要打小括号,结尾不要加;
带参宏和带参函数的区别
带参宏:在编译程序时处理;无需定义形参;不会占用存储空间;不会占用运行时间
带参函数:在运行程序时处理;需要定义形参;会占用存储空间;调用和返回时会占用运行时间
程序的编译过程:预处理、编译、汇编、链接。
	预处理:预处理相当于根据预处理命令组装成新的C程序,不过常以i为扩展名。
	编译:    将得到的i文件翻译成汇编代码,文件后缀为.s文件。
	汇编:    将汇编文件翻译成机器指令,并打包成可重定位目标程序的O文件。该文件是二进制文件,字节编码是机器指令。
	链接:    将引用的其他O文件并入到我们程序所在的o文件中,处理得到最终的可执行文件。
#ifdef XX
...
#error "XX has been defined"
...
#else
...
#endif
如果 XX 被定义了的话,编译的时候编译器就会报错,error c320 "XX has been defined".
2.用变量a给出下面的定义

a) 一个整型数
b) 一个指向整型数的指针
c) 一个指向指针的的指针,它指向的指针是指向一个整型数
d) 一个有10个整型数的数组
e) 一个有10个指针的数组,该指针是指向一个整型数的
f) 一个指向有10个整型数数组的指针
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

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

3.关键字static的作用是什么?

1). 在函数体内,声明静态局部变量,存储于全局数据区,其值保存不释放

普通局部变量是再熟悉不过的变量了,在任何一个函数内部定义的变量(不加static修饰符)都属于这个范畴。编译器一般不对普通局部变量进行初始化,也就是说它的值在初始时是不确定的,除非对其显式赋值。

  • 普通局部变量存储于进程栈空间,使用完毕会立即释放。

静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。

  • 静态局部变量在全局数据区分配内存空间;编译器自动对其初始化其作用域为局部作用域,当定义它的函数结束时,其作用域随之结束
void fn(void)
{
   
    int n = 10;				//普通局部变量
    printf("n=%d\n", n);
    n++;
    printf("n++=%d\n", n);
}
void fn_static(void)
{
   
    static int n = 10;		//静态局部变量
    printf("static n=%d\n", n);
    n++;
    printf("n++=%d\n", n);
}
int main(void)
{
   
    fn();
    printf("--------------------\n");
    fn_static();
    printf("--------------------\n");
    fn();
    printf("--------------------\n");
    fn_static();
    return 0;
}
n=10			//结果函数结束释放变量
n++=11

static n=10		//结果函数结束变量保存
n++=11

n=10
n++=11

static n=11
n++=12

2).在函数体外(但在模块内),声明静态全局变量,存储于全局数据区,其值保存不释放

普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。

静态全局变量仅对当前文件可见,可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。

在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。

static函数的使用方式与静态全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:
静态函数只能在声明它的文件中可见,其他文件不能引用该函数 ;不同的文件可以使用相同名字的静态函数,互不影响
非静态函数可以在另一个文件中直接引用,甚至不必使用extern声明

4.关键字const是什么含意?

const----只读常变量。用 const 定义的变量的值是不允许改变的,即不允许给它重新赋值,即使是赋相同的值也不可以。因此如果要给变量赋值,只能必须在定义的时候就给它赋初值。

用 const 修饰的变量,无论是全局变量还是局部变量,生存周期都是程序运行的整个过程。
经过 const 修饰过的变量存储在内存中的“只读数据段”中。只读数据段中存放着常量和只读变量等不可修改的量。
有时候, const 定义的变量仍然不能作为数组的长度

const int a;
int const a;
const int *a;
int * const a;
int const *  const a;

第一个和第二个的作用是一样,定义a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
第五一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。

5. 关键字volatile有什么含意 并给出三个不同的例子。

计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中会涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。
为了处理这个问题,在CPU里面就有了高速缓存(Cache)的概念。当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以(优化器)直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。
然后,可能出现的问题是,当两个线程分别读取了t的值,假设此时t的值为0,并且把t 的值存到了各自的高速缓存中,然后线程1对t进行了加1操作,此时t的值为1,并且把t的值写回到主存中。但是线程2中高速缓存的值还是0,进行加1操作之后,t的值还是为1,然后再把t的值写回主存

volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据,而不是使用保存在寄存器里的备份。
volatile 声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问

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

  1. 中断服务程序中修改的供其它程序检测的变量需要加volatile(会访问到的非自动变量);
  2. 多任务环境下各任务间共享的标志应该加volatile;
  3. 存储器映射的硬件寄存器(状态寄存器)通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
6.给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。

用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:

#define BIT3 (0x1<<3) 
static int a; 					// 注意|=和&=~操作。
void set_bit3(void) 
{
    
a |= BIT3; 
} 
void clear_bit3(void) 
{
    
a &= 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值