自定义类型(结构体、枚举、联合)以及内存对齐问题

结构体

结构体是一些值的集合,这些值称为成员变量,结构体每个成员可以使不同的变量

结构体声明

结构体的普通声明

struct tag
{
    member list;
}variavle list;

//举例
//描述一个学生的信息(姓名、年龄、性别、成绩)
struct Stu
{
    char name[10];
    int age;
    char sex;
    int grade[5];
};//定义结束处分号不可省略

结构体声明时可以不完全声明,例如:

//匿名结构体类型
struct
{
    int a;
    char b;
    float c;
}x;//创建一个x结构体变量

struct
{
    int a;
    char b;
    float c;
}*p;//创建一个y结构体变量指针

    //问题,上面基础下,下面代码是否合法
    p = &x;
    //未声明结构体即使内部成员相同,仍为两个不同类型结构体,因此该句非法

小结结构体创建

1.结构体内部成员不可省略,至少一个
2.结构体名称尽量不省略,即tag
3.变量可以省略

结构体访问

//以学生信息结构体为例
struct Stu
{
    char name[10];
    int age;
    char sex;
    int grade[5];
};

    sturct Stu x;
    sturct Stu y, *p = &x;
    //创建两个x和y结构体和一个结构体指针p
    //访问时可以通过下面方式访问
    p->age = x.age;
    strcpy(x.name, p->name);
    (*p).sex = M;

结构体自引用

//错误自引用
struct Node
{
    int data;
    struct Node next;//错误,无法确定结构体的大小
};

//正确的自引用
struct Node
{
    int data;
    struct Node* next;//正确,指针大小确定
};

//注意一下结构体互相引用的问题
//在前面创建的结构体想要引用后面的结构体必须先声明
struct B;
struct A
{
    int data;
    struct B* next;
};

struct B
{
    int data;
    struct A* next;
};

结构体初始化

结构体不能直接整体赋值,但可以整体初始化,初始化同数组一样使用花括号{}
例如:

struct Stu A = { Tom, 15, M, { 95, 87, 90, 92, 86 }};

结构体的内存对齐

结构体的大小并不是结构体每个成员大小简单的相加,而是按照一定规则进行存储,这个规则便是内存对齐

struct A
{
    char c1;
    int i;
    char c2;
};

struct B
{
    char c1;
    char c2;
    int i;
};

    sizeof(struct A) =  12;
    sizeof(struct B) =  8;//成员相同,但是所占大小却不同
内存对齐规则

1.第一个成员在与结构体变量偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认的值为8,linux中的默认值为4)
3.结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

为什么要有内存对齐

1、平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

结构体传参问题

数组传入函数时会发生降级,变成相对应的指针。
结构体不同,他传入时不发生降级

位段

什么是位段

位段的声明同结构体相似,但有两个不同:
1.位段的成员必须是整型
2.位段成员名后必须跟一个冒号和数字。

例如:

struct S
{
    int a:2;
    int b:5;
    int c:10;
    int d:20
};

    sizeof(struct A);//大小为2字节

位段按照我的理解即用位存储数字,在声明时就告知其可使用的位段大小,比如给两位,那么他的最大可存11即3,给五位可存最大为11111即31, 且位段存储的溢出不影响相邻的位段成员,简单来说,溢出后果自负

提醒

1.位段的成员可以是int也可以是char(整型均可)
2.位段空间按照4个字节(int)(32位)或者1个字节(char)(8位)分配
3.位段有很多不确定因素,位段不跨平台,所以可移植的程序需要避免使用位段
4.在不考虑跨平台的情况下,位段能很好地节省内存空间来存储

枚举

把可能的值一一列举出来,在c语言里称之为枚举(enum)
例如:

enum week
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};

枚举的变量其实是有值的,若不赋初值,则从0开始,向后依次递增1
也可以直接赋值,方便使用

枚举的优点

1.增加代码的可读性和可维护性
2.和#define相比,枚举具有类型检查,更加严谨
3.防止命名污染(封装)
4.便于调试
5.使用方便,可以一次定义多个常量

联合

联合即一些成员共同使用一块内存空间,即所有成员的首地址相同

union U
{
    int a;
    char b;
};

联合体的大小由占据内存最大的成员决定

联合体的应用

十进制点分ip地址

union ip_addr
{
    unsigned long addr;
    struct
    {
        unsigned char c1;
        unsigned char c2;
        unsigned char c3;
        unsigned char c4;
    }ip;
};

    union ip_addr ip;
    ip.addr = 176238749;
    printf("ip addr: %d.%d.%d.%d", ip.c1, ip.c2, ip.c3, ip.c4);

结构体应用:通讯录系统(点击跳转至通讯录系统的博客)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值