C语言中结构体(struct)的详细分解与使用(上)

简介: C语言中结构体(struct)的详细分解与使用(上)

第一:结构体的定义


结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。

77e963b63d67d55de99848d6a2e417d1.png

结构体和其他类型基础数据类型一样,例如 int 类型,char类型;只不过结构体可以做成你想要的数据类型,以方便日后的使用。

在实际项目中,结构体是大量存在的。研发人员常使用结构体来封装一些属性来组成新的类型。由于C语言无法操作数据库,所以在项目中通过对结构体内部变量的操作将大量的数据存储在内存中,以完成对数据的存储和操作。

在实际问题中有时候我们需要几种数据类型一起来修饰某个变量。

例如一个学生的信息就需要学号(字符串),姓名(字符串),年龄(整型)等等。

这些数据类型都不同但是他们又是表示一个整体,要存在联系,那么我们就需要一个新的数据类型——结构体,它就将不同类型的数据存放在一起,作为一个整体进行处理。

结构体在函数中的作用不是简便,其最主要的作用就是封装。封装的好处就是可以再次利用。让使用者不必关心这个是什么,只要根据定义使用就可以了。

结构体的大小不是结构体元素单纯相加就行的,因为我们现在主流的计算机使用的都是 32Bit 字长的 CPU,对这类型的 CPU 取 4 个字节的数要比取一个字节要高效,也更方便。所以在结构体中每个成员的首地址都是4的整数倍的话,取数据元素时就会相对更高效,这就是内存对齐的由来。

每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令 #pragma pack(n),n=1,2,4,8,16 来改变这一系数,其中的 n 就是你要指定的“对齐系数”。


第二:规则


1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在 offset 为 0 的地方,以后每个数据成员的对齐按照 #pragma pack 指定的数值和这个数据成员自身长度中,比较小的那个进行。

2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

3、结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

在C语言中,可以定义结构体类型,将多个相关的变量包装成为一个整体使用。在结构体中的变量,可以是相同、部分相同,或完全不同的数据类型。结构体不能包含函数。

在面向对象的程序设计中,对象具有状态(属性)和行为,状态保存在成员变量中,行为通过成员方法(函数)来实现。C语言中的结构体只能描述一个对象的状态,不能描述一个对象的行为。在C++中,考虑到 C 语言到 C++ 语言过渡的连续性,对结构体进行了扩展,C++的结构体可以包含函数,这样,C++的结构体也具有类的功能,与 class 不同的是,结构体包含的函数默认为 public,而不是 private。


第三:结构体声明


//声明一个结构体 
struct book 
{
  char title[MAXTITL];//一个字符串表示的titile 题目 ;
  char author[MAXAUTL];//一个字符串表示的author作者 ;
  float value;//一个浮点型表示的value价格;
}; //注意分号不能少,这也相当于一条语句;


这个声明描述了一个由两个字符数组和一个float变量组成的结构体。

但是注意,它并没有创建一个实际的数据对象,而是描述了一个组成这类对象的元素。

因此,我们有时候也将结构体声明叫做模板,因为它勾勒出数据该如何存储,并没有实例化数据对象。

下面介绍一下上面的结构体声明;

1、首先使用关键字struct,它表示接下来是一个结构体。

2、后面是一个可选的标志(book),它是用来引用该结构体的快速标记。    

因此我们以后就可以这样创建数据对象

struct book library;//把library设为一个可以使用book结构体的结构体变量,则library这个变量就包含了其book结构体中的所有元素

3、接下来就是一个花括号,括起了结构体成员列表,及每个成员变量,使用的都是其自己的声明方式来描述,用分号来结束描述;

例如:char title[MAXTITL]; 字符数组就是这样声明的,用分号结束;

注意:其中每个成员可以使用任何一种C数据结构甚至是其他的结构体,也是可以的;

4、在结束花括号后的分号表示结构体设计定义的结束。

关于其struct声明的位置,也就是这段代码要放到哪里。同样这也是具有作用域的。

这种声明如果放在任何函数的外面,那么则标记可以在在本文件中,该声明后面的所有函数都可以使用。

如果这种声明在某个函数的内部,则它的标记只能在内部使用,并且在其声明之后;

af28dc0d0836a8bf457ab671f8dc2bf6.jpg

9341e395edef4f1f1f9696b6581a0146.jpg

关于我们不断说的,标记名是可选的,那么我们什么时候可以省略,什么时候一定不能省略呢?

如果是上面那种声明定义的方法,并且想在一个地方定义结构体设计,而在其他地方定义实际的结构体变量,那么就必须使用标记;

可以省略,设计的同时就创建该结构体变量,但是这种设计是一次性的。

一般格式就是:

struct 结构体名(也就是可选标记名){    成员变量;};//使用分号表示定义结束。


第四:C 语言结构体定义的三种方式


1、最标准的方式:

#include <stdio.h>
struct student //结构体类型的说明与定义分开。声明
{
  int age;  /*年龄*/
  float score; /*分数*/
  char sex;   /*性别*/
};
int main ()
{
  struct student a={ 20,79,'f'}; //定义
  printf("年龄:%d 分数:%.2f 性别:%c\n", a.age, a.score, a.sex );
  return 0;
}


2、不环保的方式

#include <stdio.h>
struct student /*声明时直接定义*/
{
  int age;  /*年龄*/
  float score;  /*分数*/
  char sex;   /*性别*/
 /*这种方式不环保,只能用一次*/
} a={21,80,'n'};
int main ()
{
  printf("年龄:%d 分数:%.2f 性别:%c\n", a.age, a.score, a.sex );
}


