C中的结构体

本文详细介绍了C语言中的结构体和数组的区别,包括结构体的定义、存储方式、初始化、嵌套结构体、结构体数组、结构体指针以及如何向函数传递结构体。结构体允许不同类型的数据成员组合成新的数据类型,而数组是相同类型数据的集合。在使用中,结构体可以看作一种自定义的数据类型,其操作与基本数据类型类似,但提供了更灵活的数据组织形式。

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

  • 结构体与数组的区别

数组:相同数据类型的数据构成的一种数据结构。

结构体:不同类型的数据成员组成的(用户自定义的)一种新的数据类型。

下面利用图片形象的解释两者存储差别,实现以下数据的存储。

数组方式存储

结构体方式存储

可以看出,使用结构体等于说创建了一种新的数据类型,其他操作并无两异,尤其是到后面的结构体数组中会体现的更加明显。总的,结构体是一个整体。


  • 结构体变量的定义

首先声明结构体模板,标准格式如下:

struct 结构体名
{
    数据类型 成员1名字;
    数据类型 成员2名字;
    ......
    数据类型 成员n名字;
};

在声明结构模板(只是声明了一种新的数据类型),接着需要定义结构体变量。

简单示例

struct student
{
    char i;
    char x;
    char y;
};

主要有一下三种方式:

  • 先声明结构体模板,在定义结构体变量。

struct student stu1;
  • 在声明结构体模板的同时定义结构体变量。

struct student       //此种方法可以省略student,但是复用困难,所以不常用
{
    char i;
    char x;
    char y;
}stu1;            //此时定义的结构体变量为全局变量


  • typedef定义结构体

相信你已经会使用typedef定义别的数据类型。

typedef int INTEGER;

typedef的实质就是给数据类型起别名。

下面我们介绍使用typedef定义结构体:(与上述大差不差)

  • 当你已经声明了结构体模板后,只需要简单的typedef语句就可完成

typedef struct student STUDENT;
  • 或者在声明模板时就可以使用typedef

typedef struct student
{
    char i;
    char x;
    char y;
}STUDENT;

上述两者是等价的,在使用它们定义时也是相同的。

STUDENT stu1,stu2;
//一下等价语句为
struct student stu1,stu2;


  • 结构体变量的初始化

初始化语句为:

STUDENT stu1={‘a’,‘b’,‘c’};//在前面的结构体中我们定义的是三个char,所以现在初始化三个字符就🆗
//一下等价语句为
struct student stu1={‘a’,‘b’,‘c’};


  • 嵌套结构体

简单来说就是一个结构体中包含了另外一个结构体的内容。(也可以是同一个结构体间的嵌套,但是实用价值不大)

下面简单举一个嵌套结构体的例子

typedef struct data
{
    int year;
    char month[10];
    int day;
}DATE;  //注意嵌套在内部的结构体需要先声明

typedef struct student
{
    long studentID;
    char studentName[10];
    char studentSex;
    DATE brithday;        //嵌套的结构体,定义的DATE型结构体变量为brithday
    int score[4];
}STUDENT;

嵌套结构体形象的存储结构

简单的介绍一下嵌套结构体的初始化方式:

STUDENT stu1={100310121,"王刚",'M',{1991,'May',19},{98,99,100,86}};


  • 结构体变量的引用

使用成员选择运算符 (圆点运算符)

结构体变量名.成员名

可以使用此语句对于成员进行赋值操作

stu1.studentID = 100310121;

当结构体为嵌套结构体时,采用级联的方式进行引用

stu1.brithday.year = 1991;


  • 结构体数组

在上面我们提到过结构体只是相当于定义了一种新的数据类型,其他操作与已有的数据类型别无两异

结构体数组的定义与初始化

STUDENT stu[30];
//初始化就是数据的重叠
STUDENT stu1={{100310121,"王刚",'M',{1991,'May',19},{98,99,100,86}},
              {100310121,"张三",'M',{1991,'March',18},{73,62,88,86}},
              {100310121,"李四",'W',{1991,'June',17},{99,76,66,90}}
             };
//上述只初始化了三个数据,其他的系统自动赋值为0


  • 结构体指针

  • 指向结构体变量的指针

STUDENT *pt;
pt = &stu1;
//上述语句与下列语句等价
STUDENT *pt = &stu1;

使用指针对与结构体成员的引用不可以直接使用成员运算符(因为指针只是一个存储地址的变量,无法直接使用指针对于成员的应用,需要使用该指针保存的地址寻找到该地址,再利用该地址对成员进行引用)

所以,对于指针,我们有指向运算符 ->

指向结构体的结构体指针变量名 -> 成员名
//以下示例
pt -> studentID = 100310121;

那么如何通过成员运算符与指针来引用结构体成员呢

我们可以通过*运算符来寻址,从而使用成员运算符来访问

(*pt).studentID = 100310121;
  • 指向结构体数组的指针

STUDENT *pt=stu;
//与下面一条语句等价
STUDENT *pt=&stu[0]
//也等价与以下语句
STUDENT *pt;
pt=stu;

是结构体变量还是结构体指针变量?

(更新补充于23.4.1)

在使用指针与变量本身时很容易混淆

当结构体嵌套时,这个问题更明显

下面根据一个数据结构中经典的迷宫问题来辨析一下

这是定义了一个BOX类型的结构体变量,含有i,j,di三个成员

然后又定义了一个StType类型结构体变量,嵌套了BOX类型的变量

