C中的结构体

  • 结构体与数组的区别

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

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

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

数组方式存储

结构体方式存储

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


  • 结构体变量的定义

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

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

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值