嵌入式 2-预处理器篇

预处理器篇(Preprocessor)

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

  答:    #define SECONDS_PER_YEAR  (60 * 60 * 24 * 365)UL
这里会涉及以下几点知识:
1) #define 语法的基本知识,例如:不能以分号结束;没有括号导致运算顺序错误等等。
2) 预处理器需要你写的是一个这个常数值的计算表达式,这样显得更清晰,我们实际写代码也是这样做的,如果你直接写这个表达式的结果,你的能力肯定会被质疑。
3) 要有意识到这个计算结果是是非常大的一个数据,这个表达式的结果将使一个16位机的整型数溢出,因此要用到长整型符号L,告诉编译器这个常数是的长整型数;UL就是告诉机器这是一个无符号长整型;

2 . 写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个

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

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

   答:  编译程序时,只要遇到#error就会生成一个编译错误提示消息,并停止编译。其语法格式为:#error error-message

下面举个例子:
程序中往往有很多的预处理指令
#ifdef XXX
...
#else
...
#endif

当程序比较大时,往往有些宏定义是在外部指定的(如makefile),
或是在系统头文件中指定的,
当你不太确定当前是否定义了 XXX 时,就可以改成如下这样进行编译:

#ifdef XXX
...
#error "XXX has been defined"
...
#else

#endif

这样,如果编译时出现错误,输出了XXX has been defined,表明宏XXX已经被定义了。
并且终止代码运行。提示客户去查找问题;

4.预处理器在如何头文件中发挥互斥作用?

答 : 使用 #ifndef #define #endif,比如

// 当项目中有多个c文件使用到同一个头文件是,
//在编译的时候会出现大量的变量,函数声明冲突,解决就是使用宏来进行互斥
#ifndef _HEAR_H_
#define _HEAR_H_
... (头文件内容)
#endif

5.#define和const可以用于定义常量,那两者有何区别?

答:

Define和const 都可以用于定义常量,但在生效时间,内存占用情况,类型检查有以下区别:

  1. define只是单纯的文本替换,define常量的生命生命周期止于编译器,不存在分配内存,存在与程序的代码段
  2. const生效于编译的阶段;define生效于预处理阶段
  3. Const修饰的常量处于程序的数据段,在堆栈中分配空间
  4. Const有数据类型检查,define没有
  5. #define不可调试,const能调试
  6. const定义的变量在C中不是真正的常量
  7. Const 定义的常量不能作为数组的大小

6.typedef和#define都可以抽象化地简化代码,那两者有何区别?

答:

原理不同:

  1. 首先#define是预处理命令,在预处理阶段只是机械的替换带入字符串,并不会左类型检查,
  2. typedef是关键字,作用是给自己的作用域内给一个已经存在的类型起个别名
  3. #define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而typedef有自己的作用域
  4. 对指针的操作不同

7.#include包含中,使用尖括号和双引号有何区别?

答:

对于.#include<头文件>,表示是系统文件,编译会先从标准库路径下搜索,编译器设置的头文件路径-->系统变量

对于#include”头文件”,从当前头文件目录-->编译器设置的头文件路径-->系统变量

8.评价如下的代码?

#define m(a,b) a*b
// #define m(a,b) (a)*(b)//避免出问题最好加上()
int main()
{

    printf("%d\n",m(5,6));
    printf("%d\n",m(5+1,6));//实际是这样的:5+1*6
    return 0;
}

#define sqort(a)  ((a)*(a))
// 实例1: 
int a = 5;
int b = sqort(a++);//( (a++) * (a++) )  先给括号赋值 5之后a再+1=6
printf("%d\n",b);//30
printf("%d\n",a);//7

// 实例2:	
int a = 5;
int b = sqort(++a);//((++a)* (++a)) 
printf("%d\n",b);//49
printf("%d\n",a);//7

答:这里主要是考察++a和后加加的问题:(++a)返回的a的引用(即先自加再引用),(a++)返回的是a加之前的数值(即先应用再自加);

其次,有如下缺点

  1. 无法进行类型检查
  2. 运算优先级问题
  3. 无法调试
  4. 代码膨胀
  5. 无法操作类的私有数据成员

9.简述C代码编译过程?
此外又一个类似的问法:简述GCC的编译步骤和作用?

答:此处简单描述一个test.c如何编译成一个二进制文件test的过程来进行:
1.预处理:宏内容替换,#include内容替换等
        gcc -E test.c -o test.i
2编译:编译为汇编代码,输出汇编代码文件
        gcc -S test.i -o test.s
3.汇编:将汇编代码文件编译为目标文件
        gcc -C test.s -o test.o
