C语言:自定义类型(进阶)

1.结构体

1.1结构体的基础知识


即不同类型的集合:
类比数组,数组是同一类型的集合
而结构体可以是不同类型的集合

1.2结构体声明


struct stu
{
    int age;
    char name[20];
    char tel[12];
}s1, s2; //可直接在该处创建结构体变量(对象),此时的变量s1, s2为全局变量

int main()
{
    struct stu s = { 18, "zhangsan", "1008611" };//也可以在此处创建结构体变量(对象),此时s为局部变量
    printf("%d %s %s", s.age, s.name, s.tel);
    return 0;
}


64cc4159c0524f2986c1527391f7a525.png

1.3结构体的特殊声明

结构体匿名
struct            //此时没有声明结构体即结构体匿名,但该写法在语法上亦支持,该写法只能调用一次
{
    int age;
    char name[20];
    char tel[12];
}s;

1.4结构体的自引用

struct Node
{
    int date;
    struct Node* next;
};

int main()
{
    sizeof(struct Node);
    return 0;
}

写法1:
typedef struct Node
{
    int date;
    struct Node* next;
}*linklist;

写法2:
struct Node
{
    int date;
    struct Node* next;
};
typedef struct Node* linklist;

ea399d4e8ddc46ff84f4cfb6248e18d5.png

1.5结构体变量的定义和初始化

struct num
{
    int x;
    int y;
}s = {2, 3};//此时即创建变量并且初始化

struct stu 
{
    int age;
    char ch[20];
    struct num n;
}s2 = { 18, "zhangsan", {3, 4} };//结构体嵌套初始化

int main()
{
    struct stu s = { 18, "zhangsan", {3, 4} };
    printf("%d %s %d %d", s.age, s.ch, s.n.x, s.n.y);
    return 0;
}

1.6结构体内存对齐

结构体对齐规则:
1.结构体内首成员位于结构体变量的首地址处
2.其他成员要对齐到某个数字(对齐数)的整数倍的地址处
对齐数=编译器默认的一个对齐数与该成员大小的较小值
vs默认值为:8
3.结构体的总大小为最大对齐数的整数倍(每个成员都有一个对齐数)
4.嵌套结构体的情况:嵌套的那个结构体对齐到自己最大对齐数的整数倍处

eg1:

struct s1
{
    char c1;//该成员对齐数:默认值8与该类型所占字节1相比,选取较小值为对齐数,故c1对齐数为1
    int i;  //默认值为8,该类型所占字节为4,选取较小值,故i对齐数为4;因为1不为4的整数倍,故往下
    char c2;//同理c2对齐数为1,此时8为1的倍数,故直接存放进去
};

int main()
{
    printf("%d\n", sizeof(struct s1));//结构体总体大小为最大对齐数的整数倍
    return 0;
}

d865d77181d546feb5a93630bdcbb4a4.png
eg2:

struct s2
{
    char c1; //按上述规则对齐数c1为1
    char c2; //c2为1
    int i;   //对齐数为4  
};

int main()
{
    printf("%d\n", sizeof(struct s2));
    return 0;
}

8ff3360dd88c49d38049274ad40faaa0.png

eg3:

struct s2
{
    char c1; //按上述规则对齐数c1为1
    char c2; //c2为1
    int i;   //对齐数为4  
};
struct s3
{
    char c1;
    char c2;
    struct s2 s;//嵌套的结构体将对齐到自己最大对齐数的整数倍处,由上述可知,s2最大对齐数为4,此时对齐到4
};

int main()
{
    printf("%d", sizeof(struct s3));
}

89dfe70848e74326aa844cc7bcb621ad.png

1.7修改默认对齐数


#pragma pack(1)//此时将默认值修改为1

struct s2
{
    char c1; //按上述规则对齐数c1为1
    char c2; //c2对齐数为1
    int i;   //对齐数为1  
};

struct s3
{
    char c1;
    char c2;
    struct s2 s;//嵌套的结构体将对齐到自己最大对齐数的整数倍处,由上述可知,s2最大对齐数为1,此时对齐到2直接存放
};

int main()
{
    printf("%d", sizeof(struct s3));
}
#pragma pack();//代表修改的默认值在该处中止

06ca262b8f8b430c8d6755d10436f608.png


1.8结构体传参


更具体内容可看前文结构体(初阶)文章

struct S 
{
    int arr[3];
    int num;
};

void print1(struct S s)//传值在该处再次开辟一份空间copy实参,大大浪费内存,效率低
{
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d ", s.arr[i]);
    }
    printf("%d\n", s.num);
}

