014-预处理指令-C语言笔记

学习目标

1.【掌握】枚举

2.【掌握】typedef关键字

3.【理解】预处理指令

4.【掌握】#define宏定义

5.【掌握】条件编译

6.【掌握】static与extern关键字

一、枚举

当我们要描述方向、四季、性别、学历、婚配情况等等事物的时候,我们知道这些事物的取值范围是非常有限的。比如,性别取值就男、女,四季取值就春、夏、秋、冬。类似这样的需求,C语言提供了一种构造类型枚举专门针对此类需求,由程序员自己声明一种新的数据类型,并给这个新的数据类型声明几个固定枚举值。同时,声明这个新的数据类型的变量时,给变量赋值的取值范围就只能赋值我们类型里声明的某个固定枚举值。

枚举类型的声明

语法:enum 枚举名 {枚举值1,枚举值2,...};

 
1
enumGender{Genderwomen,Genderman};//声明一个枚举类型,他的取值是women和man

枚举变量的声明

语法:enum 枚举名 变量名;

 
1
2
enumGender{Genderwomen,Genderman};//声明一个枚举类型,他的取值是women和man
enumGendergender;//声明一个enum Gender类型的变量gender

变量的初始化

语法:enum 枚举名 变量名 = 枚举值;

 
1
2
enumGender{Genderwomen,Genderman};//声明一个枚举类型,他的取值是women和man
enumGendergender=Genderwomen;//声明一个enum Gender类型的变量gender并初始化为women

枚举值对应的整型数值

 
1
2
3
4
5
6
enumGender{Genderwomen,Genderman};//声明一个枚举类型,他的取值是women和man
enumGendergender=Genderwomen;//声明一个enum Gender类型的变量gender并初始化为women
printf("gender = %d\n",gender);//打印出gender = 0
gender=Genderman;//重新把枚举变量赋值man
printf("gender = %d\n",gender);//打印出gender = 1
gender=0;//这样赋值也是可以的,相当于赋值为women,但是不直观不好看.不推荐这种赋值

注意:

1.枚举也是一种数据类型,类型名是(enum 枚举名),必须要加上enum关键字啊。

2.给枚举变量初始化或者赋值的时候,取值只能取枚举类型的枚举值中的某个值,不能随意赋值。

3.每一个枚举值对应都有一个整型数值的,从第一个枚举值开始从0依次递增。

4.声明一个枚举变量的时候,这个变量里面存的实际上是这个枚举值对应的整型,而不是枚举值本身。

5.枚举值命名最好能加上区分这个枚举值属于哪个枚举类型的标示,比如在枚举值前面加上枚举类型名。

实际应用:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
//性别枚举类型
enumGender{Genderwomen,Genderman};
 
structPerson{
    char*name;//姓名
    intage;//年龄
    enumGendergender;//性别
};
intmain(intargc,constchar*argv[]){
    
    structPersonlaoWang={"老王",18,Genderman};
    printf("name = %s\nage = %d\ngender = %s\n",laoWang.name,laoWang.age,laoWang.gender==0?"男":"女");
    /*打印出
     name = 老王
     age = 18
     gender = 女
     */
    return0;
}

二、typedef关键字

如果你感觉有些数据类型太长,难以记忆难以书写,我们可以使用typedef关键字为各种数据类型定义一个新名字(别名)。

语法:typedef 数据类型 别名;

typedef与普通数据类型

 
1
2
typedefintInteger;
Integernum=10;//等同于int num = 10;

typedef与指针

 
1
2
3
typedefchar*String;
Stringstr="字符串";//等同于char *str = "字符串";
printf("str = %s\n",str);//打印 str = 字符串

typedef与结构体

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//1.先声明结构体,再给结构体定义一个别名
structPerson{
    char*name;
    intage;
};
typedefstructPersonPerson;//给struct Person定义一个别名
Personperson={"老王",18};//等同于 struct Person person = {"老王",18};
printf("姓名:%s\t年龄:%d\n",person.name,person.age);
 
//2.声明的结构体的同时给结构体定义一个别名
typedefstructPerson{
    char*name;
    intage;
}Person;
Personperson={"老王",18};//等同于 struct Person person = {"老王",18};
printf("姓名:%s\t年龄:%d\n",person.name,person.age);
 
//3.声明的结构体的同时给结构体定义一个别名,可以省略结构体名
typedefstruct{
    char*name;
    intage;
}Person;
Personperson={"老王",18};//等同于 struct Person person = {"老王",18};
printf("姓名:%s\t年龄:%d\n",person.name,person.age);

