C语言结构体、枚举以及位域的讲解

谨记

什么是价值?或许没有多少人能够明白,其实价值并不是实际存在的,它应该是一种体现,比如为城市点缀最美好的一面而起早摸黑的打扫的城市清洁工的大妈大爷;为中国航天事业而几个月没回家奋斗在一线的工程师们;为城市交通方便,上班不迟到的公交车司机;为自己人生而奋斗终生的你等等。太多了,价值是无处不在的,不管你在身在何处,做着什么事,你都体现了你的一份价值,或许你的价值在别人心中是那么的伟大,又或者是那么的微小,请你一定要记住,你的一举一动一言一行都决定了你的价值。

引用

前面我们学习了关于C语言很多的知识,希望大家都掌握了,对于C语言来说,大部分高级语法其实和C语言是想通的,所以,学好C语言将来你要转其他语言是很轻松的。那么,今天我们将学习C语言的结构体、枚举以及简单的介绍一下位域,希望读者认真的理解结构体和枚举。

结构体

1、结构体的定义

在实际的处理对象中,有许多信息是由多个不同类型的数据组合在一起进行描述,而且这些不同类型的数据是互相联系组成了一个有机的整体。此时,就要用到一种新的构造类型数据——结构体(structure),简称结构。
结构体的作用:为了处理复杂的数据结构(如动态数据结构等)提供了有效的手段,而且,它们为函数间传递不同类型的数据提供了方便。
结构体和数组一样,也是一种构造型数据类型,是用户自定义的新数据类型,在结构体中可以包含若干个不同数据类型和不同意义的数据项(当然也可以相同),从而使这些数据项组合起来反映某一个信息。
例如,可以定义一个学生student结构体,在这个结构体中包括学生学号、姓名、性
别、年龄、家庭住址、联系电话。这样就可以用一个结构体数据类型的变量来存放某个学生的所有相关信息。并且,用户自定义的数据类型student也可以与int、double等基本数据类型一样,用来作为定义其他变量的数据类型。

定义一个结构体类型的一般形式为
struct  结构体名
{
   数据类型   成员名1;
   数据类型   成员名2;
   :
   数据类型   成员名n;
};
在花括号中的内容也称为“成员列表”或“域表”。
其中,每个成员名的命名规则与变量名相同;
数据类型可以是基本变量类型和数组类型,或者是一个结构体类型;
用分号“;”作为结束符。整个结构的定义也用分号作为结束符。
struct student{   
    long number;
    char name[20];
    char gender;        
    int age;            //  age是成员名
    float salary;
    char address[80];
};                      //注意分号不能省略
int  age = 10;      //age是变量名 

结构体类型中的成员名可以与程序中的变量名相同,二者并不代表同一对象,编译程序可以自动对它们进行区分。
由于结构体的成员的数据类型可以是任何类型,可能是基本变量类型、数组类型、结构体类型、联合体类型或枚举类型等。

总结

结构体类型的特点:
a.结构体类型是用户自行构造的;
b.它由若干不同的基本数据类型的数据构成;
c.它属于C语言的一种数据类型,与整型、浮点型相当。因此,定义它时不分配空间,只有用它定义变量时才分配空间。

2、结构体变量的声明、初始化及运用

2.1 结构体变量的声明
在定义了结构体类型后,就可以声明结构体类型的变量。有下面几种形式:
① 先定义结构体类型,再定义变量名
定义结构体变量的一般形式如下:

struct 结构体名{
类型 成员名;
类型 成员名;
……
};
struct 结构体名 结构体变量名;

这里的结构体名是结构体的标识符,不是变量名。类型可以是基本的数据类型也可以是其他构造型数据类型。

struct Person
    {
        char array[20];
        char *name;
        int age;
        double height;
    };
    struct Person p;

注意

struct Person代表的是类型名,不能分开和省略写,比如“struct P”;这是错误的语法。

② 在定义类型的同时,定义变量
这种形式的定义的一般形式为

