C语言中的结构体、位于(位段)、共用体、枚举

结构体

前面学过一种构造类型——数组:
构造类型:
       不是基本类型的数据结构也不是指针,它是若干个相同或不同类型的数据构成的集合
        描述一组具有相同类型数据的有序集合,用于处理大量相同类型的数据运算
        有时我们需要将不同类型的数据组合成一个有机的整体, 以便于引用。
如:
        一个学生有学号/ 姓名 / 性别 / 年龄 / 地址等属性
                int num;
                char name[20];
                char sex;
                int age;
                char addr[30];
显然单独定义以上变量比较繁琐,数据不便于管理

结构体类型的概念及定义

1 、概念:
        结构体是一种构造类型的数据结构,
        是一种或多种基本类型或构造类型的数据的集合。
2 、 结构体类型的定义方法
        咱们在使用结构体之前必须先有类型,然后用类型定义数据结构
        这个类型相当于一个模具
        (1).先定义结构体类型,再去定义结构体变量
                struct 结构体类型名{
                        成员列表
                };
例 1:
struct stu{
    int num;
    char name[20];
    char sex;
};
        //有了结构体类型后,就可以用类型定义变量了
        struct stu lucy,bob,lilei;//定义了三个 struct stu 类型的变量
        每个变量都有三个成员,分别是 num name sex
        咱们可以 暂时认为 结构体变量的大小是它所有成员之和
(2). 在定义结构体类型的时候顺便定义结构体变量,以后还可以定义结构体变量
                struct 结构体类型名{
                        成员列表;
                }结构体变量 1, 变量 2;
        struct 结构体类型名 变量 3 ,变量 4
例:

struct stu{
    int num;
    char name[20];
    char sex;
}lucy,bob,lilei;
struct stu xiaohong,xiaoming;
3. 在定义结构体类型的时候,没有结构体类型名,顺便定义结构体变量,
因为没有类型名,所以以后不能再定义相关类型的数据了
        struct {
                成员列表;
        }变量 1 ,变量 2;

struct {
    int num;
    char name[20];
    char sex;
}lucy,bob;
        以后没法再定义这个结构体类型的数据了,因为没有类型名
4. 最常用的方法
        通常咱们将一个结构体类型重新起个类型名,用新的类型名替代原先的类型
        步骤 1 :先用结构体类型定义变量
                struct stu{
                        int num;
                        char name[20];
                        char sex;
                }bob;
        步骤 2 :新的类型名替代变量名
                struct stu{
                        int num;
                        char name[20];
                        char sex;
                }STU;
        步骤 3 :在最前面加 typedef
                typedef struct stu{
                        int num;
                        char name[20];
                        char sex;
                }STU;
注意:步骤 1 和步骤 2 ,在草稿上做的,步骤 3 是程序中咱们想要的代码
以后 STU 就相当于 struct stu
STU lucy; struct stu lucy; 是等价的。

结构体变量的定义初始化及使用

1 、结构体变量的定义和初始化
        结构体变量,是个变量,这个变量是若干个数据的集合
注:
        (1):在定义结构体变量之前首先得有结构体类型,然后在定义变量
        (2):在定义结构体变量的时候,可以顺便给结构体变量赋初值,被称为结构体的初始化
        (3):结构体变量初始化的时候,各个成员顺序初始化
例1:

struct stu{
    int num;
    char name[20];
    char sex;
};
struct stu boy;
struct stu lucy={
    101,
    "lucy",
    'f'
};
2 、结构体变量的使用
        定义了结构体变量后,要使用变量
        (1).结构体变量成员的引用方法
        结构体变量. 成员名
struct stu{
    int num;
    char name[20];
    char sex;
};
struct stu bob;
bob.num=101;//bob 是个结构体变量,但是 bob.num 是个 int 类型的变量
bob.name 是个字符数组,是个字符数组的名字,代表字符数组的地址,是个常量
bob.name ="bob";//是不可行,是个常量
strcpy(bob.name,"bob");
#include <stdio.h>
struct stu{
    int num;
    char name[20];
    int score;
    char *addr;
};
int main(int argc, char *argv[])
{
    struct stu bob;
    printf("%d\n",sizeof(bob));
    printf("%d\n",sizeof(bob.name));
    printf("%d\n",sizeof(bob.addr));
    return 0;
}
(2). 结构体成员多级引用
#include <stdio.h>
struct date{
    int year;
    int month;
    int day;
};
struct stu{
    int num;
    char name[20];
    char sex;
    struct date birthday;
};
int main(int argc, char *argv[])
{
    struct stu lilei={101,"lilei",'m'};
    lilei.birthday.year=1986;
    lilei.birthday.month=1;
    lilei.birthday.day=8;
    printf("%d %s %c\n",lilei.num,lilei.name,lilei.sex);
    printf("%d %d %d\n",lilei.birthday.year,lilei.birthday.month,lilei.birthday.day);
    return 0;
}
3 相同类型的 结构体变量可以相互赋值
注意:必须是相同类型的结构体变量,才能相互赋值。
#include <stdio.h>
struct stu{
    int num;
    char name[20];
    char sex;
};
int main(int argc, char *argv[])
{
    struct stu bob={101,"bob",'m'};
    struct stu lilei;
    lilei=bob;
    printf("%d %s %c\n",lilei.num,lilei.name,lilei.sex);
    return 0;
}