typedef与指向结构体的指针

 
1
2
3
4
5
6
7
8
9
10
11
12
//声明一个结构体并定义别名
typedefstruct{
char*name;
intage;
}Person;
 
typedefPerson*PP;//给指向结构体的指针取别名
PersonlaoWang={"老王",18};//声明结构体变量
PPp=&laoWang;// 声明指针变量指向结构体变量
 
//利用指针变量访问结构体成员
printf("name = %s  age = %d\n",p->name,p->age);

typedef与枚举

 
1
2
3
4
5
6
7
8
9
10
11
12
//1.先声明枚举类型,再给枚举类型定义别名
enumGender{Genderman,Genderwomen};
typedefenumGenderGender;//给枚举类型起别名
Gendergender=Genderman;//声明枚举变量
 
//2.声明枚举类型的同时定义别名
typedefenumGender{Genderman,Genderwomen}Gender;
Gendergender=Genderman;//使用别名声明枚举变量
 
//3.声明枚举类型的同时定义别名,枚举名也是可以省略的
typedefenum{Genderman,Genderwomen}Gender;
Gendergender=Genderman;//使用别名声明枚举变量

typedef与指向函数的指针

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义一个sum函数,计算a跟b的和
intsum(inta,intb){
    intc=a+b;
    returnc;
}
typedefint(*MySum)(int,int);//这里别名是MySum
 
intmain(intargc,constchar*argv[]){
    
    MySump=sum;//声明一个指向sum函数的指针变量p
    intresult=p(4,5);// 利用指针变量p调用sum函数
    
    printf("result = %d\n",result);//打印 result = 9
    return0;
}

三、预处理指令

让我们来回顾一下C程序从编写源代码到执行需要的步骤。先编写符合C语言语法规范的源代码文件,然后编译成二进制代码的目标文件,再然后会进行链接,最终生成可执行文件。其实在编译之前,还有一个很重要的步骤,系统会自动执行,那就是执行预处理指令。预处理指令是在编译之前执行的,我们已经学过#include文件包含指令,今天我们再来整几发指令。

四、#define宏定义

在程序编译之前,会把使用宏名的地方替换成宏值的内容。注意这里的替换是纯洁的替换,无论宏值是表达式还是数值,甚至是错误代码,都会原模原样的替换。

语法:#define 宏名 宏值

无参数的宏:使用无参数宏的时候,只是纯粹的文本替换

 
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
#include <stdio.h>
#define LOG printf("这里是LOG\n")
#define SUM a+b
#define AREA PI*r*r  //宏值可以使用宏名,在使用地方的后面也可以
#define PI 3.14
 
intmain(intargc,constchar*argv[]){
    
    doublenum=PI*3*3;//等同于double num = 3.14 * 3 * 3;
    printf("num = %.2lf\n",num);
    
    LOG;//等同于printf("这里是LOG\n");
    
    inta=1,b=1;//如果宏值中有变量,必须先声明再使用
    intresult=SUM*2;//等同于int result = a + b * 2;  注意替换后的运算符优先级
    printf("result = %d\n",result);
    
    intr=2;//半径为2
    floatarea=AREA;
    printf("area = %.2f\n",area);
    
#undef PI //取消宏 PI ,在之后就不能替换宏值了
    
    return0;
}

有参数的宏:使用有参数宏的时候,需要调用宏的人传入一个值作为宏值的一部分

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#define Log(str) printf(str)
#define SUM(a,b) a+b //这里的参数就是宏名和宏值里相同的部分
 
intmain(intargc,constchar*argv[]){
    
    Log("有参数宏\n");//等同于printf("有参数宏");
    
    intnum1=10,num2=20;
    
    //这里传入的参数是num1和num2,并不是10和20。代码执行的时候才会为num1,num2赋值10,20
    intsum=SUM(num1,num2);//等同于int sum = a + b;
    printf("sum = %d\n",sum);
    
    return0;
}

注意:

1.当宏值是一个表达式,宏值的语法错误不会报错,因为检查语法是在编译的时候干的。

2.当宏值是一个表达式,替换宏名也是替换源代码中使用宏名的地方,所以特别注意替换后的运算符优先级问题。

3.宏值当中如果有变量,使用宏值之前必须要先声明这个变量。

4.如果双引号中出现了宏名,其实这个不是宏名,只是和宏名很像的字符串。