struct 结构体名
    {
类型 成员名;
类型 成员名;
……
    }变量名;
struct Person
    {
        char array[20];
        char *name;
        int age;
        double height;
    }per;

③ 直接定义结构体变量
如果省略结构体名,则称之为无名结构体,这种情况常常出现在函数内部。
这种形式的定义的一般形式为

struct 
    {
类型 成员名;
类型 成员名;
……
    }变量名;
struct
    {
        char array[20];
        char *name;
        int age;
        double height;
    }per;

结构体所占内存空间大小

一个结构体变量占用内存的实际大小,可以利用sizeof求出。它的运算表达式为
sizeof(运算量)
其中运算量可以是变量、数组或结构体变量,可以是数据类型的名称。这样,就可以求出给定的运算量占用内存空间的字节数。
sizeof(struct person);
sizeof(per);

2.2、结构体变量的使用
结构体变量是不同数据类型的若干数据的集合体。在程序中使用结构体变量时,一般情况下不能把它作为一个整体参加数据处理,而参加各种运算和操作的是结构体变量的各个成员项数据。

结构体变量的成员用以下一般形式表示:
    结构体变量名.成员名

在定义了结构体变量后,就可以用不同的赋值方法对结构体变量的每个成员赋值。

per.age = 27;
per.name = "helloworld";

除此之外,还可以引用结构体变量成员的地址以及成员中的元素。例如:引用结构体变量成员的首地址&per.name;引用结构体变量成员的第二个字符per.name[1];引用结构体变量的首地址&per。

温馨提示
结构体变量在使用中应注意以下几点。
① 不能将一个结构体类型变量作为一个整体加以引用,而只能对结构体类型变量中的各个成员分别引用。

printf("%s",per);//这种写法是错的
printf("%s\n",per.name);//这是可以的

举一个完整的示例:

#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
    struct person {
        char name[20];
        int  age;
        double height;
    }per;
    memset(&per, 0, sizeof(per));
    printf("name:");
    scanf("%s",per.name);
        printf("age:");
    scanf("%d",&per.age);
    printf("height:");
    scanf("%lf",&per.height);
    printf("%s\n  %d\n %lf\n",per.name,per.age,per.height);
    return 0;
}
输出结果:
name:helloworld
age:12
height:178
helloworld
  12
 178.000000
Program ended with exit code: 0

② 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级成员。只能对最低级的成员进行赋值或存取以及运算。

#include <stdio.h>
#include <string.h>
#define N 64
struct employee{
    char name[N];
    struct {
        int year;
        int month;
        int day;
    }birthday;
    char gender;
    char address[N];
    float salary;
};
int main(int argc, const char * argv[]) {
    struct employee e1;
    memset(&e1, 0, sizeof(e1));
    printf("name:");
    scanf("%s", e1.name);
    printf("year:");
    scanf("%d", &e1.birthday.year);
    printf("month:");
    scanf("%d", &e1.birthday.month);
    printf("day:");
    scanf("%d", &e1.birthday.day);
    getchar();
    printf("gender:");
    scanf("%c", &e1.gender);
    printf("address:");
    scanf("%s", e1.address);
    printf("salary:");
    scanf("%f", &e1.salary);
    printf("\ninformation:%s %d-%d-%d %c %s %f\n", e1.name, e1.birthday.year,
            e1.birthday.month, e1.birthday.day, e1.gender, e1.address, e1.salary);
    return 0;
}
输出结果:
name:zhangsan
year:1989
month:12
day:11
gender:m
address:Suzhou
salary:3200.5

information:zhangsan 1989-12-11 m Suzhou 3200.500000
Program ended with exit code: 0

在该程序中,结构体的成员birthday,是一个结构体类型的变量。对于这样的变量,可以这样访问各成员:

e1.birthday.year
e1.birthday.month
e1.birthday.day

注意:不能用e1.birthday来访问e1变量中的成员birthday,因为birthday本身是一个结构体变量。
③ 对成员变量可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。例如:

