结构体总结

本文详细介绍了C语言中结构体的作用,包括数据组织、封装和传递参数。涵盖了结构体的定义、初始化、成员访问规则、嵌套结构体、结构体数组以及结构体指针,同时讨论了内存布局和字节对齐的相关知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     在C语言中,数据类型包括基本数据类型/内置类型(如整型、浮点型、字符型等)、数组类型、指针类型、结构体类型、联合体类型。今天要总结的是结构体类型。

一、结构体的作用

       C语言允许用户自己创建由不同类型数据组成的组合型的数据结构,它称为结构体

       结构体在程序设计中扮演着组织数据(将不同类型的数据组合成一个单独的数据类型)、提高代码可读性和可维护性、实现数据封装(通过限制对数据的访问,可以提高代码的安全性和健壮性,防止数据被意外地修改或篡改)和传递参数(从而方便地将多个相关联的数据一起传递给函数,减少参数数量,简化函数调用)等重要角色。

二、结构体的设计

1. 定义形式:

a.声明类型的同时定义变量

struct 结构体名称
{
数据类型 成员1; //可以是基本数据类型、指针、数组或其他结构
数据类型 成员2;
}变量名表列;            //分号后需要加“;”

示例:

//定义结构体
struct Student
{
int student_id[4];
char name[8];
int age;
float grade[5];
}student1,student2;

b.不指定类型名而直接定义结构体类型变量

struct 
{
数据类型 成员表列
}变量名表列;

指定了一个无名的结构体,没有名字(不出现结构体名称)。

2.结构体变量初始化

// 定义结构体
struct Student {
    int student_id[4];
    char name[8];
    int age;
    float grade[5];
};

