C语言之结构体

C语言之结构体

结构体

结构体的概念

是用户自定义的一种数据结构,它可以把不同类型的数据结构组合成一个有机的整体。有利于数据的管理

例子:一个学生有学号、姓名、性别、年龄

int num;
char name[20];
char sex;
int age;
// 显然单独定义这些变量,不利于数据的管理,所有C语言就发明了结构体类型

结构体属于构造类型

构造类型: 不是基本类型,也不是指针类型。它是由若干个相同类型或不同类型的数据结构的组合。如数组、结构体。

数组:描述一组相同数据类型的有序集合,用于处理大量相同类型的数据

结构体的定义

1.一般定义形式
struct 结构体名{
    数据类型 成员变量1;
    数据类型 成员变量2;
    ……
    数据类型 成员变量n;
};

struct 结构体名 变量名;

struct stu{
    int num;
	char name[20];
	char sex;
	int age;
};

struct stu student1;
2.在定义的时候就声明变量
struct 结构体名{
    数据类型 成员变量1;
    数据类型 成员变量2;
    ……
    数据类型 成员变量n;
} 变量1,变量2;

struct stu{
    int num;
	char name[20];
	char sex;
	int age;
} student1,student2;

3.定义匿名结构体
struct {
    数据类型 成员变量1;
    数据类型 成员变量2;
    ……
    数据类型 成员变量n;
} 变量1,变量2;
// 这种形式只能在定义结构体的时候声明变量。
struct {
    int num;
	char name[20];
	char sex;
	int age;
} student1,student2;
4.最常用的定义方式

通常用typedef关键字将结构体类型取一个别名,用这个别名代表原本的数据类型。

typedef struct 结构体名{
    数据类型 成员变量1;
    数据类型 成员变量2;
    ……
    数据类型 成员变量n;
}STU;

SIU student1;
// SIU student1;等价与struct 结构体名 student1; 

typedef struct stu{
    int num;
	char name[20];
	char sex;
	int age;
}STU;

struct stu student1;

结构体变量

结构体变量是一个变量,是包含多种数据类型的数据的集合。

结构体中包含多个成员变量。

1.在定义结构体变量的时候初始化
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct stu
{
    int num;         // 学号
    char zhanye[20]; // 专业
    char name[20];   // 姓名
    char sex[3];     // 性别
} STU;
int main(int argc, char *argv[])
{
    STU p = {// 在定义变量的时候顺序初始化
             10001,
             "computer",
             "zhangsan",
             "男"};
    STU q = {.num = 10002, // 在定义变量的时候指定成员初始化
             .zhanye = "match",
             .name = "lisi",
             .sex = "女"};

    printf("学号: %d\n", p.num); // 变量采用变量.成员变量的形式访问对应的数据。
    printf("专业: %s\n", p.zhanye);
    printf("姓名: %s\n", p.name);
    printf("性别: %s\n", p.sex);

    printf("学号: %d\n", q.num);
    printf("专业: %s\n", q.zhanye);
    printf("姓名: %s\n", q.name);
    printf("性别: %s\n", q.sex);
}
2.结构体变量的使用
  1. 结构体变量成员变量的引用

    通过 结构体变量.成员变量 的形式引用

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    typedef struct stu{
        int num; //学号
        char zhanye[20]; //专业
        char name[20]; //姓名
        char sex[3]; //性别
        char *addr;
    }STU;
    int main(int argc, char *argv[])
    {
        STU lihua;
        lihua.num = 1001;
       // lihua.name = "lihua";//错误的,name是数组名,是常量,不能直接用“=”赋值
        strcpy(lihua.name,"lihua");
        strcpy(lihua.addr,"翻斗花园");//错误的,addr目前是野指针
        lihua.addr = "翻斗花园";
    }
    
3.相同类型的结构体变量赋值

相同类型的结构体:当两个结构体的成员变量列表(包括成员变量的类型、顺序)完全一致时,它们就是相同类型的结构体,结构体名可以不同。

只要当结构体类型和结构体名都一致的时候,其声明的变量才能相互之间赋值。仅仅是结构体类型一样(即成员变量列表完全一致)但结构体名不一样的两个结构体变量之间是不能相互赋值的。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct stu1{
    int num; //学号
    char zhanye[20]; //专业
    char name[20]; //姓名
    char sex[3]; //性别
    char *addr;
}STU1;
typedef struct stu2{
    int num; //学号
    char zhanye[20]; //专业
    char name[20]; //姓名
    char sex[3]; //性别
    char *addr;
}STU2;
int main(int argc, char *argv[])
{
    struct stu1 p1,p3;
    struct stu2 p2;
    p1.num = 1001;
    // p2 = p1; // 错误的,因为结构体名不一样,不能够直接整体赋值
    p3 = p1; // 正确的,因为结构体类型和结构体名都一样,可以整体赋值
    printf("%d\n",p3.num);
    return 0;
}

结构体指针

结构体指针的定义