e2.age=e1.age;
sum=e1.age+e2.age;
e1.age++;

④ 在数组中,数组是不能彼此赋值的,而结构体类型变量可以相互赋值。在C程序中,同一结构体类型的结构体变量之间允许相互赋值,而不同结构体类型的结构体变量之间不允许相互赋值,即使两者包含有同样的成员。

2.3、结构体变量初始化
与其他类型变量一样,也可以给结构体的每个成员赋初值,这称为结构体的初始化。一种是在定义结构体变量时进行初始化,语法格式如下:

struct  结构体名 变量名={初始数据表};

另一种是在定义结构体类型时进行结构体变量的初始化。

struct  结构体名
{
类型 成员名;
类型 成员名;
……
}变量名={初始数据表}; 

这里就不在举例了,后面的例子会用。

结构体数组

具有相同结构体类型的结构体变量也可以组成数组,称它们为结构体数组。结构体数组的每一个数组元素都是结构体类型的数据,它们都分别包括各个成员(分量)项。
定义结构体数组的方法和定义结构体变量的方法相仿,只需说明其为数组即可。可以采用以下方法。
① 先定义结构体类型,再用它定义结构体数组,定义形式如下:

struct 结构体名 
{
类型 成员名;
类型 成员名;
……

};
struct 结构体名 数组名[元素个数];
#define N 64
struct employee{
    char name[N];
    int age;
    char gender;
    char address[N];
    float salary;
};
struct employee e[10];
这种声明格式是最常见的。

② 定义结构体类型的同时,定义结构体数组,定义形式如下:

struct 结构体名 
{
类型 成员名;
类型 成员名;
……

}数组名[元素个数];
#define N 64
struct employee{
    char name[N];
    int age;
    char gender;
    char address[N];
    float salary;
} e[10];
这种情况,定义结构体数组,不用重复写结构体类型,很简洁。一般,当需要声明一个全局的结构体数组时,使用这种方式。

③ 直接定义结构体数组,定义形式如下:

struct  
{
类型 成员名;
类型 成员名;
……

}数组名[元素个数];
#define N 64
struct{
    char name[N];
    int age;
    char gender;
    char address[N];
    float salary;
} e[10];

结构体数组初始化
结构体数组在定义的同时也可以进行初始化,并且与结构体变量的初始化规定相同。
结构体数组初始化的一般形式是:

   struct 结构体名  
    {
    类型 成员名;
    类型 成员名;
    ……
    };
    struct 结构体名 数组名[元素个数]={初始数据表};
    或者
    struct 结构体名 
    {
    类型 成员名;
    类型 成员名;
    ……
    }数组名[元素个数]={初始数据表};
    或者
    struct
    {
    类型 成员名;
    类型 成员名;
    ……
    }数组名[元素个数]={初始数据表};

由于结构体变量是由若干不同类型的数据组成,而结构体数组又是由若干结构体变量组成。所以要特别注意包围在大括号中的初始数据的顺序,以及它们与各个成员项间的对应关系。
结构体数组的使用
一个结构体数组的元素相当于一个结构体变量,因此前面介绍的有关结构体变量的规则也适应于结构体数组元素。
(1)引用某一元素中的成员。
(2)可以将一个结构体数组元素值赋给同一结构体类型的数组中的另一个元素,或赋给同一类型的变量。
(3)不能把结构体数组元素作为一个整体直接进行输入输出。

#include <stdio.h>
#include <string.h>
#define N 64
struct employee{
    char name[N];
    struct {
        int year;
        int month;
        int day;
    }birthday;
    char gender;
    char address[N];
    float salary;
}e1[2] = { {"zhangsan", {1980, 9, 4}, 'w', "Shanghai", 3400}, 
           {"lisi", {1991, 10, 24}, 'w', "Hebei", 3400}};
