做者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。 转载请注明出处。 原文:www.jianshu.com/p/39b41aa5c…程序员
《C语言探索之旅》全系列编程
上一课是 C语言探索之旅 | 第二部分第五课:预处理 ,应该是比较轻松的。数组
这一课将会很是使人激动也颇有意思,不过有些难度。bash
众所周知,C语言是面向过程的编程语言,与 Java,C++,等面向对象的编程语言有所不一样。微信
在面向对象的编程语言中,有类(class)的概念。less
C语言是没有类这种“类型”的,可是 C语言就不能“模拟”面向对象编程了吗?编程语言
不,只要你设计得好,C语言也能够模拟面向对象编程。函数
这一课咱们要学习的 struct(结构体)的知识就可使你有能力用 C语言实现“面向对象”。学习
前面咱们学习了指针,数组,字符串和预处理,掌握这些知识你的 C语言水平已经还不错啦。可是咱们岂能就此止步,必须 Bigger than bigger~测试
除了使用 C语言已经定义的变量类型,咱们还能够作一些更厉害的事情:建立你本身的变量类型。
咱们能够将其称为“自定义的变量类型”,咱们来看三种:struct,union 和 enum。
由于当你须要编写比较复杂的程序时,你会发现建立自定义的变量类型是很重要的。
幸亏,这学起来其实也不是特别难。可是你们须要专心学习这一课,由于从下一课开始,咱们会一直用到 struct 了。
什么是 struct 呢?
struct 是 structure(表示“结构”)的缩写,因此 struct 的专业术语是“结构体”。
定义:struct 就是一系列变量的集合,可是这些变量能够是不一样类型的。
这个定义是否是唤起了你们对咱们的老朋友数组的怀念啊?数组里面的每一个成员都必须是同一个类型的,相比之下 struct 更灵活。
通常来讲,咱们习惯把 struct 定义在 .h 头文件中,也就是和预处理命令以及函数原型那群“家伙”在一块儿。
下面就给出一个 struct 的例子:
struct 你的struct的名字
{
char variable1;
short variable2;
int otherVariable;
double numberDecimal;
};
复制代码
能够看到:struct 的定义以关键字 struct 开始,后面接你自定义的 struct 的名称(好比 Dog,Cat,Person,等)。
通常来讲,在个人代码里,个人 struct 的命名也是遵守变量的命名规则,惟有一点不同,就是 struct 的名称我会将首字母大写,例如:SchoolName。
可是个人普通变量通常都是首字母小写,例如:studentNumber。
这样作只是我的习惯,便于在代码里区分普通的变量和自定义的变量,以后会学到的 enum 和 union,我也是习惯将其名称的首字母大写。
在 struct 的名字以后,咱们须要写上一对大括号,在这对大括号里面写入你的 struct 要包含的各类类型的变量。
一般说来,struct 的大括号内至少得定义两个变量吧。若是只有一个变量,那定义这个结构体也没什么意义。
注意:不要忘了,在大括号后面还要加上一个分号(
;
), 由于毕竟这个 struct 是一个变量,变量的定义最后都要加分号的。
如你所见,建立一个自定义的变量也不复杂么。其实结构体就是各类基本类型变量的集合,是一个“大杂烩”。
固然之后的课程中咱们还会看到:结构体的嵌套定义(结构体里包含另外一个结构体)。
假设咱们须要自定义一个结构体,它储存屏幕上的一个点的坐标。
下面就给出 2D(D 是英语 dimension 的首字母,表示维度)世界的坐标系的大体印象:
当咱们在 2D 世界中作研究时,咱们有两个轴:
只要数学尚未还给小学体育老师,应该都知道 x 和 y 轴的。
如今,你能够写出一个名叫 Coordinate(表示“坐标”)的 struct 的定义了吗?我看好你!
能够本身先写,而后对一下咱们给出的参考答案:
struct Coordinate
{
int x; // 横坐标
int y; // 纵坐标
};
复制代码
很简单,不是吗?咱们的 Coordinate 这个 struct 包含了两个变量:x 和 y,都是 int 类型,分别表示横坐标值和纵坐标值。
固然了,若是你愿意,也能够建立一个表示 3D(三维)空间的点的 struct,只须要在刚才的 Coordinate 这个结构体的基础上加上 z 轴。
结构体里面也能够存放数组。
例如,能够构造一个名叫 Person(表示“人”)的结构体,以下所示:
struct Person
{
char firstName[100]; // 名
char lastName[100]; // 姓
char address[1000]; // 地址
int age; // 年龄
int boy; // 性别,布尔值 : 1 = boy(表示“男孩”), 0 = girl(表示“女孩”)
};
复制代码
能够看到,这个结构体变量包含五个基本的变量。
前三个分别表示 名、姓和地址,是字符数组。
第四个是年龄。
第五个是性别,是一个“布尔值”(固然,C语言自己没有定义布尔类型(true 或 false),可是能够用数值来“表示”布尔值的真或假),boy 这个 int 变量的值若是为 1,那就是表示男孩;若是为 0,那就是女孩。
这个结构体能够用于构建一个通信录程序。固然,你彻底能够在这个结构体里再添加其余变量,使其更完善。
只要内存够,通常来讲在一个结构体里没有变量的数目限制。
如今,咱们的结构体已经定义在 .h 头文件里了,那么咱们就能够在 include(“包含”)此头文件的文件中使用这些结构体了。
如下展现如何建立一个类型为 Coordinate(咱们以前已经定义了这个结构体,表示二维空间的坐标)的变量:
#include "coordinate.h" // 假设包含结构体定义的头文件叫 coordinate.h
int main(int argc, char *argv[])
{
struct Coordinate point; // 建立一个 Coordinate 类型的变量,名字是 point
return 0;
}
复制代码
如上,咱们建立了一个 Coordinate 类型的变量,名字是 point(表示“点”)。
这个变量自动拥有两个子变量:x 和 y,都是 int 类型,分别表示此二维坐标的横坐标值和纵坐标值。
你也许要问:“建立结构体变量开头的那个关键字 struct 是必须的吗?”
是的,是必须的。
struct 关键字使电脑可以区分基础变量类型(例如 int)和自定义变量类型(例如 Coordinate)。
然而,每次加 struct 关键字也有点麻烦。因此聪(懒)明(惰)伶(成)俐(性)的 C语言开发者设计了 typedef 关键字。
固然了,人类的大多数发明都是为了“懒惰”的缘故,能提升效率谁不肯意啊?
typedef 是 C语言的一个关键字,是 type(表示“类型”)和 define(表示“定义”)的缩合,顾名思义是表示“类型定义”。
听到“类型定义”,好像很难理解。但其实 typedef 的做用并无它的含义那么“高深莫测”。
从新回到刚才定义 Coordinate 这个结构体的 .h 头文件中。咱们来加一条由 typedef 开头的命令,目的是为 Coordinate 结构体建立一个别名。
什么是别名(alias)呢?
就好比有一我的,真实姓名叫王小明,别名能够是小明,明明,等,但都表明那我的。
有点相似 C++ 语言的引用的机制。
因此对别名的操做就是对原先对象的操做。
好比小时候你上课不乖,老师点名的时候,点到你的小名或者你的真实名字,都是叫的你,你就得去罚站。
咱们就在 Coordinate 结构体的定义以前加这句命令吧,通常习惯加在后面的,可是加在前面也能够:
typedef struct Coordinate Coordinate;
struct Coordinate
{
int x;
int y;
};
复制代码
能够看到,咱们新加了一行命令:
typedef struct Coordinate Coordinate;
复制代码
为了更好地理解这句命令的做用,咱们把它拆为三部分来看:
typedef
:说明咱们将要建立一个别名。struct Coordinate
:这是咱们要为其建立别名的结构体。Coordinate
:这就是要建立的别名。因此,上面这句命令的含义就是“从今之后,Coordinate
就至关于 struct Coordinate
了 ”。
这样作之后,咱们就能够不用每次在建立一个新的 Coordinate 结构体的变量时都加上 struct 关键字了。
因此,咱们的 .c 文件中就能够改写为:
int main(int argc, char *argv[])
{
Coordinate point; // 由于用了 typedef,电脑就清楚地知道此处的 Coordinate 其实就是 struct Coordinate
return 0;
}
复制代码
固然,别名不必定要叫 Coordinate,也能够叫做 Coor,也许更不容易混淆。例如:
typedef struct Coordinate Coor;
struct Coordinate
{
int x;
int y;
};
Coor coor; // 建立一个结构体变量
复制代码
建议你们在平时定义了 struct 类型后,也加一句 typdedef 命令,这样在代码里就不用每次新建一个此类型的变量时都要在开头写 struct 关键字了。
不少程序员都会这么作。 由于一个好的程序员是懂得如何“偷懒”的程序员,这和一个懒惰的程序员是有区别的。 咱们要使代码"write less,do more"(用尽可能少的代码作更多的事)。
固然,上面的代码块能够简写为:
typedef struct struct的名字
{
// struct 的内容
} 别名;
复制代码
因此上面 Coordinate 的代码块能够简写为:
typedef struct Coordinate
{
int x;
int y;
} Coordinate;
复制代码
注意:以后咱们的示例代码,有时会出现例如
Person player1;
复制代码
这样的形式,那就是假定咱们以前已经用了 typedef 了:
typedef struct Person Person;
复制代码
这样就能够省略开头的 struct 关键字,不须要再写成:
struct Person player1;
复制代码
既然咱们的 point 变量(是 Coordinate 类型的,但愿你们还没晕)已经建立好了,那咱们就能够修改它的成员的值了。
咱们如何访问 point 的两个成员 x 和 y 呢?以下所示:
int main(int argc, char *argv[])
{
Coordinate point;
point.x = 10;
point.y = 20;
return 0;
}
复制代码
这样,咱们就顺利地修改了 point 的两个成员的值,使其 x 坐标为 10,y 坐标为 20。
所以咱们的点就位于坐标系的(10, 20)处了。
因此,为了能访问到结构体的某个成员,咱们能够这样作:
结构体实例名称.成员名
复制代码
中间的点(.
)表示“从属”关系。
若是有面向对象编程基础的朋友,就会以为:这与“类和对象”也太像了吧。
是的,其实咱们能够用 struct 来“模拟”类。
若是咱们用以前建立的 Person 这个结构体来举例的话:
int main(int argc, char *argv[])
{
Person user; // user 表示“用户”
printf("你姓什么 ? ");
scanf("%s", user.lastName);
printf("你名叫什么 ? ");
scanf("%s", user.firstName);
printf("原来你的名字是 %s%s,失敬失敬\n", user.lastName, user.firstName);
return 0;
}
复制代码
运行输出:
你姓什么?王
你名叫什么?小明
原来你的名字是 王小明,失敬失敬
复制代码
咱们把 user.lastName 传给 scanf,使得用户输入的值直接修改 user 的 lastName 成员;咱们对 user.firstName 也是如此。
固然咱们也能够再添加对 address,age,boy 的赋值。
固然了,你也许会说:“我不知道结构体的使用,我用两个单独的字符串变量 lastName 和 firstName 不是也能够作到和上述程序相同的事么?”
是的,可是用结构体的好处就是咱们能够建立此结构体的变量,将不少相关联的数据封装在一块儿,成为一个总体,而不是零散地定义。
好比定义了 Person 这个结构体以后,凡是用 Person 来建立的变量,里面都自动包含了 lastName,firstName,address,age 和 boy 这五个变量,很是方便。
好比咱们能够这样建立:
Person player1, player2; // 以前已经用 typedef( typedef struct Person Person; )
复制代码
在 player1 和 player2 中都包含 lastName,firstName,address,age 和 boy 这五个变量。
咱们也能够更“偷懒”一些:建立结构体数组。例如:
Person players[2];
复制代码
这样,咱们就能够很方便的访问 players[1] 当中的变量了,例如:
players[1].lastName = "xiaoming";
复制代码
用结构体数组的好处是能够方便地使用循环,等等。
建立一个名叫 CoderHub(「程序员联盟」公众号)的结构体,在定义里放入你想建立的变量。而后建立此结构体的一个数组,用循环的方式给变量赋值,再用循环的方式打印出其中变量的信息。
以前的课程里,咱们建议对于基本变量,数组和指针,最好在建立的时候对其初始化。结构体也不例外。
初始化有一个很大的好处,就是避免此变量里存放“任意数据”。
事实上,一个变量在建立时,若是没有初始化,那么它会取当时在内存那个位置所存的值,因此这个值的随机性是很大的。
咱们来回忆一下,不一样变量的初始化应该怎么作:
基础变量(int,double,char,等):初始化为 0。
指针:初始化为 NULL。事实上,NULL 位于 stdlib.h 标准库头文件中,是用 #define
预处理命令定义的一个常量。它的值一般是 0。虽然是 0,可是有多种定义形式,例如:
#define NULL 0
#define NULL 0L
#define NULL ((void *) 0)
复制代码
可是咱们只要每次用 NULL 就行了,为了清楚代表这是指针变量,而不是通常变量。
那么对于咱们的“朋友” 结构体,咱们怎么初始化呢?
其实结构体的初始化也很简单,与数组的初始化很相似。咱们能够像下面这样定义:
Coordinate point = {0, 0};
复制代码
这样,咱们就依照顺序将 point.x 和 point.y 都初始化为 0 了。
对于像 Person 这样的结构体,里面的变量类型有 char 型数组和 int,那么咱们能够将 char 型数组初始化为 ""
(双引号中间为空)。
咱们能够像这样初始化一个字符串,在 C语言探索之旅 | 第二部分第四课:字符串 那一课忘记提了。不过,我想如今提还不算晚吧。
因此咱们就能够这样来初始化咱们的 Person 结构体变量:
Person player = {"", "", "", 0, 0};
复制代码
然而,咱们也能够这样来初始化一个结构体变量:建立一个函数,好比叫 initializeStruct,能够为每个传递给它的结构体作初始化,这样就方便不少,特别是当结构体中的变量不少时。
以前指针那一章咱们也已经学了,若是咱们对函数传递普通变量,那么由于 C语言的函数参数传递方式是值传递,因此它会对传给它的函数参数作一份拷贝,这样函数里面修改的实际上是那一份拷贝,真正的实参并无被改变。
为了让实参实实在在被修改,咱们须要用到指针,也就是传递此变量的地址。
对于结构体,也须要这样。所以,接下来咱们就来学习如何使用结构体指针。开始难起来咯,准备好了吗?
结构体指针的建立其实和普通的指针变量建立没什么区别。例如:
Coordinate *point = NULL;
复制代码
上面的代码就建立了一个叫作 point 的 Coordinate 结构体指针变量(Coordinate 是咱们上面定义的表示坐标的一个结构体)。
咱们再来提醒一次:
通常推荐写成:
Coordinate *point = NULL; // 星号挨着指针变量名字
复制代码
而不推荐写成:
Coordinate* point = NULL; // 星号挨着结构体名,这种写法很差!
复制代码
在指针的建立中,咱们推荐第一种写法。
由于用第二种写法,若是你在一行上建立好几个指针变量时,会容易忘记在第二个以后的变量前加 *
号。例如,容易写成这样:
Coordinate* point1 = NULL, point2 = NULL; // 编译会出错
复制代码
但这样编译会出错,由于 point2 实际上是 Coordinate 结构体变量,而不是 Coordinate 结构体指针变量!
因此咱们建议这样写:
Coordinate *point1 = NULL, *point2 = NULL;
复制代码
在之前的课程中,对于基础类型的指针变量,咱们也是这样建议:
int *number1 = NULL, *number2 = NULL;
复制代码
特别是 int 型的指针,还很不容易察觉到错误,若是写成:
int* number1 = NULL, number2 = NULL;
复制代码
编译器是不会报错的。由于 NULL 的值就是 0,能够赋给 number2 这个 int 型变量(注意:上面的 number2 不是 int 指针)。
回顾老是很好的(“伤心老是不免的...”)。
这里,咱们主要来学习如何将一个结构体指针(为何是传结构体指针而不是传结构体,能够看以前的解释)传给一个函数(做为参数),使得函数内部能够真正修改此结构体。
咱们来看一个实例:
#include <stdio.h>
typedef struct Coordinate
{
int x; // 横坐标值
int y; // 纵坐标值
} Coordinate;
void initializeCoordinate(Coordinate *point); // 函数原型
int main(int argc, char *argv[]) {
Coordinate myPoint;
initializeCoordinate(&myPoint); // 函数的参数是 myPoint 变量的地址
return 0;
}
// 用于初始化结构体变量
void initializeCoordinate(Coordinate *point) {
// 结构体初始化的代码
}
复制代码
上面的 initializeCoordinate 函数体内,咱们将放置初始化结构体的成员变量的代码。
咱们按顺序来看一下这段代码:
首先,咱们定义了一个结构体,叫作 Coordinate,里面包含两个变量,x 和 y。
咱们在 main 函数中建立了Coordinate 结构体的变量,名字叫 myPoint。
咱们将 myPoint 的地址传递给 initializeCoordinate 这个函数。
接下来,咱们就在 initializeCoordinate 函数中添加初始化 x 和 y 变量的代码吧:
void initializeCoordinate(Coordinate *point){
*point.x = 0;
*point.y = 0;
}
复制代码
point 前面的 *
号是必不可少的噢。由于,传进函数的参数是一个结构体指针,咱们要取到此结构体,就须要用到“解引用”符号:星号(*
)。
可是,认真的读者看出上面这个函数中的错误了吗?
咱们的初衷是想要:先用 *
号解引用 point 这个结构体指针,取到结构体,而后再用 .
号取到其中的变量 x 和 y。可是若是按上面的写法,其实效果至关于以下:
*(point.x) = 0;
*(point.y) = 0;
复制代码
由于 .
号的优先级是高于 *
号的。
有兴趣能够看一下 C语言运算符的优先级,不过以前的课咱们也说过了,记不清怎么办呢?加括号就解决啦。
上面的代码编译是通不过的,由于结构体指针 point 并无成员叫 x 和 y,并且,对于结构体指针咱们也不能用 .
号来取到什么值。
所以,咱们须要修改一下。改成以下就能够了:
void initializeCoordinate(Coordinate *point) {
(*point).x = 0;
(*point).y = 0;
}
复制代码
这样就对了。用括号去掉了运算符优先级的影响。
可是,以前也说过:程序员是懂得偷懒的一群人。
若是每次要取结构体的成员变量都要这么麻烦,先用 *
号,还要加括号,再用 .
号。想一想都要让 Denis Ritchie(C语言的做者)老爷子醉了。他是决不容许这种事发生的,所以,他就定义了一个新的符号: ->
(一个箭头。是的,就是这么“霸气侧漏”)。
用法以下:
point->x = 0;
复制代码
就至关于:
(*point).x = 0;
复制代码
是否是简便了不少?
记住:这个符号,只能用在指针上面。
所以,咱们的函数能够改写为:
void initializeCoordinate(Coordinate *point) {
point->x = 0;
point->y = 0;
}
复制代码
咱们在 main 函数里也能够这样写:
int main(int argc, char *argv[])
{
Coordinate myPoint;
Coordinate *myPointPointer = &myPoint;
myPoint.x = 10; // 用结构体的方式,修改 myPoint 中的 x 值
myPointPointer->y = 15; // 用结构体指针的方式,修改 myPoint 中的 y 值
return 0;
}
复制代码
结构体是 C语言中一个很是好用且很重要的概念,但愿你们好好掌握!
固然,还有很多知识细节,就要你们本身去看 C语言的经典教材了,例如《C程序设计语言》(不是谭浩强那本《C语言程序设计》!而是 C语言做者写的经典之做),《C和指针》,《C专家编程》,《C语言深度解剖》,《C陷阱和缺陷》,等等。
union 是“联合”的意思,是 C语言的关键字,也有的书上翻译为“共用体”。
咱们能够来写一个 union 的例子。
union CoderHub
{
char character;
int memberNumber;
double rate;
};
复制代码
乍看之下,和 struct 没什么区别么。可是真的没有区别吗?
假如咱们用 C语言的 sizeof 关键字(size 表示“尺寸,大小”,of 表示“...的”)来测试此 union 的大小(大小指的是在内存中所占的字节(byte)数,一个字节至关于 8 个 bit(二进制位)):
#include <stdio.h>
typedef union CoderHub
{
char character; // 大小是 1 个字节
int memberNumber; // 大小是 4 个字节
double rate; // 大小是 8 个字节
} CoderHub;
int main(int argc, char *argv[]){
CoderHub coderHub;
printf("此 union 的大小是 %lu 个字节\n", sizeof(coderHub));
return 0;
}
复制代码
运行程序,输出:
此 union 的大小是 8 个字节
复制代码
假如咱们对结构体也作一次测试,对比一下:
#include <stdio.h>
typedef struct CoderHub
{
char character; // 大小是 1 个字节
int memberNumber; // 大小是 4 个字节
double rate; // 大小是 8 个字节
} CoderHub;
int main(int argc, char *argv[]){
CoderHub coderHub;
printf("此 struct 的大小是 %lu 个字节\n", sizeof(coderHub));
return 0;
}
复制代码
运行程序,输出:
此 struct 的大小是 16 个字节
复制代码
为何咱们自定义的 union 的大小是 8 个字节,而 struct 是 16 个字节呢?
这就涉及到 union(共用体)和 struct(结构体)的区别了。
struct 的大小是其中全部变量大小的总和。
可是你会说:“不对啊, 1 + 4 + 8 = 13,为何 sizeof(coderHub)
的值为 16 呢?”
好问题!这个有点复杂,涉及到内存对齐的问题,咱们之后再说。若是你必定要知道,那是由于内存对齐使得第一个 char 变量对齐了第二个 int 变量的空间,也变成了 4,如此一来:4 + 4 + 8 = 16。
有兴趣的读者能够去参考《C语言深度解剖》的解释。
在嵌入式编程等内存有限的环境下,须要考虑内存对齐,以节省空间。
union 的大小等于其中最大(sizeof()
获得的值最大)的那个变量的大小。因此咱们就知道了,其实 union 的储存是这样的:其中的每一个变量在内存中的起始地址是同样的,因此 union 同一时刻只能存放其中一个变量,union 的大小等于其中最大的那个变量,以保证能够容纳任意一个成员。
union 适合用在不少相同类型的变量集,可是某一时刻只需用到其中一个的状况,比较节省空间。
看完了 struct(结构体)和 union(联合),咱们最后来学习很经常使用的一个自定义变量类型:enum。
enum 是 enumeration(表示“枚举”)的缩写,也是一个 C语言关键字。
枚举是一个比较特别的自定义变量类型。当初我学 C语言时,一开始还真有点不理解。但用得好,却很是实用。
咱们以前学了:结构体里面包含了多个能够是不一样类型的成员变量(一说“成员”就有点面向对象的感受 :P)。
可是 enum(枚举)里面是一系列可选择的值。也就是说每次只能取其中一个值,听着和 union 有点相似啊。可是 enum 和 union 仍是有区别的。
咱们来举一个例子就知道区别了:
typedef enum Shape Shape;
enum Shape // shape 表示“身材、体型”
{
THIN, // thin 表示“瘦”
MEDIUM, // medium 表示“中等”
FAT // fat 表示“胖”
};
复制代码
因此,咱们定义了一个名叫 Shape 的 enum 变量。其中有三个值,分别是 THIN,MEDIUM 和 FAT(身材有瘦,中等和胖之分)。不必定要大写,只是习惯。
那咱们怎么来建立 enum 变量呢?以下:
Shape shape = MEDIUM;
复制代码
shape 这个变量,咱们在程序里也能够再将其修改成 THIN 或者 FAT。
你们看到 enum 和 union 以及 struct 的区别了吗?是的,enum 的定义里,每一个成员没有变量类型(int,char,double,之类)!
很奇怪吧。想起来为何 enum 的成员习惯用大写了吗?
对,就是由于 enum 的每一个成员都不是变量,而是常量!可是 enum 的机制和常量定义以及 #define
仍是有些区别:
像上面的代码:
typedef enum Shape
{
THIN,
MEDIUM,
FAT
} Shape;
复制代码
编译器会自动为其中的每个成员绑定一个常量值,咱们写程序测试一下:
#include <stdio.h>
typedef enum Shape Shape;
enum Shape
{
THIN,
MEDIUM,
FAT
};
int main(int argc, char *argv[]) {
Shape shape = THIN;
printf("THIN = %d\n", shape);
shape = MEDIUM;
printf("MEDIUM = %d\n", shape);
shape = FAT;
printf("FAT = %d\n", shape);
return 0;
}
复制代码
运行程序,输出:
THIN = 0
MEDIUM = 1
FAT = 2
复制代码
看到了吗?编译器自动给这三个成员赋值 0,1 和 2。若是没有指定 enum 成员的值,那么它们的值是从 0 开始,依次加 1。
咱们也能够本身来定义 enum 成员的值,不必定要每次让编译器给咱们自动分配。
咱们能够这样写:
typedef enum Shape
{
THIN = 40,
MEDIUM = 60,
FAT = 90
} Shape;
复制代码
这样,咱们就本身给每一个成员定义了值。
咱们也可让编译器为咱们自动分配几个值,再本身定义几个值,例如:
typedef enum Shape
{
THIN,
MEDIUM,
FAT = 90
} Shape;
复制代码
上面,咱们没有为 THIN 和 MEDIUM 赋值,那么编译器会将他们赋值为 0 和 1。
而 FAT,由于咱们已经指定了其值为 90,因此 FAT 就等于 90。
是否是以为 enum 和用 #define
来定义的常量是有些相似呢?
其实,仍是有些不一样的:
#define
宏常量(或预处理常量)是在预处理阶段进行简单替换,枚举常量则是在编译的时候肯定其值。
通常在编译器里,能够调试枚举常量,可是不能调试宏常量。
枚举能够一次定义大量相关的常量,而 #define
宏一次只能定义一个。
结构体(struct)是一种自定义的变量类型,彻底由咱们自由发挥,本身定制(走的是“高级定制”的路线啊),与 int,double 等基础变量类型有所区别。结构体的使用可让咱们的 C语言程序更加灵活,能够作更多事。
结构体里包含成员变量,一般是基础变量类型的变量,如 int,double 等变量,但也能够有指针变量,数组,甚至其余的结构体变量。
为了访问到结构体的成员变量,咱们能够用普通的结构体方式访问:结构体变量名称.成员变量名
(中间用一个“点”链接)。
咱们也能够用特别简便的结构体指针的方式来访问结构体的成员变量:结构体指针变量名->成员变量名
(中间用一个“箭头”链接)。
union(“共用体”,或“联合”)和 struct 的最大不一样就是:union 的大小是其中容量最大的那个成员变量的大小,而结构体的大小是每个成员变量的总和(还要考虑内存对齐)。union 一次只能取其中一个变量。
enum(枚举)一次只能取其中的一个成员的值,这一点和 union 有些相似。可是 enum 的成员都是常量,而不是变量。并且 enum 的成员若是没有指定数值,编译器会按照递增顺序为每个变量赋值,从 0 开始。
今天的课就到这里,一块儿加油吧!
我是 谢恩铭,公众号「程序员联盟」(微信号:coderhub)运营者,慕课网精英讲师 Oscar 老师,终生学习者。 热爱生活,喜欢游泳,略懂烹饪。 人生格言:「向着标杆直跑」