int main() {
    // 声明结构体变量并初始化
   Student student1 = {1, "Alice", 23, 85.5};
   Student student2;//.cpp文件
                    //.c文件中定义方式为struct Student student2;
struct Student b={.name="Zhang"};   //在成员名前有成员运算符“.”

".name"隐含代表结构体变量b中的成员b.name。其他未被指定初始化的数值型成员被系统初始化为0,字符型被系统初始化为'\0',指针型成员被系统初始化为NULL。

3.结构体成员访问

(1)结构体成员变量使用 . 访问

获取和赋值结构体变量成员的格式为:结构体变量 . 成员名

示例:

#include <stdio.h>

// 定义一个结构体
struct Person {
    char name[50];
    int age;
    float height;
};

int main() {
    // 声明一个结构体变量
    struct Person person1;

    // 对结构体成员赋值方式一
    strcpy(person1.name, "John");
    person1.age = 30;
    person1.height = 6.0;
    
    //对结构体成员赋值方式二
    //person1={"John",30,6.0};

    //修改结构体成员的值
    //person1={"Mike",20,7.0};//err,不能直接赋值进行修改
    strcpy(person1.name,"Mike");
    //或者用memcpy(person1.name,"Mike",5);范围更广泛,一般strcpy用于字符数组
    

    // 访问结构体成员并打印输出
    printf("Name: %s\n", person1.name);
    printf("Age: %d\n", person1.age);
    printf("Height: %.2f\n", person1.height);

    return 0;
}

 (2)只能对最低级的成员进行赋值或存取以及运算。若在结构体struct Student类型的成员中包含另一个结构体struct date类型的成员birthday,则引用方式为:

student1.num                //结构体变量student1中的成员num
student1.birthday.month     //结构体变量student1中的成员birthday中的成员month
student.birthdy             //err

不能用student1.birthday来访问student1变量中的成员birthday,因为birthdy本身是一个结构体成员。

(3)对结构体变量的成员可以像普通变量一样进行各种运算。如:

student2.score=student1.score;   (赋值运算)
sum=student1.score+student2.score;(加法运算)
student.age++;                    (自加运算)

 (4)同类的结构体变量可以相互赋值。如:

student1=student2;

(5)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。如:

scanf("%d",&student1.num);   //输入student1.name的值
printf("%o",&student1);      //输出结构体变量student1的起始地址

但不能用scanf语句整体读入结构体变量。如:

scanf("%d,%s,%c,%d,%f,%s\n",&student1);   //err

4.结构体变量重命名

 可以用 typedef 为结构体定义一个新的名称

//使用typedef关键字为struct student创建两个别名:Student和stu

typedef struct student
{
char name[50];
int age;
}Student,stu; 
typedef struct Student student; //定义了一个新的类型别名 student指向了结构体 struct Student
struct Student s; //声明了一个名为 s 的结构体变量,其类型是 struct Student
Student s; //student 来声明一个结构体变量 s,其类型实际上是 struct Student,相当于第二行的代码

5.结构体嵌套

结构体嵌套是一种将一个结构体作为另一个结构体的成员的方法

示例:

#include <stdio.h>

// 定义一个地址结构体
struct Address {
    char city[50];
    char country[50];
};

// 定义一个用户结构体,嵌套地址结构体
struct User {
    char name[50];
    int age;
    struct Address address; // 嵌套的地址结构体
};

int main() {
    // 创建一个用户
    struct User user = {
        .name = "Alice",
        .age = 30,
        .address = {.city = "Beijing",.country = "China"}
    };

    // 访问嵌套的字段
    printf("Name: %s\n", user.name);
    printf("Age: %d\n", user.age);
    printf("City: %s\n", user.address.city);
    printf("Country: %s\n", user.address.country);

    return 0;
}

三、结构体数组

一个结构体变量中可以存放一组有关联的数据(如一个学生的学号、姓名、成绩等)。如果有10个学生的数据需要参与运算,应用数组,这就是结构体数组。

1.定义方式

①一般定义方式:

struct 结构体名称
{
成员列表
}数组名[数组长度];

②先声明一个结构体类型,然后再用此类型定义结构体数组:

结构体类型 数组名[数组长度];
如:
struct Person student[3];    //student是结构体数组名

  示例:

struct Person {
    char name[50];
    int age;
    float height;
};

2.结构体数组初始化

对结构体数组初始化的形式是在定义数组的后面加上 ={初值列表};

示例1:

struct Person                             //声明结构体类型struct Person
{
char name[20];                           //候选人姓名
int count;                               //候选人得票数
}leader[3]={"Luo",0,"Zhang",1,"Liu",2};  //定义结构体数组类型并初始化

示例2:

// 定义结构体
struct Person {
    char name[50];
    int age;
    float height;
};

int main() {
    // 声明结构体数组并初始化
    struct Person people[3] = {
        {"Alice", 25, 1.75},
        {"Bob", 30, 1.80},
        {"Charlie", 22, 1.70}
    };

四、结构体指针

结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针。如果把一个结构体变量的起始地址存放在一个指针变量中,那么这个指针变量就指向该结构体变量。

1.指向结构体变量的指针

指向结构体对象的指针变量既可指向结构体变量,也可以指向结构体数组中的元素。指针变量的基类型必须与结构体变量的类型相同。

如:

struct Student *p;  //p可以指向struct Student类型的变量或数组元素

示例:

// 定义一个结构体
struct Person {
    char name[50];
    int age;
    float height;
};

int main() {
    // 声明一个结构体变量
    struct Person person1;
    
    // 使用结构体变量的成员运算符(.)给结构体成员赋值
    strcpy(person1.name, "John");
    person1.age = 30;
    person1.height = 5.9;
    
    // 声明一个结构体指针,并将其指向 person1
    struct Person *ptrPerson = &person1;
return 0;
}
struct Student
{
long num;
char name[20];
char sex;
float score;
};

int main()
{
struct Student stu_1;     //定义struct Student类型的变量stu_1
struct Student *p;        //定义指向struct Student类型数据的指针变量p
p=&stu_1;                 //p指向stu_1
stu_1.num=2004;           //对结构体变量进行赋值
strcpy(stu_1.name,"Luo"); //用字符串复制函数给stu_1.name赋值
stu_1.sex="M";
stu_1.score=89;

printf("No:%ld\nname:%s\nsex:%c\nscore:%5.lf\n",stu_1.num,stu_1.name,stu_1.sex,stu_1.score);
printf("\nNo:%ld\nname:%s\nsex:%c\nscore:%5.lf\n",(*p).num,(*p).name,(*p).sex,(*p).score);
return 0;
}

如果p指向一个结构体变量stu,以下三种用法等价:

①stu.成员名(如stu.name)

②(*p).成员名(如(*p).name)  //*p两边的括号不可省,成员运算符”."优先于“*”运算符

③p->成员名(如p->name)  //"->"代表一个箭头,p->name表示p指向的结构体变量中的name元素

2.指向结构体数组的指针

# include <stdio. h>
struct Student	//声明结构体类型struct Student	
{int num;
char name[20]; 
char sex; 
int age;
};
struct Student stu[3]={{10101,"Li Lin",'M',18},{10102,"Zhang Fang",'M',19},
                       {10104,"Wang Min",'F',20}};//定义结构体数组并初始化
int main( )
{
struct Student *p;	//定义指向struct Student结构体变量的指针变量	
printf(" No. Name	sex age\n");	
for (p=stu;p<stu+3;p++)
printf("%5d %-20s %2c %4d\n",p->num,p->name,p->sex,p->age);
return 0;
}

(1)若p 的初值为stu,即指向stu的序号为0的元素,p加1后,p就指向下一个元素。如:

(p++)->num   先求得p->num的值(即10101),然后再使p自加1,指向stu[1]
 (++p)->num   先使p自加1,然后得到p指向的元素中num成员值(即10102)  

 (2)p的值是stu数组中的一个元素(如stu[0]或stu[1])的起始地址,不也能用来指向stu数组中的某一成员。如:

p=stu[1].name;       //err   stu[1].name是stu[1]元素中的成员name的首字符的地址

若要将某成员的地址赋值给p,可以用强制类型转换,先将成员的地址转换成p的类型。如:

p=(struct Student*)stu[0].name;

此时,p 的值是stu[0]元素的name成员的起始地址。可以用“printf(”%s",p);”输出stu[0]中成员name的值,但是,p保持原来的类型。如果执行“printf("%s",p+1);”,则会输出stu[1]中name的值,执行p++时,p的值的增量时结构体struct Student的长度 

五、结构体大小

1.字节对齐方式

a. cpu并非逐字节读写内存,而是以2,4,8的倍数读取,因此要考虑字节对齐

b. 预处理指令 #pragma pack(n)可以改变默认对齐数,n的取值是1,2,4,8,16

c. vs中默认值=8,gcc中默认值=4

2.结构体内存大小

(1)结构体变量的首地址,必须是MIN{“结构体最大基本数据类型成员所占字节数”,指定对齐方式}大小的整数倍

(2)结构体每个成员相对于结构体首地址的偏移量,都是MIN{基本数据类型成员,指定对齐方式}大小的整数倍

(3)结构体的总大小,为MIN{结构体“最大基本数据类型成员所占字节数”(将嵌套结构体里的基本类型也算上,得出最大基本数据类型成员所占字节数),指定对齐方式}大小的整数倍

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值