结构体指针是指向结构体的指针,当定义一个结构体指针时,这个指针可以存储结构体变量在内存中的地址。

struct Student {
    char name[20]; 
    int age;
    float score;
} *q; // 可以在这里直接定义结构体指针

typedef struct Student{
    char name[20];
    int age;
    float score;
} STU, *PSTU; // //  定义结构体别名STU和结构体指针别名PSTU

struct Student *p; // 也可以像这样定义结构体指针
STU *a; // 这样也行
PSTU c,d; // 定义了结构体指针变量c、d
// C语言中任何指针变量在32位系统中占4,在64系统位中占8个字节。
结构体指针的初始化
1.通过取地址运算符&获取已定义的结构体变量的地址来初始化结构体指针。
#include <stdio.h>
typedef struct Student
{
    char name[20];
    int age;
    float score;
} STU;
int main()
{
    STU stu1 = {"lihua", 18, 99.9};
    STU *p = &stu1;
    
    STU stu2;
    STU *q=&stu2;
    *q = (STU){"bob", 20, 60.1};
    
    printf("q->name = %s\n", q->name);
    printf("q->age = %d\n", q->age);
    printf("q->score = %f\n", q->score);
}

2.动态分配内存并初始化
#include <stdio.h>
#include <stdlib.h>
typedef struct Student
{
    char name[20];
    int age;
    float score;
} STU;
int main()
{

    STU *p = (STU *)malloc(sizeof(STU));// 动态分配内存
    *p = (STU){"zhangsan", 18, 99.9};
    printf("%s %d %.1f", p->name, p->age, p->score);
}

结构体指针指向内存的成员变量的访问和赋值

引用形式

指针变量名->成员变量名 或者 (*指针变量名).成员变量名。

1.p->age   或者 2.(*p).age
第二种方式相当于是先用解引用将指针降级成变量,然后用变量的方式访问。(较为繁琐)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Student
{
    char name[20];
    int age;
    float score;
} STU;
int main()
{
    STU *p = (STU *)malloc(sizeof(STU));
    *p = (STU){"zhangsan", 18, 99.9};
    p->age = 33;
    (*p).score = 10.1;
    strcpy(p->name, "lisi"); // 成员变量的name是个数组名,不能直接用"="赋值
    printf("%s %d %.1f\n", (*p).name, (*p).age, (*p).score);
    printf("%s %d %.1f\n", p->name, p->age, p->score);
}

注意
  1. 结构体变量的地址与第一个成员变量的地址一样,当其数据类型不一样

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    typedef struct stu{
        int num;
        char name[20];
        char sex[10];
    } STU, *PSTU;  //  定义结构体别名STU和结构体指针别名PSTU
    
    int main(){
        STU stu;
        printf("&stu=%p\n", &stu); // 类型是STU *
        printf("&stu.num=%p", &stu.num); // 类型是int *
    }
    PS E:\code\c_code> cd "e:\code\c_code\" ; if ($?) { gcc test.c -o test } ; if ($?) { .\test }
    &stu=0xffffcbf0
    &stu.num=0xffffcbf0
    

结构体数组

结构体数组是一个数组,由若干个相同类型的结构体构成的一个有序集合。

结构体数组的定义方法
struct 结构体类型名 数组名[元素个数]
例子:
struct stu{
    int num;
    char name[20];
    char sex[10];
};
struct stu edu[3]; // 定义了struct stu类型的结构体数组edu,这个数组有三个元素,分别是edu[0]、edu[1]、edu[2]
结构体数组的引用

数组名[下标]

*(数组名 + 下标)

和普通的数组一样

对结构体数组的单个元素的成员变量的引用
edu[0].num = 100; // 用100给结构体数组edu的第0个元素的成员变量num赋值。
strcpy(edu[1].name,"bob");

案例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct stu{
    int num;
    char name[20];
    char sex[10];
} STU, *PSTU;  //  定义结构体别名STU和结构体指针别名PSTU

int main(){
    STU suts[3] = {{1,"zhangsan","男"},{2,"lisi","女"},{3,"wangwu","男"}};// 初始化结构体数组
    int suts_size = sizeof(suts) / sizeof(suts[0]); // 获取数组长度
    for (int i = 0; i < suts_size; i++){
        printf("num:%d, name:%s, sex:%s\n", suts[i].num, suts[i].name, suts[i].sex);
    }
    printf("\n");
    suts[0].num = 4; //  修改结构体数组0号元素的成员变量num
    strcpy(suts[0].name, "zhaoliu"); //  修改结构体数组0号元素的成员变量name

    suts[2] = (STU){5, "夏波", "女"}; //  修改结构体数组2号元素的所有成员变量
    PSTU p = suts; //  定义结构体指针指向结构体数组
    for(int i = 0; i < 3; i++){
        printf("num:%d, name:%s, sex:%s\n", p->num, p->name, p->sex);
        p++;
    }
}

