C语言结构体的相关知识

前言

从0开始记录我的学习历程,我会尽我所能,写出最最大白话的文章,希望能够帮到你,谢谢。


1.结构体类型的概念及定义

1.1、概念:

        结构体是一种构造类型的数据结构, 是一种或多种基本类型或构造类型的数据的集合。
        它允许我们组合多个基本数据类型(如整数、浮点数、字符等)来创建一个新的复合数据类型。这个新的数据类型就像一个小包裹,里面装着了我们需要的不同类型的数据。 

1.2、 结构体类型的定义方法 

咱们在使用结构体之前必须先有类型,然后用类型定义数据结构,这个类型相当于一个模具

(1).先定义结构体类型,再去定义结构体变量

struct 结构体类型名{

成员列表(允许我们组合多个基本数据类型(如整数、浮点数、字符等))

};

例1:

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

我们看这个例子。

我们定义了一个名为stu的结构体类型。这个结构体类型包含了三个成员:一个整数num,一个字符数组name,和一个字符sex。这些成员代表了学生的学号、姓名和性别。

struct stu lucy, bob, lilei;

一旦我们定义了这个结构体类型,就可以创建变量来存储实际的学生信息了。在这个例子中,我们创建了三个变量:lucyboblilei。每个变量都是一个结构体类型的实例,它们都有三个成员:numnamesex

在定义结构体类型的时候顺便定义结构体变量,以后还可以定义结构体变量
struct 结构体类型名 {
成员列表 ;
} 结构体变量 1, 变量 2;
struct 结构体类型名 变量 3 ,变量 4

例如看下面这个例子,实际上是和上面的例子是一个意思的。

struct stu{
int num;
char name[20];
char sex;
}lucy;
struct stu bob, lilei;

在定义结构体类型的时候,没有结构体类型名,顺便定义结构体变量,

因为没有类型名,所以以后不能再定义相关类型的数据了。也就是不可以往成员列表里面添加东西了。
struct {
成员列表 ;
} 变量 1 ,变量 2;

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

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

结构体变量,是个变量,这个变量是若干个数据的集合

注:
(1): 在定义结构体变量之前首先得有结构体类型,然后在定义变量
(2): 在定义结构体变量的时候,可以顺便给结构体变量赋初值,被称为结构体的初始化
(3): 结构体变量初始化的时候,各个成员顺序初始化
struct stu{
int num;
char name[20];
char sex;
};
struct stu boy;
struct stu lucy={101,"lucy",'f'};

我们创建了两个变量来存储实际的学生信息。第一个变量boy它是一个未初始化的结构体变量,它没有任何成员的值被赋定。第二个变量lucy是一个已经初始化的结构体变量。在声明时,我们直接给它赋初值。这里,我们给了学号、姓名和性别的值。

在这个初始化中,我们按照成员的顺序给了值:

  • 101 是 num 成员的值。
  • "lucy" 是 name 成员的值。由于 name 是一个字符数组,我们使用引号括起来来表示字符串。
  • 'f' 是 sex 成员的值。

 1、结构体变量的使用

定义了结构体变量后,要使用变量

(1). 结构体变量成员的引用方法
        结构体变量. 成员名
struct stu{
int num;
char name[20];
int age;
}bob;
int main(int argc, char *argv[])
{
lihua={10,"bob",10};
printf("%d\n",bob.num);
printf("%s\n",bob.name);
printf("%d\n",bob.age);
return 0;
}

 可以看到 通过.操作符,我们可以访问结构体变量的各个成员。上面的代码分别打印出了bob的学号、姓名和年龄。

2、结构体成员多级引用

        首先,我们有一个名为date的结构体,它包含了三个整数字段:yearmonthday。这些字段代表了一个特定的日期。

struct date{
    int year;
    int month;
    int day;
};

        接下来,我们有一个名为stu的结构体,它包含了四个字段:一个整数num、一个字符数组name、一个字符sex和一个子结构体birthday。子结构体birthday是我们之前定义的date结构体。

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

        现在,我们在主函数中创建了一个名为lilei的变量,它是一个stu类型的变量。我们通过赋值来初始化这个变量的字段。

struct stu lilei={101,"lilei",'m'};
lilei.birthday.year=1986;
lilei.birthday.month=1;
lilei.birthday.day=8;

        在这里,我们通过.birthday.year.birthday.month, 和 .birthday.day来访问和设置子结构体中的日期字段。

最后,我们使用printf函数来打印出变量

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);

这里,我们通过多级结构体成员的引用来访问和打印出各个字段的值。可以看到就是通过点来访问的 嘎嘎简单哈。

3.结构体数组

结构体数组是个数组,由 若干个相同类型的结构体变量构成的集合
1 、结构体数组的定义方法
struct 结构体类型名 数组名 [ 元素个数 ];

结构体数组是一种数据结构,它允许我们存储多个相同类型的结构体变量。这是非常有用的,因为它可以帮助我们组织和管理大量的相关数据。

让我们来看看一个例子,以便更好地理解结构体数组:

首先,我们有一个名为stu的结构体,它包含了三个字段:一个整数num、一个字符数组name和一个字符sex

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

