学习目标
1.【掌握】枚举程序员
2.【掌握】typedef关键字web
3.【理解】预处理指令ide
4.【掌握】#define宏定义函数
5.【掌握】条件编译学习
6.【掌握】static与extern关键字spa
1、枚举
当咱们要描述方向、四季、性别、学历、婚配状况等等事物的时候,咱们知道这些事物的取值范围是很是有限的。好比,性别取值就男、女,四季取值就春、夏、秋、冬。相似这样的需求,C语言提供了一种构造类型枚举专门针对此类需求,由程序员本身声明一种新的数据类型,并给这个新的数据类型声明几个固定枚举值。同时,声明这个新的数据类型的变量时,给变量赋值的取值范围就只能赋值咱们类型里声明的某个固定枚举值。指针
枚举类型的声明code
语法:enum 枚举名 {枚举值1,枚举值2,...};blog
1
|
enum
Gender
{
Genderwomen
,
Genderman
}
;
//声明一个枚举类型,他的取值是women和man
|
枚举变量的声明ci
语法:enum 枚举名 变量名;
1
2
|
enum
Gender
{
Genderwomen
,
Genderman
}
;
//声明一个枚举类型,他的取值是women和man
enum
Gender
gender
;
//声明一个enum Gender类型的变量gender
|
变量的初始化
语法:enum 枚举名 变量名 = 枚举值;
1
2
|
enum
Gender
{
Genderwomen
,
Genderman
}
;
//声明一个枚举类型,他的取值是women和man
enum
Gender
gender
=
Genderwomen
;
//声明一个enum Gender类型的变量gender并初始化为women
|
枚举值对应的整型数值
1
2
3
4
5
6
|
enum
Gender
{
Genderwomen
,
Genderman
}
;
//声明一个枚举类型,他的取值是women和man
enum
Gender
gender
=
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>
//性别枚举类型
enum
Gender
{
Genderwomen
,
Genderman
}
;
struct
Person
{
char
*name
;
//姓名
int
age
;
//年龄
enum
Gender
gender
;
//性别
}
;
int
main
(
int
argc
,
const
char
*
argv
[
]
)
{
struct
Person
laoWang
=
{
"老王"
,
18
,
Genderman
}
;
printf
(
"name = %s\nage = %d\ngender = %s\n"
,
laoWang
.
name
,
laoWang
.
age
,
laoWang
.
gender
==
0
?
"男"
:
"女"
)
;
/*打印出
name = 老王
age = 18
gender = 女
*/
return
0
;
}
|
2、typedef关键字
若是你感受有些数据类型太长,难以记忆难以书写,咱们可使用typedef关键字为各类数据类型定义一个新名字(别名)。
语法:typedef 数据类型 别名;
typedef与普通数据类型
1
2
|
typedef
int
Integer
;
Integer
num
=
10
;
//等同于int num = 10;
|
typedef与指针
1
2
3
|
typedef
char
*
String
;
String
str
=
"字符串"
;
//等同于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.先声明结构体,再给结构体定义一个别名
struct
Person
{
char
*name
;
int
age
;
}
;
typedef
struct
Person
Person
;
//给struct Person定义一个别名
Person
person
=
{
"老王"
,
18
}
;
//等同于 struct Person person = {"老王",18};
printf
(
"姓名:%s\t年龄:%d\n"
,
person
.
name
,
person
.
age
)
;
//2.声明的结构体的同时给结构体定义一个别名
typedef
struct
Person
{
char
*name
;
int
age
;
}
Person
;
Person
person
=
{
"老王"
,
18
}
;
//等同于 struct Person person = {"老王",18};
printf
(
"姓名:%s\t年龄:%d\n"
,
person
.
name
,
person
.
age
)
;
//3.声明的结构体的同时给结构体定义一个别名,能够省略结构体名
typedef
struct
{
char
*name
;
int
age
;
}
Person
;
Person
person
=
{
"老王"
,
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
|
//声明一个结构体并定义别名
typedef
struct
{
char
*name
;
int
age
;
}
Person
;
typedef
Person
*
PP
;
//给指向结构体的指针取别名
Person
laoWang
=
{
"老王"
,
18
}
;
//声明结构体变量
PP
p
=
&
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.先声明枚举类型,再给枚举类型定义别名
enum
Gender
{
Genderman
,
Genderwomen
}
;
typedef
enum
Gender
Gender
;
//给枚举类型起别名
Gender
gender
=
Genderman
;
//声明枚举变量
//2.声明枚举类型的同时定义别名
typedef
enum
Gender
{
Genderman
,
Genderwomen
}
Gender
;
Gender
gender
=
Genderman
;
//使用别名声明枚举变量
//3.声明枚举类型的同时定义别名,枚举名也是能够省略的
typedef
enum
{
Genderman
,
Genderwomen
}
Gender
;
Gender
gender
=
Genderman
;
//使用别名声明枚举变量
|
typedef与指向函数的指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 定义一个sum函数,计算a跟b的和
int
sum
(
int
a
,
int
b
)
{
int
c
=
a
+
b
;
return
c
;
}
typedef
int
(
*MySum
)
(
int
,
int
)
;
//这里别名是MySum
int
main
(
int
argc
,
const
char
*
argv
[
]
)
{
MySum
p
=
sum
;
//声明一个指向sum函数的指针变量p
int
result
=
p
(
4
,
5
)
;
// 利用指针变量p调用sum函数
printf
(
"result = %d\n"
,
result
)
;
//打印 result = 9
return
0
;
}
|
3、预处理指令
让咱们来回顾一下C程序从编写源代码到执行须要的步骤。先编写符合C语言语法规范的源代码文件,而后编译成二进制代码的目标文件,再而后会进行连接,最终生成可执行文件。其实在编译以前,还有一个很重要的步骤,系统会自动执行,那就是执行预处理指令。预处理指令是在编译以前执行的,咱们已经学过#include文件包含指令,今天咱们再来整几发指令。
4、#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
int
main
(
int
argc
,
const
char
*
argv
[
]
)
{
double
num
=
PI
*
3
*
3
;
//等同于double num = 3.14 * 3 * 3;
printf
(
"num = %.2lf\n"
,
num
)
;
LOG
;
//等同于printf("这里是LOG\n");
int
a
=
1
,
b
=
1
;
//若是宏值中有变量,必须先声明再使用
int
result
=
SUM
*
2
;
//等同于int result = a + b * 2; 注意替换后的运算符优先级
printf
(
"result = %d\n"
,
result
)
;
int
r
=
2
;
//半径为2
float
area
=
AREA
;
printf
(
"area = %.2f\n"
,
area
)
;
#undef PI //取消宏 PI ,在以后就不能替换宏值了
return
0
;
}
|
有参数的宏:使用有参数宏的时候,须要调用宏的人传入一个值做为宏值的一部分
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 //这里的参数就是宏名和宏值里相同的部分
int
main
(
int
argc
,
const
char
*
argv
[
]
)
{
Log
(
"有参数宏\n"
)
;
//等同于printf("有参数宏");
int
num1
=
10
,
num2
=
20
;
//这里传入的参数是num1和num2,并非10和20。代码执行的时候才会为num1,num2赋值10,20
int
sum
=
SUM
(
num1
,
num2
)
;
//等同于int sum = a + b;
printf
(
"sum = %d\n"
,
sum
)
;
return
0
;
}
|
注意:
1.当宏值是一个表达式,宏值的语法错误不会报错,由于检查语法是在编译的时候干的。
2.当宏值是一个表达式,替换宏名也是替换源代码中使用宏名的地方,因此特别注意替换后的运算符优先级问题。
3.宏值当中若是有变量,使用宏值以前必需要先声明这个变量。
4.若是双引号中出现了宏名,其实这个不是宏名,只是和宏名很像的字符串。
5.宏能够定义在任何地方,能被使用的做用域是从定义开始到文件结束。
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
int
main
(
)
{
int
result
=
SUM
(
20
,
10
)
;
//若是Flag为1则相加,不然相减
printf
(
"result = %d\n"
,
result
)
;
return
0
;
}
|
第二种状况,判断一个宏有被定义
1
2
3
4
5
6
7
8
9
10
11
|
#include <stdio.h>
#define Score 5
int
main
(
int
argc
,
const
char
*
argv
[
]
)
{
#ifdef Score //等同于 #if defined(Score)
printf
(
"已经定义了\n"
)
;
#else
printf
(
"没有定义\n"
)
;
#endif
return
0
;
}
|
第三种状况,判断一个宏没有被定义
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.条件编译只会编译符合条件的那一个分支编译成二进制代码。
6、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>
void
test
(
)
{
static
int
num
=
10
;
num
++
;
printf
(
"num = %d\n"
,
num
)
;
}
int
main
(
)
{
for
(
int
i
=
0
;
i
<
5
;
i
++
)
{
test
(
)
;
//执行5次test函数
}
/*打印出
num = 11
num = 12
num = 13
num = 14
num = 15
*/
return
0
;
}
|
被static关键字修饰的全局变量只能在当前模块中使用,不能被其余模块共享,至关于私有全局变量。
main.c文件
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
#include "test.h"
void
test
(
)
{
num
++
;
printf
(
"num = %d\n"
,
num
)
;
}
int
main
(
)
{
for
(
int
i
=
0
;
i
<
5
;
i
++
)
{
test
(
)
;
}
return
0
;
}
|
test.h文件
1
|
static
int
num
;
|
test.c文件
1
2
|
#include "test.h"
static
int
num
=
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"
void
test
(
)
{
num
++
;
printf
(
"num = %d\n"
,
num
)
;
}
int
main
(
)
{
for
(
int
i
=
0
;
i
<
5
;
i
++
)
{
test
(
)
;
}
return
0
;
}
|
test.h文件
1
|
extern
int
num
;
|
test.c文件
1
2
|
#include "test.h"
extern
int
num
=
100
;
|