3、最奈何人的方式

#include <stdio.h>
struct   //直接定义结构体变量,没有结构体类型名。这种方式最烂
{
  int age;
  float score;
  char sex;
} t={21,79,'f'};
int main ()
{
  printf("年龄:%d 分数:%f 性别:%c\n", t.age, t.score, t.sex);
  return 0;
}


定义结构体变量

之前我们结构体类型的定义(结构体的声明)只是告诉编译器该如何表示数据,但是它没有让计算机为其分配空间。

我们要使用结构体,那么就需要创建变量,也就是结构体变量;

创建一个结构体变量;

struct book library;


看到这条指令,编译器才会创建一个结构体变量library,此时编译器才会按照book模板为该变量分配内存空间,并且这里存储空间都是以这个变量结合在一起的。

这也是后面访问结构体变量成员的时候,我们就要用到结构体变量名来访问。


分析:

struct book的作用:

在结构体声明中,struct book所起到的作用就像 int 等基础数据类型名作用一样。

struct book s1,s2,*ss;


定义两个 struct book 结构体类型的结构体变量,还定义了一个指向该结构体的指针,其 ss 指针可以指向 s1,s2,或者任何其他的book结构体变量。

struct book library;


等效于:

struct book{
char … 
…. 
….. 
}library;


这两种是等效的,只是第一种可以减少代码的编写量;

现在还是回到刚才提及的那个问题,可选标志符什么时候可以省略;

其一:

struct
{
  char title[MAXTITL]; 
  char author[MAXAUTL];
  float value;
}library;


注意,这里不再是定义声明结构体类型,而是直接创建结构体变量了,这个编译器会分配内存的;

这样的确可以省略标识符也就是结构体名,但是只能使用一次;因为这时,声明结构体的过程和定义结构体变量的过程和在了一起,并且各成员变量没有初始化。

如果你想多次使用一个结构体模块,这样子是行不通的。

其二,

用 typedef 定义新类型名来代替已有类型名,即给已有类型重新命名;

一般格式为;typedef 已有类型 新类型名;

typedef int Elem; 
typedef struct{
int date;
    .....
    .....
}STUDENT;
STUDENT stu1,stu2;


7bd3eeecad6c6f7a76e7fa69c23692e8.jpg

总结一下关于结构体变量的定义:

1、先定义结构体类型后再定义结构体变量;

格式为:struct 结构体名 变量名列表;

//注意这种之前要先定义结构体类型后再定义变量;
struct book s1,s2,*ss;


2、在定义结构体类型的同时定义结构体变量;

格式为:

struct 结构体名
{
 成员列表;
}变量名列表;//这里结构体名是可以省的,但尽量别省;
struct book
{
  char title[MAXTITL];//一个字符串表示的titile 题目 ;
  char author[MAXAUTL];//一个字符串表示的author作者 ;
  float value;//一个浮点型表示的value价格;
}s1,s2


直接定义结构体类型变量,就是第二种中省略结构体名的情况;

这种方式不能指明结构体类型名而是直接定义结构体变量,并且在只定义一次结构体变量时适用,无结构体名的结构体类型是无法重复使用的。

也就是说,后面程序不能再定义此类型变量了,除非再写一次重复的 struct。


C语言中结构体(struct)的详细分解与使用(中)https://developer.aliyun.com/article/1389335

目录
相关文章
|
18天前
|
C语言
C语言结构体内存对齐
C语言结构体内存对齐
|
21天前
|
存储 编译器 Linux
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
|
18天前
|
存储 C语言
C语言自定义类型结构体详解
在C语言中,结构体是复合数据类型,能组合不同类型的数据显示。定义结构体用`struct`关键字,如`struct Student {char name[20]; int age; float score;};`。声明结构体变量如`struct Student stu1;`,访问成员用`.`操作符,如`stu1.age = 20;`。初始化可直接赋值`struct Student stu1 = {&quot;李四&quot;, 22, 85.5};`。结构体数组如`struct Student stuArray[3]`,结构体指针如`struct Student *pStu = &stu1;`。
6 0
|
21天前
|
存储 搜索推荐 编译器
【C语言】一篇文章深入解析联合体和枚举且和结构体的区别
【C语言】一篇文章深入解析联合体和枚举且和结构体的区别
|
21天前
|
存储 网络协议 编译器
【C语言】自定义类型:结构体深入解析(三)结构体实现位段最终篇
【C语言】自定义类型:结构体深入解析(三)结构体实现位段最终篇
|
29天前
|
存储 编译器 C语言
【C语言】结构体的大小是如何计算的?(结构体对齐)
【C语言】结构体的大小是如何计算的?(结构体对齐)
27 0
|
8月前
|
存储 C语言
【C语言】 条件操作符 -- 逗号表达式 -- []下标访问操作符,()函数调用操作符 -- 常见关键字 -- 指针 -- 结构体
【C语言】 条件操作符 -- 逗号表达式 -- []下标访问操作符,()函数调用操作符 -- 常见关键字 -- 指针 -- 结构体
|
7月前
|
C语言
【C语言】——define和指针与结构体初识
【C语言】——define和指针与结构体初识
|
10月前
|
存储 C语言
C语言初识-关键字-操作符-指针-结构体
C语言初识-关键字-操作符-指针-结构体
45 0