PS E:\code\c_code> cd "e:\code\c_code\" ; if ($?) { gcc test.c -o test } ; if ($?) { .\test }
num:1, name:zhangsan, sex:男
num:2, name:lisi, sex:女
num:3, name:wangwu, sex:男

num:4, name:zhaoliu, sex:男
num:2, name:lisi, sex:女
num:5, name:夏波, sex:

结构体对齐规则

概念
  • 结构体是一种用户自定义的数据类型,它允许将不同类型的数据组合在一起。在计算机存储结构体变量时,为了提高存储和访问效率,存在结构体对齐规则。
偏移量
  • 在结构体(以及其他一些数据存储结构)的语境中,偏移量是指一个成员变量相对于结构体起始地址(也就是结构体第一个字节的地址)的字节数差值。它用于确定每个成员在结构体内部的具体存储位置。
基本对齐规则
  • 规则一:数据成员对齐
    • 结构体的第一个数据成员的地址和结构体变量的地址相同,即偏移量为 0。例如,有一个结构体struct S {int a; char b;},成员a的存储地址就是结构体变量存储地址的起始位置。
  • 规则二:按自身大小对齐(基本数据类型)
    • 对于基本数据类型(如charshortintlongfloatdouble等),每个成员变量的存储起始位置必须是其自身大小(字节数)的整数倍。例如,32位系统中int类型一般占 4 个字节,那么int类型的成员存储的起始位置应该是 4 的倍数。
    • struct S {char a; int b;}为例,char类型占 1 个字节,它可以从偏移量为 0 的位置开始存储。而int类型占 4 个字节,它的存储起始位置应该是 4 的倍数。由于char已经占用了 1 个字节,所以int成员b的存储起始位置是 4(偏移量为 4),在a之后会有 3 个字节的填充。
  • 规则三:结构体整体对齐
    • 结构体的总大小必须是其内部最大(基本类型)成员大小(字节数)的整数倍。继续上面的struct S {char a; int b;}例子,内部最大(基本类型)成员是int,占 4 个字节,而结构体S目前大小为 1(char)+ 3(填充)+ 4(int)=8 字节,8 是 4 的倍数,所以结构体S的大小为 8 字节。
// 32位机
struct S{
    char a; //a占编号0,由于c是int类型,从编号4开始,故此处偏移量为3个字节
    int c; // 到这里共8个字节
    short b; // 到这里10个字节 
} // 整体对齐,最大基本类型成员int的整数倍,故补2个字节
// 总字节: 1 + 3 + 4 + 2 + 2 = 12

在这里插入图片描述

嵌套结构体对齐规则
  • 当结构体中有嵌套的结构体时,嵌套结构体的对齐要求和普通成员一样,它的起始存储位置是它内部最大成员大小的整数倍。并且,结构体的总大小也需要考虑嵌套结构体的对齐情况。

  • //32位系统
    struct Inner {
        char c; 
        int d;
    }; // 8个字节
    struct Outer {
        char a;  // 以int大小开辟空间,占4个字节
        struct Inner in;
    }
    

    对于Inner结构体,大小为 8 字节(前面分析过类似结构)。对于Outer结构体,char a占 1 个字节,然后Inner结构体的起始位置要按照Inner内部最大成员(int,4 字节)对齐,所以在a之后会有 3 个字节填充,Inner结构体占 8 字节,所以Outer结构体总大小为 1 + 3+ 8 = 12 字节。

指定对齐原则

使用#pragma pack(value) 改变默认对齐

格式:#pragma pack(value)指定对齐值是value

注意:

#pragma pack 指令

  • 语法和作用

    • 语法:#pragma pack(value),其中value是一个正整数,表示字节对齐数。这个指令告诉编译器按照value字节的边界来对齐结构体的成员。
    • 作用:它可以改变编译器默认的对齐规则,使结构体成员的存储更加紧凑或者按照特定的要求对齐。当设置n = 1时,结构体成员将紧密排列,没有额外的填充字节,这种方式可以节省内存空间,但可能会牺牲一些访问效率。
  • 注意: value的取值只能为0、1、2、4、8、16。取0时是系统默认对齐原则。

    // 32位系统中
    #include <stdio.h>
    #pragma pack(2) // 以2字节指定对齐
    struct test
    {
        char a; // a占编号0,偏移量为1,  // 1+1 = 2字节
        int b;  // 占编号2、3、4、5,偏移量为0 // 2 + 4 = 6字节
        double c; // 占编号6、7、8、9、10、11、12  // 6 + 8 = 14字节
    };
    int main()
    {
        printf("size of char: %d\n", sizeof(char));
        printf("size of int: %d\n", sizeof(int));
        printf("size of double: %d\n", sizeof(double));
        printf("size of struct test: %d\n", sizeof(struct test));
    }
    PS E:\code\c_code> cd "e:\code\c_code\" ; if ($?) { gcc test.c -o test } ; if ($?) { .\test }
    size of char: 1
    size of int: 4
    size of double: 8
    size of struct test: 14
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

繁星北斗

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

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

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

打赏作者

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

抵扣说明:

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

余额充值