5.宏可以定义在任何地方,能被使用的作用域是从定义开始到文件结束。

五、条件编译

在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译,这就是条件编译。

语法:

 
1
2
3
4
5
6
7
#if 条件1
    代码1
#elif 条件2
    代码2
#else
    代码3
#endif

执行顺序:如果条件1满足,则代码1参与编译。如果条件1不满足,条件2满足,则代码2参与编译。如果条件1和条件2都不满足,则执行代码3。

第一种情况,判断宏值是否满足条件

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#define Flag 1
 
//这里Score只能使用宏,不能使用变量
#if Flag == 1
#define SUM(a,b) a+b
#else
#define SUM(a,b) a-b
#endif
 
intmain(){
    
    intresult=SUM(20,10);//如果Flag为1则相加,否则相减
    printf("result = %d\n",result);
    
    return0;
}

第二种情况,判断一个宏有被定义

 
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#define Score 5
 
intmain(intargc,constchar*argv[]){
#ifdef Score   //等同于 #if defined(Score)
    printf("已经定义了\n");
#else
    printf("没有定义\n");
#endif
    return0;
}

第三种情况,判断一个宏没有被定义

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef ___AAAA____     //等同于 #if !defined(Score)
#define ___AAAA____
//#include <stdio.h>
//#include "xxxxx.h"
//#include "aaaaa.h"
//#include "zzzzz.h"
//#include "vvvvv.h"
//这里是一百多个头文件。。
/*
这里表示如果没有定义宏名为___AAAA____的宏,就定义一个,然后包含进来若干头文件。
然并卵?
 
当其他文件包含本文件多次的时候,每次都会判断是否定义了___AAAA____。如果没有定义才包含,定义过了就算了
*/
#endif

注意:

1.条件编译一定要使用宏进行条件判断,不能使用变量,因为变量的值是在程序执行的时候才赋值的。

2.#endif表示这个条件编译结束,一定不能少,不然会发生一些不可预料的事情。

3.条件编译只会编译符合条件的那一个分支编译成二进制代码。

六、static与extern关键字

static与函数

如果一个函数被static关键字修饰,只能在当前模块中使用,就相当于内部函数咯。

extern与函数

如果一个函数被extern关键字修饰,可以在当前模块中使用,也能被其他模块共享,不过默认函数就是被extern修饰的,可以省略不写。

static与变量

被static修饰的局部变量会声明在常量区中,函数执行完毕后不会被释放,只有程序执行结束才会释放。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
voidtest(){
    staticintnum=10;
    num++;
    printf("num = %d\n",num);
}
intmain(){
    
    for(inti=0;i<5;i++){
        test();//执行5次test函数
    }
    /*打印出
     num = 11
     num = 12
     num = 13
     num = 14
     num = 15
     */
    return0;
}

被static关键字修饰的全局变量只能在当前模块中使用,不能被其他模块共享,相当于私有全局变量。

main.c文件

 
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include "test.h"
voidtest(){
    num++;
    printf("num = %d\n",num);
}
intmain(){
    for(inti=0;i<5;i++){
        test();
    }
    return0;
}

test.h文件

 
1
staticintnum;

test.c文件

 
1
2
#include "test.h"
staticintnum=100;

extern与变量

extern不能修饰局部变量,被extern修饰的全局变量可以在当前模块中使用,也能被其他模块共享。不过默认全局变量就是extern修饰的,所以我们可以省略(Xcode6.3前是默认加extern的,Xcode6.3后必须自己在声明里加上extern,但定义的地方可以不写)。

main.c文件

 
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include "test.h"
voidtest(){
    num++;
    printf("num = %d\n",num);
}
intmain(){
    for(inti=0;i<5;i++){
        test();
    }
    return0;
}

test.h文件

 
1
externintnum;

test.c文件

 
1
2
#include "test.h"
externintnum=100;

