天天看点

c语言宏定义在预处理阶段,预处理和宏定义

c语言宏定义在预处理阶段,预处理和宏定义

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

宏定义

基本概念预处理部分: #include #define 放在函数之外,一般都放在源文档的前面

预处理由预处理进程负责,当对一个源文档进行编译时,系统将自动引用预处理进程对源进程中的预处理部分作处理,处理完毕将自动进入对源进程的编译。

C语言提供了多种预处理功能,如宏定义、文档包含、条件编译等。

不带参数的宏定义被定义为 宏 的标识符称为 宏名。 在编译阶段对进程中所有出现的 宏名 都用宏定义中的字符串去代换

宏定义由源进程中的宏定义命令完成的。宏代换是由预处理进程自动完成的。

c语言中 宏分为 有参数 和无参数两种。1

2

3

4

5

6

7#define 标识符 字符串

以 # 开头的均为预处理命令

define 为预处理命令

标识符 为所定义的宏名

字符串 可以是常数、表达式、格式串等

宏使用时的注意事项习惯上宏名用大写字母表示,以便于与变量区别,但是也允许使用小写字母

宏定义是用宏名表示一个字符串(可以是任何字符,常数,表达式),在宏展开时又以该字符串取代宏名。

预处理进程不做检查,在宏展开编译的时候发现

宏定义不是说明或语句,末尾不用加 分号“;” 加“;” 连同“;”一起置换

宏定义必须写在函数之外,默认的有效区到源进程结束, 要是提前结束 使用 #undef 命令1

2#undef PI

宏名在源进程中使用双引号括起来,则预处理进程不对其作宏代换1

2

3

4

5

6

7#define H "hello world"

int main(){

printf(H);

printf("n%sn",H);

printf("Hn"); // H

}

宏定义允许嵌套,在宏展开时由预处理进程层层代换1

2

3#define PI 3.1415926

#define R 5

#define SUM 2*PI*R

可用宏定义表示数据类型,使书写方便

对输出格式做宏定义,减少书写麻烦

#define 和 typedef的区别注意用宏定义表示数据类型和用typedef定义数据说明符的区别。宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,不是作简单的代换,而是对类型说明符重新命名,被命名的标识符具有类型定义说明的功能。1

2

3

4

5

6

7

8

9

10#define PIN1 int *

typedef int * PIN2;

PIN1 a,b

a = &m; // OK int *a; a = &m;

b = &n; // ERROE int b; b = &n;

PIN2 c,d;

c = &m; // OK int *c; c = &m;

d = &n; // OK int *d; d = &n;

带有参数的宏定义有参宏的定义方法

C语言中允许带有参数,在宏定义中的参数成为形式参数,在宏调用中的参数称为实际参数。

对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。1

2

3

4

5

6

7

8

9

10

11// 带参数的宏定义的一般形式为:

#define 宏名(形参表) 字符串

// 带参宏调用的一般形式为:

宏名(实参表)

eg:

// 宏定义

#define M(y) y*y+3*y

// 宏调用

M(5);

有参宏的注意事项带参宏定义中,形参之间可以出现空格,但是宏名和形参表之间不能有空格出现。

带参宏定义中,形参不分配内存单元,实参有具体值,必须作类型说明,带参宏定义只是符号代换,不存在值传递。而函数中形参和实参是两个不同的量,有各自的作用域,调用时进行 值传递

在宏定义中形参是标识符,而宏调用中的实参可以是表达式

在宏定义中,字符串中的形参通常要用括号括起来避免出错。

宏定义可以用来定义多个语句。在宏调用时,把这些语句又代换到源进程内。

条件编译

为什么要用条件编译?按不同条件去编译不同的进程部分,因而产生不同的目标代码文档。有利于进程的移植和调试。

可以使用条件语句来实现条件编译。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28// 第一种形式

#if 常量表达式

进程段1

#elif

进程段2

#else

进程段3

#endif

条件编译后面的表达式中不能出现变量,只能识别常量和宏定义

// 第二种形式

#ifdef 标识符

进程段1

#else

进程段2

#elseif

// 如果标识符已被#define命令定义过则对进程段1进行编译;否则对进程段2进行编译。

// 第三种形式

#ifndef 标识符

进程段1

#else

进程段2

#endif

// 如果标识符未被 #define 命令定义, 则对进程段1进行编译,否则对进程段2进行编译。

预处理

什么是预处理?以 # 开头的代码行

#号必须是该行除了任何空白字符外的第一个字符。

#后是命令关键字,在关键字和#号之间允许存在任意个数的空白字符。

整行语句构成了一条预处理命令,该命令将在编译器进行编译之前对源代码做某些转换

常见的预处理命令命令说明#空命令,无任何效果

#include包含一个源代码文档

#define定义宏

#undef取消已定义的宏

#if如果给定条件为真,编译下面的代码

#ifdef如果宏已经定义,编译下面的代码

#ifndef如果宏没有定义,编译下面的代码

#elif#if不为真,当前条件为真,编译下面的代码

#endif结束条件编译块

static和extern的使用它们都是用来修饰变量(局部的static实际也是全局)

static修饰的变量 只有你的包含那个变量定义的源代码文档可以访问

extern定义的变量 是任何一个源文档都可以访问,只要声明了就可以

static与extern对局部变量的作用static对局部变量的作用延长局部变量的生命周期,从进程启动到进程退出,但是它没有改变 变量的 作用域

定义变量的代码在整个进程运行期间仅仅会执行一次1

2

3

4

5

6

7

8

9

10

11

12

13

14void add(){

static int a = 10; // int a = 10;

a++;

printf("a++: %dn",a);

}

int main() {

add(); // 11

add(); // 12

add(); // 13

return 0;

}

// extern 不是定义局部变量,它是在函数内部声明的全局变量

static与extern对全局变量的作用全局变量内部变量:只能在本文档中访问的变量

外部变量:可以在其他中访问的变量,默认所有全局变量都是外部变量static作用声明一个内部变量

定义一个内部变量

注意:在不同文档中可以定义同名的内部变量如果声明的时候没有写extern那系统会自动定义这个变量,并将其初始化为0

如果你使用extern来声明一个变量,如果没有你定义的,那么系统就会报错

static与extern对函数的作用内部函数: 在A文档中定义的函数,在A文档中访问

外部函数: 在B文档中访问A文档中定义的函数,函数在B中称之为外部函数

全局变量&局部变量常见问题

局部变量能否和全局变量重名?能,局部会屏蔽全局,要用全局变量,需要使用”::”(域运算符)

如何引用一个已经定义过的全局变量?extern, 可以用引用头文档的方式,也可以用extern关键字,使用引入头文档方式(有错误会在编译阶段报错),使用extern方式引用时(有错误会在连接期间报错)

全局变量可以定义在被多个.c文档包含的头文档中?可以,在不同的C文档中以static形式来声明同名全局变量

static全局变量和普通的全局变量区别static全局变量只初始化一次,防止在其他文档单元中被引用.

static局部变量和普通的局部变量区别static局部变量只被初始化一次,下一次依据上一次的结果值.

static函数和普通函数区别static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝.

进程的局部变量存在于(堆栈)中,全局变量存在于(静态区)中,动态申请数据存在于(堆)中