当前位置:首页 > 公众号精选 > 嵌入式大杂烩
[导读]关注「Linux大陆」,一起进步!#error的作用是什么?#error 指令让预处理器发出一条错误信息,并且会中断编译过程。下面我们从Linux代码中抽取出来一小段代码并做修改得到示例代码:这段示例代码很简单,当RX_BUF_IDX宏的值不为0~3时,在预处理阶段就会通过#er...

关注Linux大陆」,一起进步!

#error的作用是什么?

#error  指令让预处理器发出一条错误信息,并且会中断编译过程。下面我们从Linux代码中抽取出来一小段代码并做修改得到示例代码:

这段示例代码很简单,当RX_BUF_IDX宏的值不为0~3时,在预处理阶段就会通过 #error  指令输出一条错误提示信息:

"Invalid configuration for 8139_RXBUF_IDX"

下面编译看一看结果:

推荐文章:认识认识#pragma、#error指令

位操作的基本使用

给一个32bit数据的位置1,怎么用宏来实现?

#define SET_BIT(x, bit) (x |= (1 << bit)) /* 置位第bit位 */
推荐文章:C语言、嵌入式位操作精华技巧大汇总

隐式转换规则

如下代码的输出结果是?为什么?

#include 
int main(void)
{
 unsigned int a = 6;
 int b = -20;

 if (a   b > 6)
  printf("a b大于6\n");
 else
  printf("a b小于6\n");

 return 0;
}
程序输出结果为:

a b大于6
原因是因为编译器会将有符号数b转换成为一个无符号数,即此处 a b 等价于 a (unsigned int)b

该程序运行在32bit环境下,b的值为 0xFFFFFFFF-20 1 = 4294967276 ,即a b将远远大于6。

C 语言按照一定的规则来进行此类运算的转换,这种规则称为 正常算术转换 ,转换的顺序为:

double>float>unsigned long>long>unsigned int>int
即操作数类型排在后面的与操作数类型排在前面的进行运算时,排在后面的类型将 隐式转换 为排在前面的类型。

推荐文章:关于有符号数与无符号数的一些总结

typedef与define的区别

(1)#define之后不带分号,typedef之后带分号。

(2)#define可以使用其他类型说明符对宏类型名进行扩展,而 typedef 不能这样做。如:

#define INT1 int
unsigned INT1 n;  //没问题
typedef int INT2;
unsigned INT2 n;  //有问题
INT1可以使用类型说明符unsigned进行扩展,而INT2不能使用unsigned进行扩展。

(3)在连续定义几个变量的时候,typedef 能够保证定义的所有变量均为同一类型,而 #define 则无法保证。如:

#define PINT1 int*;
P_INT1 p1,p2;  //即int *p1,p2;
typedet int* PINT2;
P_INT2 p1,p2;  //p1、p2 类型相同
PINT1定义的p1与p2类型不同,即p1为指向整形的指针变量,p2为整形变量;PINT2定义的p1与p2类型相同,即都是指向 int 类型的指针。

推荐文章:#define与typedef的区别?

写一个MAX宏

#define MAX(x,y) ((x) > (y) ? (x) : (y))
使用括号把参数括起来可以解决了运算符优先级带来的问题。这样的MAX宏基本可以满足日常使用,但是还有更严谨的高级写法。

感兴趣的可参考文章:

https://www.zhaixue.cc/c-arm/c-arm-express.html

死循环

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

(1)while

while(1) { }
(2)for

for(;;) { }
(3)goto

Loop:



goto Loop;

static的作用

在C语言中,关键字static有三个明显的作用:

1、在函数体修饰变量

一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

2、 在模块内(但在函数体外)修饰变量

一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

3、在模块内修饰函数

一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

const的作用

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

const int a; 
int const a;
const int *a;
int * const a;
int const * a const;
  • 前两个的作用是一样,a是一个常整型数。
  • 第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
  • 第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
  • 最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

volatile的作用

以下内容来自百度百科:

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1). 并行设备的硬件寄存器(如:状态寄存器)

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3). 多线程应用中被几个任务共享的变量

回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。

假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。

1). 一个参数既可以是const还可以是volatile吗?解释为什么。

2). 一个指针可以是volatile 吗?解释为什么。

3). 下面的函数有什么错误:

int square(volatile int *ptr)
{
 return *ptr * *ptr;
}
下面是答案:

1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。

3). 这段代码的有个恶作剧。这段代码的目的是用来返指针 *ptr 指向值的平方,但是,由于 *ptr 指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
关闭
关闭