C语言结构体详解

        C语言中用数组表示一组相同类型元素的集合,要表示不同类型元素的集合就要用到结构体。

结构体的声明

        结构体的标准格式如下:

//struct是关键字,tag是结构体标签,member-list是结构体成员列表,variable-list是结构体变量列表,就是在结构体声明的同时创建的变量(也可以没有)
struct tag
{
    member-list;
}variable-list;//这里的分号不能省略

        例如要描述一个学生类型:

struct student
{
    char name[20];//姓名
    int age;//年龄
    char id[20];//学号
};//分号不能省略

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

        结构体变量的定义有以下两种方式:

struct point
{
    int x;
    int y;
}p1;//在声明结构体类型的同时创建变量


int main()
{
    struct point p2;//定义结构体变量p2,此时的struct point相当于一个数据类型,和int等数据类型类似,注意struct不能省略
}

        在定义变量的同时为变量的成员赋值就是初始化:

struct point
{
    int x;
    int y;
};
struct Point p3 = {x, y};//创建变量同时赋值
struct Stu        //类型声明
{
     char name[15];//名字
     int age;      //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
     int data;
     struct Point p;
     struct Node* next; 
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

结构体成员访问

        结构体变量访问成员使用.操作符,结构体指针访问成员使用->操作符:

struct Stu
{
     char name[20];
     int age;
};
void print(struct Stu* ps)
{
    //使用结构体指针访问指向对象的成员
     printf("name = %s   age = %d\n", ps->name, ps->age);
}
int main()
{
    struct Stu s = {"zhangsan", 20};
    printf("name:%s age:%s\n",s.name,s.age);//结构体变量访问成员
    print(&s);//结构体地址传参
    return 0;
}

结构体传参

struct S
{
     int data[1000];
     int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
     printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
     printf("%d\n", ps->num);
}
int main()
{
     print1(s);  //传结构体
     print2(&s); //传地址
     return 0;
}

        上面两个print函数中print2更好,因为值传递需要临时拷贝一份结构体变量,如果结构体很大会加大内存开销,降低性能,所以结构体传参最好用地址传递

匿名结构体

        结构体类型声明时可以没有标签tag,称为匿名结构体,但是这种结构体只能用一次,下面举个例子:

//匿名结构体类型
struct
{
     int a;
     char b;
     float c;
}x;//只能在这里定义变量,因为没有标签,不能在其他地方定义变量

struct
{
     int a;
     char b;
     float c;
}a[20], *p;

p = &x;//这样写是不可以的,p是匿名结构体指针,x是匿名结构体变量,虽然两个结构体的成员相同,但是也视为是两种不同的结构体。

结构体的自引用

        是否可以在结构体中包含一个该结构体类型的成员呢:

//代码1
struct Node
{
     int data;
     struct Node next;//结构体成员的类型和结构体相同
};
//上面的写法是不可行的,如果可以,sizeof(struct Node)求不出来

//下面是正确的方法,链表的节点就是这样的结构体
//代码2
struct Node
{
     int data;
     struct Node* next;//这里存放指向下一个节点的地址
};

结构体内存对齐

        结构体是不同类型的元素的集合,那么不同结构体的大小怎么计算呢,这就涉及到结构体的内存对齐,下面是结构体内存对齐的规则:
①第一个成员在与结构体变量偏移量为0的地址处,也就是结构体的起始位置。

②其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
        对齐数=编译器默认的一个对齐数 与 该成员大小的较小值

        vs中默认对齐数为8,Linux中没有默认对齐数,对齐数就是成员自身的大小

③结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍

④如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

        下面举个例子说明一下:

        c1的大小是1,默认对齐数是8,所以根据规则2,c1的对齐数是1,对齐到0的地址处是可以的。

        i的大小是4,默认对齐数是8,所以根据规则2,i的对齐数是4,因为c1只占了1个字节,i又要对其到4的整数倍,所以需要浪费三个字节,i从偏移量4的位置开始存储
        c2同c1,对齐数是1,i占四个字节,c2从偏移量8的位置开始是可以的。
        又因为规则3,总大小应该是最大对齐数的整数倍,c1和c2对齐数是1,i的对齐数是4,所以最大对齐数是4,目前的总大小是9,因此总大小应该增加到4的整数倍,也就是12,c2后面要再浪费三个字节的空间。

        根据上面的例子可以得出下面的结论:

        在设计结构体类型时,尽量让占用空间小的成员集中在一起

        默认对齐数可以自行修改:

#pragma pack(8)//设置默认对齐数为8
struct S1
{
     char c1;
     int i;
     char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-26 02:14:45       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-26 02:14:45       100 阅读
  3. 在Django里面运行非项目文件

    2024-05-26 02:14:45       82 阅读
  4. Python语言-面向对象

    2024-05-26 02:14:45       91 阅读

热门阅读

  1. Django搭建和数据迁移

    2024-05-26 02:14:45       29 阅读
  2. 【Redis精通之路】数据类型(2)

    2024-05-26 02:14:45       30 阅读
  3. 删除有序数组中的重复项-力扣

    2024-05-26 02:14:45       33 阅读
  4. springboot项目跨域设置总结

    2024-05-26 02:14:45       28 阅读
  5. Flink 任务调度策略:Eager 模式详解

    2024-05-26 02:14:45       31 阅读
  6. Flink 任务调度策略:Lazy from Sources 深入解析

    2024-05-26 02:14:45       29 阅读
  7. 海外投放面试手册

    2024-05-26 02:14:45       39 阅读