嵌入式软件经典面试题

预处理器

用预处理命令#define声明一个常数,用以表明一年中有多少秒(忽略闰年问题)

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

考点
1)#define语法的基础知识
2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是何年机断一年中有多少秒而不是计算出实际的值,是更清晰没有代价的。
3)意识到这个表达式将使用一个16位机的整型数溢出,因此要使用到成整形符号L,告诉编译器这是长整型数值。
4)如果在你使用表达式中用到UL(无符号长整形),表明有一个良好的编译习惯
#define 的作用
在C或C++语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。“宏”分为有参数和无参数两种。还可以防止重复定义。

#difine M (a)+(b)
M*M= (a)+(b)*(a)+(b)

上例提醒我们,需要在括号的使用上,一定注意
对于宏定义还要注意:
  1. 预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。
  2. 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。
  3. 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令。

"标准"宏MIN

这个宏输入两个参数并返回一个较小的一个

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

1)懂得在宏中小心的把参数用括号括起来。
2)指针越界可能是很严重的问题
3)了解宏的副作用
例子:

least=MIN(*p++,b)

有兴趣的可以自行实践

预处理器标识#error

当预处理器预处理到#error命令时将停止编译并输出用户自定义的错误消息
用法示例:
检查编译此源文件的编译器是不是C++编译器
如果使用的是C语言编译器则执行#error命令
如果使用的是 C++ 编译器则跳过#error命令

死循环

while1{
    
    }
for(;;)
{
    
    }
goto

Loop

1与2的比较:
for(;;)死循环里的两个;;代表两个空语句,编译器一般会优化掉它们,直接进入循环体。 while(1)死循环里的1被看成表达式,每循环一次都要判断常量1是不是等于零。相对来说for式死循环更加高效一点 当然以上条件成立还要看编译器的优化,一些编译器优化的两者并无区别,但是,并非所有的编译器都做了这样的优化。
执行到"goto代码"的时候,程序会自动跳到goto后面跟着的字符标志处,即"Loop"处…但是"Loop"处位于"goto"代码之前,因此跳到了"Loop"的代码处向下走,会又一次碰到goto…如此循环反复,因此会变为死循环

数据声明

用变量a给出下面定义
1)一个整型数
2)一个指向整型数据的指针
3)一个指向指针的指针,他指向的指针是指向一个整形数
4)一个有5个整型数的数组
5)一个有5个指针的数组,该指针是指向一个整型数
6)一个指向有5个整形数数组的指针
7)一个指向函数的指针,该函数有一个整形参数并返回一个整型数
8)一个有5个指针的数组,该指针指向一个函数,该函数有一个象形参数并返回一个整型数
  
1)

int a;
int *a;
int **a;
int a[5];
int *a[5];
int (*a)[5];

7)

int*a)(int)
{
    
    
}
int (*a[5])(int)
{
    
    
}

static

三个作用
1)在函数体内,被定义的变量是static的变量,在其他函数调用该函数的过程中,维持其值不变
2)在模块内,在函数体外,被定义位static的变量,可以被该模块内的所有函数访问,他是一个本地的全局变量。
3)在模块内,一个被声明位static的变量被限制在不能在模块外的函数调用。

const

volatile

位操作

上面三个知识点在其他的博客中有详细说明,在这里还要提供一下其他操作:
使用define来进行位操作的定义:
#define BIT(X) (0X01<<(X))
我们可以使用这个BIT(X)来进行置位和清零:
置位

uint8_t a=0x11;
a|=BIT(3);

清零

uint8_t a=0x11;
a&=~BIT(3);

访问内存固定内存位置

要求设置一绝对地址0x5678的整型变量值为0x1234:

int *p;
p=0x5678;
*p=0x1234;

或者:

*int *const(0x5678)=0x1234;

中断

1)中断函数中不能有参数
2)中断函数中不能有返回值
3)中断中应该尽量简单,不要进行浮点运算,不同的编译器效果不同,浮点一般都是不可重入的,打印函数printf()经常有重入和性能上的问题。

整数自动转换

unsigned int a=6;
signed int b=-20;
(a+b)>6?puts(">6"):puts("<=6");

答案是前者!
当表达式中存在有符号和无符号类型时,所有的操作数都自动转换为无符号的。因此-20变成了一个非常大的数。

unsigned int zero=0;
unsigned int compzero=0xFFFF;

对于不是16位的编译器,第二个应该这样:

unsigned int compzero-~0;

动态内存分配

在堆中动态内存分配可能会存在内存碎片。

char *p;
if((p=(char*)malloc(0))==NULL)
puts("null");
else
put("YES");

会输出YES;

typedef

#define D struct s*
typedef struct s *  P

上面两种typedef更好,为什么呢?

D D1,D2;
P P1,P2;

第一题扩展为:

struct s* D1,D2;

自然就可能会出现错误

猜你喜欢

转载自blog.csdn.net/weixin_42271802/article/details/106241084
今日推荐