int main(int argc, const char * argv[]) {
    struct employee e2[2] = {{"wangwu", {1986, 2, 24}, 'w', "Hubei", 6400}, 
                             {"Lucy", {1986, 8, 14}, 'w', "Henan", 3421}};
    int i;
    for (i = 0; i < sizeof(e1)/sizeof(struct employee); i++)
        printf("information :%s %d-%d-%d %c %s %f\n", e1[i].name, 
                e1[i].birthday.year, e1[i].birthday.month, e1[i].birthday.day, 
                e1[i].gender, e1[i].address, e1[i].salary);
    printf("\n");
    for (i = 0; i < sizeof(e2)/sizeof(struct employee); i++)
        printf("information :%s %d-%d-%d %c %s %f\n", e2[i].name, 
                e2[i].birthday.year, e2[i].birthday.month, e2[i].birthday.day, 
                e2[i].gender, e2[i].address, e2[i].salary);
    return 0;
}
在该程序中,声明了两个结构体数组,一个全局数组,一个局部数组。在声明的同时,写初始化列表初始化。

结构体指针

可以设定一个指针变量用来指向一个结构体变量。此时该指针变量的值是结构体变量的起始地址,该指针称为结构体指针。
结构体指针与前面介绍的各种指针变量在特性和方法上是相同的。与前述相同,在程序中结构体指针也是通过访问目标运算“*”访问它的对象。结构体指针在程序中的一般定义形式为

struct 结构体名  *结构指针名;

其中的结构体名必须是已经定义过的结构体类型。
例如,对于上一节中定义的结构体类型struct employee,可以说明使用这种结构体类型的结构指针如下:

struct employee *p;

其中p是指向struct employee结构体类型的指针。结构体指针的说明规定了它的数据特性,并为结构体指针本身分配了一定的内存空间。但是指针的内容尚未确定,即它指向随机的对象,需要为指针变量赋初值。

#include <stdio.h>
#include <string.h>
#define N 64
struct employee{
    char name[N];
    int age;
    char gender;
    char address[N];
    float salary;
};
void input(struct employee *p){
    printf("name:");
    scanf("%s", p->name);
    printf("age:");
    scanf("%d", &p->age);
    getchar();
    printf("gender:");
    scanf("%c", &p->gender);
    printf("address:");
    scanf("%s", p->address);
    printf("salary:");
    scanf("%f", &p->salary);
}
void output(struct employee *p){
    printf("information:%s %d %c %s %f\n", p->name, p->age,
            p->gender, p->address, p->salary);
}
int main(int argc, const char * argv[]) {
    struct employee e1;
    memset(&e1, 0, sizeof(e1));
    input(&e1);
    output(&e1);
    return 0;
}
输出结果:
name:zhao
age:21
gender:m
address:Shanghai
salary:3421.1
information:zhao 21 m Shanghai 3421.100098
Program ended with exit code: 0

枚举

在C语言中还有一种构造类型,即枚举。在实际问题中,有些变量只有几种可能的取值。例如:一周有七天,程序中的错误只有那么几种等。针对这样特殊的变量,C语言中提供了“枚举”类型,在枚举的定义中,会将变量的值一一列出来。当然,枚举类型的变量的值也就只限于列举出来的值的范围内。
枚举类型的定义
枚举类型的定义形式如下:
enum 枚举名{ 枚举成员列表 };
在枚举成员列表中列出所有可能的取值,以分号结尾。注意和结构体、联合体类似,“enum 枚举名”是新定义的类型名。
举例如下:

enum TimeofDay 
{ 
morning, 
afternoon, 
evening
};

该枚举名为TimeofDay,共有3种可能的取值。在定义该枚举类型的变量时,也只能取其中的一个值,进行赋值。
枚举变量的声明
其实枚举变量的声明和我们上面说的结构体变量的声明是一样的。
方式一:先定义类型,再声明变量

enum TimeofDay 
{ 
morning, 
afternoon, 
evening 
};
enum TimeofDay a, b;

方式二:在定义类型的同时,声明变量

enum TimeofDay 
{ 
morning, 
afternoon, 
evening 
} a, b;