结构体数组

        结构体数组是个数组,由 若干个相同类型的结构体变量构成的集合
1 、结构体数组的定义方法
struct 结构体类型名 数组名 [ 元素个数 ];
        例 9:
                struct stu{
                        int num;
                        char name[20];
                        char sex;
                };
        struct stu edu[3];//定义了一个 struct stu 类型的结构体数组 edu
这个数组有 3 个元素分别是 edu[0] edu[1] edu[2]
1 、结构体数组元素的引用 数组名 [ 下标 ]
2 、数组元素的使用
        edu[0].num =101;//用 101 edu 数组的第 0 个结构体变量的 num 赋值
        strcpy(edu[1].name,"lucy");
#include <stdio.h>
typedef struct student
{
    int num;
    char name[20];
    float score;
}STU;
    STU edu[3]={
        {101,"Lucy",78},
        {102,"Bob",59.5},
        {103,"Tom",85}
    };
int main()
{
    int i;
    float sum=0;
    for(i=0;i<3;i++)
    {
        sum+=edu[i].score;
    }
    printf("平均成绩为%f\n",sum/3);
    return 0;
}

结构体指针

        即结构体的地址,结构体变量存放内存中,也有起始地址
        咱们定义一个变量来存放这个地址,那这个变量就是结构体指针变量。
1 、结构体指针变量的定义方法:
        struct 结构体类型名 * 结构体指针变量名 ;
                struct stu{
                        int num;
                        char name[20];
                };
        struct stu * p;//定义了一个 struct stu * 类型的指针变量
        变量名 是 p p 4 个字节,用来保存结构体变量的地址编号
        struct stu boy;
        p=&boy;
访问结构体变量的成员方法:
boy.num=101;//可以,通过 结构体变量名.成员名
(*p).num=101;//可以,*p 相当于 p 指向的变量 boy
p->num=101;//可以,指针->成员名
        通过结构体指针来引用指针指向的结构体的成员,前提是 指针必须先指向一个结构体变量。

结构体指针经常用到的地方:

(1) :保存结构体变量的地址
typedef struct stu{
    int num;
    char name[20];
    float score;
}STU;
int main()
{
    STU *p,lucy;
    p=&lucy;
    p->num=101;
    strcpy(p->name,"baby");
    //p->name="baby";//错误,因为 p->name 相当于 lucy.name 是个字符数组的名字,是个常量
}
(2) :传 结构体变量的地址
#include<stdio.h>
#include<string.h>
typedef struct stu{
    int num;
    char name[20];
    float score;
}STU;
void fun(STU *p)
{
    p->num=101;
    (*p).score=87.6;
    strcpy(p->name,"lucy");
}
int main()
{
    STU girl;
    fun(&girl);
    printf("%d %s %f\n",girl.num,girl.name,girl.score);
    return 0;
}
(3) :传结构体数组的地址
结构体数组,是由多个相同类型的结构体变量构成的。存放在内存里,
也有起始地址,其实就是第 0 个结构体的地址。
#include<stdio.h>
#include<string.h>
typedef struct stu{
    int num;
    char name[20];
    float score;
}STU;
void fun(STU *p)
{
    p[1].num=101;
    (*(p+1)).score=88.6;
}
int main()
{
    STU edu[3];
    fun(edu);
    printf("%d %f\n",edu[1].num,edu[1].score);
    return 0;
}
注意:
(1) :结构体变量的地址编号和结构体第一个成员的地址编号相同,但指针的类型不同
#include <stdio.h>
struct stu{
    int num;
    char name[20];
    int score;
};
int main(int argc, char *argv[])
{
    struct stu bob;
    printf("%p\n",&bob);
    printf("%p\n",&(bob.num));
    return 0;
}
(2) :结构体数组的地址就是结构体数组中第 0 个元素的地址
#include <stdio.h>
struct stu{
    int num;
    char name[20];
    int score;
};
int main(int argc, char *argv[])
{
    struct stu edu[3];
    printf("%p\n",edu);//struct stu *
    printf("%p\n",&(edu[0]));//struct stu *
    printf("%p\n",&(edu[0].num));//int *
    return 0;
}

