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函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝.
进程的局部变量存在于(堆栈)中,全局变量存在于(静态区)中,动态申请数据存在于(堆)中