014-预处理指令-C语言笔记的更多相关文章

  1. 预处理指令--C语言

    ANSI标准C还定义了如下几个宏: __LINE__ 表示正在编译的文件的行号 __FILE__ 表示正在编译的文件的名字 __DATE__ 表示编译时刻的日期字符串,例如:"25 Dec ...

  2. c语言学习之基础知识点介绍(九):预处理指令和多文件开发

    一:预处理指令 /* 预处理指令: 作用:在程序编译之前做一些操作. 预处理命令写法的共同特点:都是以#号开头. 文件包含指令:#include 是一个文件包含命令. 作用:把某个文件内的内容读取出来 ...

  3. C语言预处理指令的初步了解

    所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作.预处理是C语言的一个重要功能,它由预处理程序负责完成.当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分 ...

  4. c语言的预处理指令分3种   1> 宏定义   2> 条件编译   3> 文件包含

    宏简介 1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译 所有的预处 ...

  5. Docs-.NET-C#-指南-语言参考-预处理器指令:#if 预处理指令

    ylbtech-Docs-.NET-C#-指南-语言参考-预处理器指令:#if 预处理指令 1.返回顶部 1. #if(C# 参考) 2018/06/30 如果 C# 编译器遇到 #if 指令,最终是 ...

  6. C语言系列之预处理指令、循环左移函数的使用(四)

    本章节将讲两个知识点 第一个知识点:常用的预处理指令 第二个知识点:循环左移右移函数 第一个知识点:预处理指令 一种预处理指令是#define,他把名字A定义为P0,当这个名字出现在源文件的任何地方时 ...

  7. C#学习笔记(十五):预处理指令

    C#和C/C++一样,也支持预处理指令,下面我们来看看C#中的预处理指令. #region 代码折叠功能,配合#endregion使用,如下: 点击后如下: 条件预处理 条件预处理可以根据给出的条件决 ...

  8. C学习笔记之预处理指令

          一.什么是预处理指令            预处理指令是告诉编译器在编译之前预先处理的一些指令,有宏定义,文件包括,条件编译. 预处理指令一般以 # 号开头,能够出如今文件的不论什么地方, ...

  9. c语言学习之基础知识点介绍(二十):预处理指令

    一.预处理指令的介绍 预处理命令:在编译之前触发的一系列操作(命令)就叫预处理命令. 特点:以#开头,不要加分号. #include: 文件包含指令 把指定文件的内容复制到相应的位置 #define: ...

随机推荐

  1. 13.unittest扩展

  2. 居家办公之后,鹅厂小哥发现Web开发竟能助力身体健康!

    疫情特殊时期,各企业.学校纷纷启用远程办公和学习的方式,在办公期间,除了要完成日常工作安排,还要照顾自身的饮食起居,在高强度的工作节奏和缺少运动锻炼的情况之下,如何及早发现健康隐患,快乐办公?且看鹅厂 ...

  3. Mol Cell Proteomics. | MARMoSET – Extracting Publication-ready Mass Spectrometry Metadata from RAW Files

    本文是马克思普朗克心肺研究所的三名研究者Marina Kiweler.Mario Looso和Johannes Graumann发表在8月刊的MCP的一篇文章. 由于Omics实验经常涉及数百个数据文 ...

  4. golang超级mapper包 - coven

    coven介绍 你可以把它理解成.NET 的 AutoMapper,java的modelmapper 一个快速的转换器去,支持结构到结构,切片到切片和映射到映射非反射转换,类型与嵌套指针支持. 不支持 ...

  5. JDBC怎么连接数据库

    1:注册驱动:class.forName("com.mysql.jdbc.Driver"); 2:连接数据库:DriverManager.getConnection(url , u ...

  6. drf 权限认证

    目录 复习 前期准备 三大认证简介 AbstracUser源码分析 自定义User下的权限六表 models.py 到settings.py中注册 注意点: 执行数据迁移的俩条命令 创建超级用户 t_ ...

  7. 四则运算APP版

    (一)四则运算APP版 这这个Demo的目的就是强化一下本周学习的Android的Jetpack里的新内容,接下来我将通过这个Demo来展示我所学到的新知识. 先列出新学到的知识:ViewModel, ...

  8. 12c OCR corrupted results in CRS stack down.

    12c OCR corrupted results in CRS stack down. 1. check crsd.trc2017-03-21 16:14:44.667838 :  CRSOCR:2 ...

  9. LLVM 编码规范 - 中文翻译

    LLVM 编码规范 导论 语言.库和标准 C++ 标准版本 C++ 标准库 Go 代码准则 机械的代码问题 代码格式化 注释 头文件 类概述 method information 注释格式化 使用Do ...

  10. [tyvj2032]升降梯上<dp&spfa>

    题目背景 开启了升降梯的动力之后,探险队员们进入了升降梯运行的那条竖直的隧道,映入眼帘的是一条直通塔顶的轨道.一辆停在轨道底部的电梯.和电梯内一杆控制电梯升降的巨大手柄. 题目描述 Nescafe 之 ...