结构体内存分配

1 、结构体内存分配
        结构体变量大小是,它所有成员之和。
        因为结构体变量是所有成员的集合。
#include<stdio.h>
struct stu{
    int num;
    int age;
}lucy;
int main()
{
    printf("%d\n",sizeof(lucy));//结果为 8
    return 0;
}
但是在实际给结构体变量分配内存的时候,是规则的
#include<stdio.h>
struct stu{
    char sex;
    int age;
}lucy;
int main()
{
    printf("%d\n",sizeof(lucy));//结果为 8???
    return 0;
}
规则 1 :以多少个字节为单位开辟内存
        给结构体变量分配内存的时候,会去结构体变量中找基本类型的成员
        哪个基本类型的成员占字节数多,就以它大大小为单位开辟内存,
        在 gcc 中出现了 double 类型的,例外
        (1):成员中只有 char 型数据 ,以 1 字节为单位开辟内存。
        (2):成员中出现了 short int 类型数据,没有更大字节数的基本类型数据。
        以 2 字节为单位开辟内存
        (3):出现了 int float 没有更大字节的基本类型数据的时候以 4 字节为单位开辟内存。
        (4):出现了 double 类型的数据
情况 1
        在 vc 里,以 8 字节为单位开辟内存。
情况 2
        在 gcc 里,以 4 字节为单位开辟内存。
        无论是那种环境,double 型变量,占 8 字节。
注意:
        n 如果在结构体中出现了数组,数组可以看成多个变量的集合。
        如果出现指针的话,没有占字节数更大的类型的,以 4 字节为单位开辟内存。
        在内存中存储结构体成员的时候,按定义的结构体成员的顺序存储。
例 19:struct stu{
                        char sex;
                        int age;
                }lucy;
        lucy 的大小是 4 的倍数。
规则 2 :字节对齐
        (1): char 1 字节对齐 ,即存放 char 型的变量,内存单元的编号是 1 的倍数即可。
        (2): short int 2 字节对齐 ,即存放 short int 型的变量,起始内存单元的编号是 2 的倍数即可。
        (3): int 4 字节对齐 ,即存放 int 型的变量,起始内存单元的编号是 4 的倍数即可
        (4): long int 32 位平台下, 4 字节对齐 ,即存放 long int 型的变量,起始内存单元的编号是 4 的倍数即 可
        (5): float 4 字节对齐 ,即存放 float 型的变量,起始内存单元的编号是 4 的倍数即可
        (6): double
a.vc 环境下
        8 字节对齐,即存放 double 型变量的起始地址,必须是 8 的倍数, double 变量占 8 字节
b.gcc 环境下
        4 字节对齐,即存放 double 型变量的起始地址,必须是 4 的倍数, double 变量占 8 字节。
注意 3 :当结构体成员中出现数组的时候,可以看成多个变量。
注意 4 :开辟内存的时候,从上向下依次按成员在结构体中的位置顺序开辟空间
#include<stdio.h>
struct stu{
    char a;
    short int b;
    int c;
}temp;
int main()
{
    printf("%d\n",sizeof(temp));
    printf("%p\n",&(temp.a));
    printf("%p\n",&(temp.b));
    printf("%p\n",&(temp.c));
    return 0;
}
结果分析:
a 的地址和 b 的地址差 2 个字节
b 的地址和 c 的地址差 2 个字节
例 21:temp 的大小为 12 个字节
#include<stdio.h>
struct stu{
    char a;
    int c;
    short int b;
}temp;
int main()
{
    printf("%d\n",sizeof(temp));
    printf("%p\n",&(temp.a));
    printf("%p\n",&(temp.b));
    printf("%p\n",&(temp.c));
    return 0;
}
结果分析:
a c 的地址差 4 个字节
c b 的地址差 4 个字节
例 22:
                struct stu{
                                char buf[10];
                                int a;
                }temp;
                //temp 占 16 个字节
例 23:
        在 vc 中占 16 个字节 a 和 b 的地址差 8 个字节
        在 gcc 中占 12 个字节 a 和 b 的地址差 4 个字节
#include<stdio.h>
struct stu{
    char a;
    double b;
}temp;
int main()
{
    printf("%d\n",sizeof(temp));
    printf("%p\n",&(temp.a));
    printf("%p\n",&(temp.b));
    return 0;
}
为什么要有字节对齐?
用空间来换时间,提高 cpu 读取数据的效率
指定对齐原则:
        使用#pragma pack 改变默认对其原则
格式:
        #pragma pack (value)时的指定对齐值 value
注意:
        1.value只能是: 1 2 4 8
        2.指定对齐值与数据类型对齐值相比取较小值
        说明:咱们制定一个value
