【自定义类型详解】第一篇——结构体详解

简介: 【自定义类型详解】第一篇——结构体详解

从这篇文章开始,我们来学习C语言中的自定义类型(构造类型),今天来看第一种自定义类型——结构体,一起来学习吧!!!


1.认识结构体

前面我们已经学习过了很多的数据类型,整型、浮点型、指针类型等等。


1.1为什么要学习结构体类型

已经有这么多数据类型了,那我们为什么还要学习结构体类型呢?


因为在开发的过程中,我们有时候难免要去描述一些复杂的对象,而想要描述这些对象,我们再使用之前学过的int,double等这些类型可能就不适用了。

比如我们想要描述一本书,对于书这个类型来说,它具有的特征不止一个,我们要想去描述一本书的话,可能要给出书的书名,书的作者,书的定价等等这些信息。

这时如果我们只用一个int,double,char类型的数据好像无法描述。

这时候,我们就需要使用结构体来描述了。

因此,结构体作为一种自定义类型,使得我们有能力去描述复杂类型。


1.2什么是结构体

那既然结构体这么牛,结构体到底是什么呢?


结构体(结构)是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。


2.结构体的声明

我们已经知道结构体是什么了,那如果我们想用结构体来描述一个学生该怎么做呢?

首先我们要进行结构体的声明。

如果我们想要描述一个学生,那我们就先来声明一个学生类型,怎么声明呢?


结构体声明的语法是:

struct tag
{
  member - list;
} variable - list;

每一部分都是什么意思呢?解释一下:

932ae64371ea41bbb7d6473dae46d1b4.png

光看这个图,可能还不是特别明白,给大家举个例子就明白了。

我们就来声明一个学生类型,指定该学生类型拥有的成员变量有姓名,年龄,性别,学号。

struct Stu
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  char id[20];//学号
}; //分号不能丢

我们看到上面没有变量列表variable - list,这个是可选的。

3.结构成员的类型

结构体的成员变量可以是什么类型呢?

结构的成员可以是标量、数组、指针,甚至是其他结构体。

struct SS
{
  int a;
  char b;
  float c;
};
struct Stu
{
  char ch;
  int* p;
  double arr[10];
  struct SS s1;
}; 

4.结构体变量的定义

有了结构体类型,那如何定义变量,其实很简单。

定义结构体的方式有两种:

4.1 在声明结构体类型的同时定义结构体变量

即在声明类型的}后面直接创建结构体变量,但要注意这里创建的结构体变量是全局变量。

举个例子:

struct Point
{
  int x;
  int y;
}p1; //声明类型的同时定义变量p1

4.2用声明过的结构体类型定义结构体变量

struct Point
{
  int x;
  int y;
};
struct Point p2;
int main()
{
  struct Point p3;
  return 0;
}

5.结构体变量的初始化

初始化,即在定义变量的同时赋初值。

和数组一样,初始化结构体变量也用的是{}

举个例子:

struct Point
{
  int x;
  int y;
}p1 = { 2,3 };
struct Stu 
{
  char name[15];//名字
  int age; //年龄
};
int main()
{
  struct Stu s = { "zhangsan", 20 };//初始化
  return 0;
}

5.1结构体嵌套的初始化

#include <stdio.h>
struct Point
{
  int x;
  int y;
}p1 = { 2,3 };
struct Node
{
  int data;
  struct Point p;
}n1 = { 10, {4,5},}; //结构体嵌套初始化
int main()
{
  struct Node n2 = { 20, {5, 6}};//结构体嵌套初始化
  printf("%d %d %d", n1.data, n1.p.x, n1.p.y);
  return 0;
}

再嵌套一个大括号来初始化被嵌套的那个结构体。

打印一下看看:740779e6fe224bf98ec0fe20815d5df3.png

5.2指定成员变量初始化

在初始化结构体的时候,我们可以不按成员变量的顺序去初始化,可以指定某个成员初始化,按我们想要的顺序初始化。

举个例子:

struct Stu 
{
  char name[15];//名字
  int age; //年龄
};
int main()
{
  struct Stu s = {.age=33,.name="zhangsan"};//指定成员初始化
  printf("%d %s", s.age, s.name);
  return 0;
}

打印出来看看:

265178b4f1c74abc92163b7de99bbb6d.png

这样也是可以的。


6.特殊的声明(匿名结构体类型)

除了上面介绍的结构体声明方式之外,还有一种特殊的结构体声明。


即在声明结构体的时候,可以不完全的声明。


那这个不完全声明又是什么意思呢?


就是在声明一个结构体的时候的时候省略掉结构体标签(tag),或者说该结构体没有类型名。

也称为匿名结构体类型。


举个例子:

struct
{
  int a;
  char b;
  float c;
}x;

那么匿名结构体类型有什么特点呢?

因为匿名结构体类型没有类型名,所以匿名结构体类型只能在定义的时候创建结构体变量,后面再想利用这个结构体类型创建变量就做不到了。

struct
{
  int a;
  char b;
  float c;
}x;//创建匿名结构体变量x