接下来,我们定义了一个名为edu的结构体数组,它包含了三个元素:edu[0]edu[1]edu[2]

struct stu edu[3];

这个数组 edu 可以被视为三个 stu 类型的结构体变量的集合。我们可以通过数组的下标来访问每个元素,并对它们进行操作。

例如,我们可以将值 101 给 edu[0] 数组中的第一个结构体变量的 num 字段赋值:

edu[0].num = 101;

这里,.num 是指 edu[0] 结构体变量中的 num 字段。

同样,我们可以使用 strcpy 函数将字符串 “lucy” 给 edu[1] 数组中的第一个结构体变量的 name 字段赋值:

strcpy(edu[1].name, "lucy");

这里,.name 是指 edu[1] 结构体变量中的 name 字段。

4.结构体指针

结构体指针是一种在C语言中用于存储和操作结构体变量的特殊类型的指针。它允许我们通过指针来访问和修改结构体变量的字段,而无需直接使用变量的名称。

让我们来看看一个例子,以便更好地理解结构体指针:

首先,我们有一个名为stu的结构体,它包含了两个字段:一个整数num和一个字符数组name

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

接下来,我们定义了一个名为p的结构体指针变量,它可以存储 stu 类型的结构体变量的地址。

struct stu * p;

这个指针变量 p 可以被视为一个存储结构体变量地址的“指针”,它占用了相同数量的内存空间,用于保存这个地址。

现在,我们创建了一个名为boy的结构体变量,并使用 & 指针运算符将其地址赋值给指针变量 p

struct stu boy;
p = &boy;

这里,&boy 是 boy 结构体变量的地址,我们将其赋值给指针变量 p

接下来,我们可以通过以下方式访问和修改 boy 结构体变量的字段:

  1. 直接访问

    boy.num = 101; // 直接访问并修改 boy 的 num 字段
    
  2. 通过指针访问

    (*p).num = 101; // 使用指针来访问并修改 boy 的 num 字段
    
  3. 使用指针运算符

    p->num = 101; // 使用指针运算符来访问并修改 boy 的 num 字段
    

在这三种方法中,最后两种都是通过指针来访问和修改结构体变量的字段。前提是指针必须先指向一个结构体变量。如果指针没有正确地指向一个结构体变量,那么尝试访问或修改其字段可能会导致程序出错。

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

(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;
}

注意: 结构体变量的地址编号和结构体第一个成员的地址编号相同,但指针的类型不同

#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;
}

注意:结构体数组的地址就是结构体数组中第 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;
}

原本不想把这个代码放上来的 但是我觉得虽然废话多 但是如果光看话的话感觉总是有点离谱。

5、结构体内存分配

内存分配与对齐规则

分配单位
  • 定义:结构体中最大成员变量的长度。
    • 例如,如果结构体中最大成员是int(占4字节),则分配单位为4字节;如果最大成员是double(占8字节),则分配单位为8字节。
成员变量偏移
  • 定义:成员变量的起始地址必须是其自身长度的整数倍。
    • 例如,一个int类型变量(4字节)必须放在一个地址是4的倍数的位置上。
    • 一个short类型变量(2字节)必须放在一个地址是2的倍数的位置上。
结构体总大小
  • 定义:结构体总大小必须是分配单位的整数倍。
    • 如果最终计算出的结构体大小不是分配单位的整数倍,需要填充(padding)到分配单位的整数倍。

内存分配与对齐示例

假设我们有以下结构体:

struct Example {
    char a;   // 1字节,偏移0
    short b;  // 2字节,偏移2(1 + 1对齐到2)
    int c;    // 4字节,偏移4
    double d; // 8字节,偏移8
};
1. 确定分配单位
  • char:1字节
  • short:2字节
  • int:4字节
  • double:8字节

最大成员变量是double,占8字节,因此分配单位是8字节。

2. 计算每个成员变量的偏移量

按照结构体中成员变量的顺序,并根据它们自身长度的整数倍来计算偏移:

  1. char a
    • 起始地址:0(分配结构体的起始地址)
    • 长度:1字节
    • 偏移量:0(0是1的倍数)
  2. short b
    • 上一个成员a结束地址:1
    • 为满足2字节对齐,b的起始地址:2
    • 长度:2字节
    • 偏移量:2(2是2的倍数)
  3. int c
    • 上一个成员b结束地址:4
    • 为满足4字节对齐,c的起始地址:4
    • 长度:4字节
    • 偏移量:4(4是4的倍数)
  4. double d
    • 上一个成员c结束地址:8
    • 为满足8字节对齐,d的起始地址:8
    • 长度:8字节
    • 偏移量:8(8是8的倍数)
3. 计算结构体总大小
  • 最后一个成员d结束地址:16(8 + 8)
  • 结构体总大小必须是分配单位(8字节)的整数倍。

最终大小为16字节(已经是8的整数倍,无需额外填充)。

成员类型偏移量
achar0
bshort2
cint4
ddouble8

结构体总大小为16字节,满足对齐规则。

就是这样存的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值