(1) :以多少个字节为单位开辟内存
        结构体成员中,占字节数最大的类型长度和value 比较,
        取较小值,为单位开辟内存
例 24:
        #pragma pack(2)
        struct stu{
                char a;
                int b;
        }
2 个字节为单位开辟内存
#include<stdio.h>
#pragma pack(2)
struct stu{
    char a;
    int b;
}temp;
int main()
{
    printf("%d\n",sizeof(temp));
    printf("%p\n",&(temp.a));
    printf("%p\n",&(temp.b));
    return 0;
}
temp 的大小为 6 个字节
a b 的地址差 2 个字节
例 25:
        #pragma pack(8)
                struct stu{
                                char a;
                                int b;
                } ;
4 个字节为单位开辟内存
#include<stdio.h>
#pragma pack(8)
struct stu{
char a;
int b;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
return 0;
}
temp 的大小为 8 个字节
a b 的地址差 4 个字节
(2) :字节对齐
        结构体成员中成员的对齐方法,各个默认的对齐字节数和value 相比,
#include<stdio.h>
#pragma pack(2)
struct stu{
    char a;
    int b;
}temp;
int main()
{
    printf("%d\n",sizeof(temp));
    printf("%p\n",&(temp.a));
    printf("%p\n",&(temp.b));
    return 0;
}

 b成员是2字节对齐,ab的地址差2个字节

#include<stdio.h>
#pragma pack(8)
struct stu{
    char a;
    int b;
}temp;
int main()
{
    printf("%d\n",sizeof(temp));
    printf("%p\n",&(temp.a));
    printf("%p\n",&(temp.b));
    return 0;
}
a b 都按原先的对齐方式存储
如:如果指定对齐值:
设为 1 :则 short int float 等均为 1
设为 2 :则 char 仍为 1 short 2 int 变为 2

位域(位段)

概述
注意:
        1、不能对位域取地址
        2、给位域赋值 不要超过位域的大小(会溢出)
示例
typedef struct data06
{
    unsigned char a:2;
    unsigned char b:2;
    unsigned char c:2;
    unsigned char d:2;
}Data06;
void fun03()
{
    printf("%ld\n",sizeof(Data06));//长度1字节
}
示例 2: 另起一个单元存储
typedef struct data07
{
    unsigned char a:2;
    unsigned char b:2;
    unsigned char c:2;
    unsigned char :0;
    unsigned char d:2;
}Data07;
void fun04()
{
    printf("%ld\n",sizeof(Data07));//长度2
}
示例 3: 无意义位段
typedef struct data08
{
    unsigned char a:2;
    unsigned char b:2;
    unsigned char :2;
    unsigned char d:2;
}Data08;
void fun05()
{
    printf("%ld\n",sizeof(Data08));//长度1
}

共用体

关键字 :union
特点:
        所有成员共用一块空间。
//a b c共用同一块空间(最大的成员类型决定)。
union Data09
{
    char a;
    short b;
    int c;
};
void fun06()
{
    union Data09 d01;
    printf("%ld\n",sizeof(d01));//长度4
}
虽然共用体成员在同一块空间,但是每个成员操作空间的大小 是由成员自身类型决定。
union Data09
{
    char a;
    short b;
    int c;
};
void fun07()
{
    union Data09 d01;
    d01.a = 10;
    d01.b = 20;
    d01.c = 30;
    printf("%d\n",d01.a+d01.b+d01.c);
}
union Data09
{
    char a;
    short b;
    int c;
};
void fun08()
{
    union Data09 d01;
    d01.a = 0x01;
    d01.b = 0x0102;
    d01.c = 0x01020304;
    printf("%d\n",d01.a+d01.b+d01.c);
}

枚举

关键字 :enum
特点:
        限定其值的范围
概念 : 枚举就是将枚举变量可以赋的值一一列举出来
语法
        enum 枚举名称
                          {
                                        值1,
                                        值2,
                                        值3,
                                         ...
                                        值n
                        }
注意 :
        枚举中的值为符号常量, 也是枚举变量可以赋的值
        枚举列表中的值从0 递增
enum Type
{
    OK,
    ERROR,
    OVER
};
void fun08()
{
    enum Type t1 = OK;
    enum Type t2 = ERROR;
    enum Type t3 = OVER;
    printf("%d %d %d\n",OK,ERROR,OVER);
}
enum Type2
{
    NUM01,
    NUM02 = 3,
    NUM03
};
void fun09()
{
    enum Type2 t1 = NUM01;
    enum Type2 t2 = NUM02;
    enum Type2 t3 = NUM03;
    printf("%d %d %d\n",NUM01,NUM02,NUM03);
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值