这样是可以的。

struct
{
  int a;
  char b;
  float c;
};
int main()
{
  struct s;
  return 0;
}

935b299fec9f412a91c1ea413186f7a5.png

这样是不行的。

然后,我们再来分析一段代码:

struct
{
  int a;
  char b;
  float c;
}x;
struct
{
  int a;
  char b;
  float c;
}*p;
int main()
{
  p = &x;
  return 0;
}

大家思考一下,这样写,可以吗?

这样写是不行的,编译器会报警告的。

6ee0e01b6e0d4afca114839c222d9a59.png

虽然上面两个匿名结构体类型的成员变量完全一样,但是编译器会把上面的两个声明当成完全不同的两个类型。

所以是非法的。


7.结构体成员的访问

对于结构体成员的访问,不同的情况下可以有不同的访问方式,一般可以分为两种:


7.1结构体变量访问成员

通过结构体变量访问成员是通过点操作符(.)访问的。点操作符接受两个操作数。

语法:结构体变量.成员变量


举个例子:

#include <stdio.h>
struct Stu
{
  char name[15];
  int age; 
};
int main()
{
  struct Stu s = { "zhangsan", 20 };
  printf("%s %d", s.name, s.age);
  return 0;
}

f00b2914c62c4817b111e2c956cf4c26.png

看看结果:

f4af8335b6f34b27a4eac4f689aebdb0.png

7.2结构体指针访问指向变量的成员

有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针。

那该如何访问成员?


两种方式:


我们可以对该结构体指针解引用,这样就找到了对应的结构体变量,然后我们就可以使用(.)操作符来访问成员变量了。

那我们可不可以直接通过结构体指针访问对应结构体的成员变量呢?

当然可以。

这时候我们可以使用->操作符来实现。

语法:结构体指针->成员变量

一起来看一个例子:

struct Stu
{
  char name[15];
  int age;
};
void print(struct Stu* ps) {
  printf("name = %s   age = %d\n", (*ps).name, (*ps).age);
  //使用结构体指针访问指向对象的成员
  printf("name = %s   age = %d\n", ps->name, ps->age);
}
int main()
{
  struct Stu s = { "zhangsan", 20 };
  print(&s);//结构体地址传参
  return 0;
}

11178f42b876466a959140645ba4f227.png

看看结果:

ebdb2ed17e7046b49b98158549acf546.png

两种方式都可以成功访问。

8.结构体的自引用

首先,我们来思考一个问题:

在结构体中包含一个类型为该结构体本身的成员是否可以呢?

像这样:

struct Node
{
 int data;
 struct Node next;
};

这样是否可行,如果可行,那sizeof(struct Node)是多少?

如果这样写,我们去计算struct Node的大小时,需要计算成员里面一个同类型的结构体struct Node next的大小,而在计算它的大小时,发现里面还包含一个自己,这样的话就会无限套娃下去,是不是没法计算啊。


那应该怎么写,我们可以考虑这样做:

struct Node
{
 int data;
 struct Node* next;
};

换成一个同类型的结构体指针,这样它就指向了一个类型为该结构体本身的结构体变量作为成员。

通过这样一个问题,我们引出一个新的概念:

结构体的自引用:在结构体内部,包含指向自身类型结构体的指针。

数据结构中链表的结点其实就是这样搞的。


目录
相关文章
|
7月前
|
存储 开发框架 编译器
C语言进阶—自定义类型:结构体,枚举,联合
C语言进阶—自定义类型:结构体,枚举,联合
|
7月前
|
编译器 Linux C语言
C语言:结构体(自定义类型)知识点(包括结构体内存对齐的热门知识点)
C语言:结构体(自定义类型)知识点(包括结构体内存对齐的热门知识点)
|
7月前
|
编译器 Linux C语言
自定义类型:结构体进阶学习分享
自定义类型:结构体进阶学习分享
46 0
|
7月前
|
存储 开发框架 .NET
【C语言进阶】自定义类型详解(结构体、枚举、联合)
【C语言进阶】自定义类型详解(结构体、枚举、联合)
|
存储 编译器 C语言
C语言进阶-自定义类型:结构体、枚举、联合(2)
C语言进阶-自定义类型:结构体、枚举、联合
67 0
|
编译器 Linux C语言
C语言进阶-自定义类型:结构体、枚举、联合(上)
C语言进阶-自定义类型:结构体、枚举、联合(上)
71 0
|
C语言
C语言进阶-自定义类型:结构体、枚举、联合(下)
C语言进阶-自定义类型:结构体、枚举、联合(下)
90 0
|
存储 编译器 Linux
C语言进阶之自定义类型(结构体,枚举,联合)
C 语言允许定义可存储相同类型数据项的变量,结构体是 C 编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。
|
存储 C++
自定义类型及相关知识点的讲解
自定义类型及相关知识点的讲解
97 0
【进阶C语言】自定义类型:结构体,枚举,联合(二)
【进阶C语言】自定义类型:结构体,枚举,联合(二)

热门文章

最新文章