我们看看在程序中或者函数中如何使用它们吧

上述语句中,st为定义的StType类型的结构体的指针类型变量(有点拗口,,ԾㅂԾ,,)

很容易犯得错误是忘记st是指针型变量,使用成员运算符 . 来引用内部成员,很显然应该使用指向运算符来引用内部成员。然而对于其内部嵌套的结构体类型数组data[k]是BOX类型结构体变量,直接使用成员运算符即可引用内部成员。

在函数中也一样,函数传过来的一般多是指针,所以下面的例子也一样

stu与&stu[0]与&stu的关系

在这里有一个隐含的概念,和数组也是一样的

即stu与&stu[0]是等价的

但是对于&stu是具有不一样的含义的

首先我们从数组开始解释

有以下简单代码:

    char a[4];
    printf("%p\n",a);
    printf("%p\n\n",a+1);    
    printf("%p\n",&a[0]);
    printf("%p\n\n",&a[0]+1); 
    printf("%p\n",&a);
    printf("%p\n\n",&a+1);    

预计执行结果为:

2,4两行结果相同,3,5两行在前一种结果的基础上+1(char类型占一个字节)

那么6,7两行的结果呢?

第6行的结果和2,4的结果相同

第七行结果为此结果基础上+4

很显然,对于&a来说,是将整个数组当作一个整体,所以对于&a进行+1的操作,即是像后挪动一整个数组。

下面以结构体数组来进行演示,看看的预测的结果是否正确吧?

 typedef struct Teacher
 {
     char i;
     char x;
     char y;
 }TEACHER;

    printf("%p\n",teac);          //结构体首地址(可以进行+1操作) 与&teac[0]等价 
    printf("%p\n",teac+1);        // 往后挪一个结构体 
    printf("%p\n",&teac[0]);      //结构体首地址 
    printf("%p\n",&teac[0]+1);    //结构体地址加一
    printf("%p\n",&teac);         //也是首地址,但是是整个结构体数组首地址 
    printf("%p\n",&teac+1);       //往后诺挪一个结构体数组 
    printf("%p\n",teac[0]);       //数据 (不可以进行+1操作) 

结构体不一样的只是结构体内部还有成员,但是你可以将它看作一个新的数据类型,不需要管内部的数据成员。只是在引用的时候将看成内部的细分。


  • 向函数传递结构体

  • 将结构体单个成员作为函数参数,向函数传递单个成员

此处和普通类型的变量作为函数实参无区别,传值调用。较少使用。

  • 结构体变量作为函数参数,向函数传递结构体的完整结构

由于我们前面提到的,结构体可以看作一种新的数据类型,所以此方式还是传值调用。与第一中传递方法并无两异。

  • 用结构体指针或者结构体数组作为函数参数,向函数传递结构体的地址

按地址调用。

  • 结构体也可以作为函数的返回值。

一个贯彻的原理是,结构体相当于定义的一个新的数据类型。所以上述与函数传参中的知识点,语法是相同的。但不要忘记在第三种中,无论是通过指针还是结构体数组,在函数体内部访问结构体成员,由于函数得到的都是地址,所以都需要使用指向运算符。(如果结构体数组是全局变量,就不需要传参,并且可以使用成员运算符)

结构体的知识介绍就到此,希望你在接下来的编程中找到属于自己的船,扬帆远航!

——23.3.17

### C语言结构体使用教程 #### 定义结构体 在C语言中,`struct` 关键字用于定义结构体结构体允许程序员创建自定义数据类型,可以组合不同类型的数据项在一起。一般语法格式如下: ```c struct 结构体名 { 数据类型 成员名1; 数据类型 成员名2; }; ``` 例如,定义一个表示书籍信息的结构体[^1]。 ```c struct Books { char title[50]; char author[50]; int pages; }; ``` #### 初始化结构体变量 声明并初始化结构体有两种常见方式:一种是在声明的同时赋初值;另一种是先声明再分别给各成员赋值。 直接初始化的例子如下所示。 ```c struct Books book1 = {"C Programming Language", "Brian W. Kernighan", 272}; ``` 或者分步进行初始化: ```c struct Books book2; strcpy(book2.title, "The C Puzzle Book"); strcpy(book2.author, "Alan R. Feuer"); book2.pages = 184; ``` #### 访问结构体成员 要访问结构体中的某个特定字段,可采用点运算符`.`连接对象名称与其内部元素名称。对于通过指针间接引用的情况,则需利用箭头操作符 `->` 来获取目标位置上的属性值[^2]。 ```c printf("Book Title : %s\n", book1.title); // 如果有一个指向 struct Books 的指针 pBooks pBooks->pages = 300; ``` #### 使用结构体指针 当处理大量相同类型的记录时,通常会考虑建立结构体数组或链表等复杂数据结构,在这种情况下经常需要用到指向这些实体的指针。定义形式为 `struct 结构体名 *结构体指针名;` 。下面展示了一个简单的例子说明如何声明以及应用这样的指针变量。 ```c struct Books *struct_pointer; /* 假设已经分配内存 */ struct_pointer = &book1; printf("Using pointer to access structure members:\nTitle: %s\nAuthor:%s\nPages:%d\n", (*struct_pointer).title, (*struct_pointer).author, (*struct_pointer).pages); // 或者更简洁的方式 printf("Or using -> operator:\nTitle: %s\nAuthor:%s\nPages:%d\n", struct_pointer->title, struct_pointer->author, struct_pointer->pages); ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值