學習目標
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
;
|