void print2(const struct S* s)//保护该指针所指向的对象防止被修改
{
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d ", s->arr[i]);
    }
    printf("%d\n", s->num);
}

int main()
{
    struct S s = { {1, 2, 3}, 66 };
    print1(s);   //传值
    print2(&s);  //传址
    return 0;
}

2.位段

2.1位段概念  

位段用于结构体上:
  位段的成员必须是整形家族(如:int, unsigned int, signed int, char)
  成员名后必须加冒号和数字
eg:
struct s       //此时s就是一个位段类型
{
    int _a : 3;//位段用法:变量名后加冒号和数字
    int _b : 4;//数字表示_b只要分配4个比特位的内存
    int _c : 5;
};

2.2位段的内存分配

在各个平台的内存分配方式不一样,也没统一的标准规定位段的分配方式,所以位段的跨平台应用性比较差
代码移植性低,但可以节省内存空间
 

 

3.枚举

枚举类型同结构体,int, float, 等等一样,是个类型
枚举类型是个自定义类型
eg:

enum day //此时day就是个枚举类型,类似于结构体的声明,声明这个枚举类型
{
    mon = 1,  //此时mon,thus等为枚举常量,枚举常量是常量,有具体值
    thus,     //默认枚举类型里的第一个枚举常量默认赋值为零
    wed = 5,  //后面枚举常量值依次加1,即thus==1, wed==2以此类推
    thur,
    fri,  //枚举常量在初始化时赋值,如:mon=1,此时直接赋值为1
    sat,  //后面的枚举常量初始化时一样可以赋值,如wed=5,此时即为5、
    sun   //没有赋具体值的枚举常量就在前一个枚举常量的值的基础上加1
};

int main()
{
    printf("%d\n", mon);  //此时mon为1
    printf("%d\n", thus); //mon基础上加1为2
    printf("%d\n", wed);  //此时wed初始化赋值为5
    printf("%d\n", thur); //wed基础上加1为6
    return 0;
}

282ec312a18a4156b6934c81fc79118f.png
枚举优点:

1.增加代码可读性和可维护性
2.和define定义的类型相比枚举类型有类型检查,逻辑更严谨
3.便于调试
4.一次性定义多个常量

4.联合(共用体)

4.1联合体定义特点与结构体差别: 


联合体在内存中各个成员共用一块内存空间

在很大程序上可以节省内存空间

union Un    //联合体也是自定义类型:内部成员共用一块内存空间
{           //union为联合体(与结构体相类似去来理解)
            //Un为联合体声明,类似于(结构体声明)
    char c;
    int i;
}u;         //此时创建联合体变量(对象)u

int main()
{

    printf("%p\n", &u);
    printf("%p\n", &u.c);
    printf("%p\n", &u.i);
    
    return 0;
}


f6725961527e4543b93852f28a126ebf.png

int main()
{
    u.c = 1;
    printf("%d\n", u.c);

    u.i = 2;
    printf("c=%d\n", u.c);
    printf("i=%d\n", u.i);
    /*因为他们用的是共同内存,调用时该内存就会被覆盖,故不能同时调用该内存赋值
        该内存所存的值为最后一次调用创建的值*/
}

4cbac05434f64dc886ca56b2d6c730ca.png

02ccb59c322b49dd9188a1be04ed4aeb.png4519fc12f946407c9dba7f8c8b2b88ee.png

4.2联合体大小计算

1.与结构体一样有同样的对齐规则

2.所开辟的空间最小为内部最大成员所占空间

eg1:

union Un
{
	char ch; //所占字节1,对齐数为1
	int i;   //所占字节数4,对齐数为4
}u;

int main()
{
	printf("%d", sizeof(u));
	//此时联合体对齐数为4,
	return 0;
}

c11b8e87da134e0c89a0688eb2880ebf.pngfb5e145d48224c17acc9e066080464f6.pngeg2:

union Un
{
	char ch[5]; //在内存中占用5个字节,对齐数为1
	int i;      //在内存中占用4个字节,对齐数为4
}u;

int main()
{
	printf("%d", sizeof(u));
	//联合体对齐数为内部成员最大对齐数,此时对齐数为4
	//而5不是4的整数倍,联合体仍需往下开辟空间
	//当开辟到8时为对齐数的整数倍,停止开辟
	//多开辟出来的字节数为浪费的字节数
	return 0;
}

4d57c44dc86b4b15b5c02592de5a60bb.png9d9446677c304adebb83504dd058116a.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

食懵你啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值