方式三:直接定义无名枚举变量

   enum 
    { 
    morning, 
    afternoon, 
    evening 
    } a, b;

枚举变量的使用
通过一个示例来说吧

#include <stdio.h>
enum TimeofDay{
    morning = 2,
    afternoon,
    evening
}a;
int main(int argc, const char * argv[]) {
    enum TimeofDay b, c;
    a = morning;
    b = afternoon;
    c = evening;
    printf("a=%d b=%d c=%d\n", a, b, c);
    c = 10;
    printf("a=%d b=%d c=%d\n", a, b, c);
    return 0;
}
运行结果:
a=2 b=3 c=4
a=2 b=3 c=10
Program ended with exit code: 0

通过该程序,可以看出,可以把枚举成员的值赋予枚举变量,也可以把整数值直接赋予枚举变量。实际上,枚举变量,也是一种整型变量,因此,也可以使用switch-case结构,示例程序如下:

#include <stdio.h>
enum TimeofDay{
    morning,
    afternoon,
    evening
};
int main(int argc, const char * argv[]) {
    int i, j;
    enum TimeofDay a[10];
    j = morning;
    for (i = 0; i < 10; i++)
    {
        a[i] = j;
        j++;
        if (j > evening)
            j = morning;
    }
    for (i = 0; i < 10; i++)
    {
        switch (a[i])
        {
            case morning: printf("%d morning\n", a[i]); break;
            case afternoon: printf("%d afternoon\n", a[i]); break;
            case evening: printf("%d evening\n", a[i]); break;
            default:break;
        }
    }
    return 0;
}
输出结果:
0 morning
1 afternoon
2 evening
0 morning
1 afternoon
2 evening
0 morning
1 afternoon
2 evening
0 morning
Program ended with exit code: 0

位域的介绍

在这里,对于位域,只是一个简单的介绍,这个用的不是很多,如果真需要用,建议读者去官网看教程。
位域的定义
所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
位域的定义与结构体的定义相似,其一般形式如下:

struct 位域结构名
{   
  位域列表 
};

其中位域列表的形式为

类型说明符 位域名:位域长度

例如:

 struct data{
    unsigned int a:2;
    unsigned int b:3;
    unsigned int c:3;
};
其中a表示data的低两位,表示data的3~5位,c表示data的6~8位。

关于位域的定义,有一些问题需要注意。
① 个位域必须存储在同一个字节中,不能跨两个字节。
② 位域的占用的位数,不能超过8个二进制位。
③ 允许位域无域名。
位域的使用
位域的使用和结构成员的使用相同,其一般形式为
位域变量名.位域名
示例代码

#include <stdio.h>
struct data{
    unsigned int a: 2;
    unsigned int b: 4;
    unsigned int: 0;
    unsigned int c: 3;
}t;
int main(int argc, const char * argv[]) {
    struct data *p;
    t.a = 3;
    t.b = 5;
    t.c = 6;
    printf("t.a=%d t.b=%d t.c=%d\n",t.a, t.b, t.c);
    p = &t;
    p->a = 2;
    p->b &= 0;
    p->c |= 1;
    printf("t.a=%d t.b=%d t.c=%d\n",t.a, t.b, t.c);
    return 0;
}
输出结果:
t.a=3 t.b=5 t.c=6
t.a=2 t.b=0 t.c=7
Program ended with exit code: 0
说明:由于位域中的各个域是以二进制位为单位,因此,大部分的位域程序,都有位运算

总结

本篇文章介绍了C语言的结构体、枚举、位域,以及结构体和数组、指针的联系,内容比较多,希望读者认真的理解和体会,那么,在这里,C语言的基础知识点我们都已经学的差不多了。希望能给读者带来帮助。

结尾

希望读者真诚的对待每一件事情,每天都能学到新的知识点,要记住,认识短暂,开心也是过一天,不开心也是一天,无所事事也是一天,小小收获也是一天,欢迎收藏和点赞、喜欢。最后送读者一句话:你的路你自己选择。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容