4.链接:将目标文件与所需的附加目标文件(静态链接库和动态链接库
标准输入输出库)连接起来,最终生成可执行文件test
        gcc test.o -o test

整个过程我们我们可以用ESC(电脑左上角的按键速记)  和  iso(苹果系统简称速记)。

  1. 预处理(Preprocessing):
  • 预处理阶段是在实际编译之前的一个可选步骤,用于处理源代码中的预处理指令,比如 #include 和 #define
  • 预处理器将处理这些指令,并且可能会包含其他文件、进行宏替换等。
  • 预处理的输出是一个经过处理的源文件,通常以.i.ii为扩展名。
  1. 编译(Compiling):
  • 编译阶段将预处理后的源代码转换为汇编代码(Assembly Code)。
  • 编译器(如GNU Compiler Collection中的gcc)将C源代码翻译成汇编语言。
  • 输出是一个以.s为扩展名的汇编代码文件。
  1. 汇编(Assembling):
  • 汇编阶段将汇编代码转换为机器语言指令。
  • 汇编器(如GNU Assembler中的as)将汇编代码翻译成机器码。
  • 输出是一个以.o.obj为扩展名的目标文件,包含了二进制指令。
  1. 链接(Linking):
  • 链接阶段将多个目标文件(例如,多个C文件分别编译得到的目标文件)合并为一个可执行文件。
  • 链接器(如GNU的ldgcc中的链接器部分)将各个目标文件中的函数和变量引用解析,并创建一个可执行文件。
  • 链接的输出是一个可执行的二进制文件,可以在操作系统上运行。

10.在头文件中是否可以定义静态变量

答:不可以,因为静态变量是有记忆的,不会随函数结束而结束,所以,如果定义在头文件中,那么就会被多个文件开辟空间,浪费资源或者重新出错.

11.简述 #和##的作用?

答:在C语言中,# 和 ## 都是预处理器(preprocessor)操作符,它们与宏定义(#define)一起使用,以在编译前对代码进行文本替换和拼接操作。这些操作符在处理宏定义时特别有用,允许我们创建更灵活和强大的宏。其中
1 、# 操作符
# 操作符用于将其后的宏参数转换为字符串字面量。这在你需要打印变量的名字或者需要用到变量的字符串表示形式时非常有用。

#define STRINGIFY(x) #x

const char* name = STRINGIFY(variable_name); // name 将指向 "variable_name" 的字符串

2.## 操作符
## 操作符是令牌粘贴操作符,用于连接两个令牌(可以是宏参数、标识符等)形成一个单一的令牌。这在需要创建具有动态名称的变量或函数时特别有用。 

#define CONCAT(x, y)    x ## y

int CONCAT(var, 1) = 10; // 这将定义一个名为 var1 的整数变量,并初始化为 10 

综上,简单的说:

#利用宏,将参数实现字符串化 ;##是运算符粘合剂,组合成一个变量,强制分隔。

用例:

// #利用宏参数字符串化
#define ARGV(x) printf(""#x" is %s\n",#x) //表示把参数x解释为字符串
int a = 5;
ARGV(a); //a is a

// ##运算符粘合剂 组合成一个变量,强制分隔
#define targ( n )   X##n   //表示X1......或Xn
int main()
{
   int a = 5;
   // ARGV(a);
   int targ(1) = 10;//表示 X1 =10
   printf("%d\",X1);//10
   return 0
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
预处理预处理预处理指令预处理指令是预处理指令是用预处理指令是用于预处理指令是用于在预处理指令是用于在程序预处理指令是用于在程序编预处理指令是用于在程序编译预处理指令是用于在程序编译之预处理指令是用于在程序编译之前预处理指令是用于在程序编译之前对预处理指令是用于在程序编译之前对源预处理指令是用于在程序编译之前对源代码预处理指令是用于在程序编译之前对源代码进行预处理指令是用于在程序编译之前对源代码进行处理预处理指令是用于在程序编译之前对源代码进行处理的预处理指令是用于在程序编译之前对源代码进行处理的指预处理指令是用于在程序编译之前对源代码进行处理的指令预处理指令是用于在程序编译之前对源代码进行处理的指令。预处理指令是用于在程序编译之前对源代码进行处理的指令。在预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行效预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行效率预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行效率和预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行效率和代码预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行效率和代码的预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行效率和代码的可预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行效率和代码的可读预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行效率和代码的可读性预处理指令是用于在程序编译之前对源代码进行处理的指令。在代码中使用预处理指令可以方便地定义宏常量、包含头文件、条件编译等操作。预处理指令通常以符号“#”开始,不同的指令有不同的功能。使用预处理指令可以提高程序的运行效率和代码的可读性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoxilang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值