【C语言基础】:自定义类型(一)--> 结构体-1

简介: 【C语言基础】:自定义类型(一)--> 结构体

一、内置类型与自定义类型

在C语言中,有内置类型(也称为基本数据类型)和自定义类型(结构体)两种类型。


1.1 内置类型(基本数据类型)

  1. 整型(Integer types):用于表示整数值,包括:
  • int:通常表示整数,取决于编译器和系统架构,一般为4字节。
  • short int:短整数,通常为2字节。
  • long int:长整数,通常为4字节或8字节。
  • long long int:长长整数,通常为8字节。
  1. 字符型(Character type):
  • char:用于表示单个字符或小整数值,通常为1字节。
  1. 浮点型(Floating-point types):用于表示实数,包括:
  • float:单精度浮点数,通常为4字节。
  • double:双精度浮点数,通常为8字节。
  • long double:扩展精度浮点数,大小不定,通常大于8字节。
  1. 空类型(Void type):
  • void:表示无类型,常用于函数返回类型或指针类型。

这些内置类型是C语言提供的基本数据类型,用于表示基本数据,如整数、浮点数、字符等。


1.2 自定义类型

在C语言中,除了内置的基本数据类型外,还可以通过结构体(Structures)和枚举类型(Enums)来定义自定义类型。


1.结构体(Structures)

结构体是一种用户自定义的数据类型,用于组合不同类型的数据成员。它允许将多个不同类型的变量组合在一起,形成一个新的数据类型,以便更方便地操作相关数据。


2.枚举类型(Enums)

枚举类型是一种用户自定义的数据类型,用于定义一组相关的命名常量。它允许将一组有限的取值集合在一起,形成一个新的数据类型,以便更清晰地表示程序中的意图。


二、结构体

2.1 结构体的声明

在C语言中,定义结构体使用 struct 关键字,结构体的形式如下:

struct 结构体名 {
    数据类型 成员名1;
    数据类型 成员名2;
    // 更多成员...
};

【示例】:描述⼀个学⽣


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

2.2 结构体变量的创建和初始化

初始化结构体变量:有几种方法可以初始化结构体变量:


1.按照结构体成员的顺序初始化:

#include<stdio.h>

int main()
{
  struct Stu s = { "张三", 19, "男", "202201170248" };
  printf("%s\n", s.name);
  printf("%d\n", s.age);
  printf("%s\n", s.set);
  printf("%s\n", s.id);
  return 0;
}

92e9f22f82fbad03a45ec0638d4ddf4d_d4971e93a6c34a22ba1fd355548ad208.png

2. 按照指定的顺序初始化

前面在说操作符时我们讲过,可以通过点操作符来访问结构体成员,这里同样可以通过点操作符来给结构体成员进行初始化。


int main()
{
  struct Stu s = { .age = 19, .id = "202201170248", .name = "张三", .set = "男" };
  printf("%s\n", s.name);
  printf("%d\n", s.age);
  printf("%s\n", s.set);
  printf("%s\n", s.id);
  return 0;
}

57eb08f23069093c2bdc2a64499dac49_5ca0ecea832e48c8b2f62022f2e55232.png


2.3 结构体的特殊声明

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


匿名结构体类型


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

struct
{
  int a;
  char b;
  float c;
}a[20], *p;

注意:


匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。

p = &x; 这种写法是不合法的,编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。

2.4 结构体的自引用

结构体中的成员不仅可以是内置的数据类型,还可以是这个结构体本身,也就是结构体中包含指向相同类型结构体的指针或引用的情况。这种自引用的数据结构通常称为递归数据结构。


比如说定义一个链表的结点:


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

注意:这种自引用是错误的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的大小就会无穷的大,是不合理的。即无法确定 sizeof(struct Node) 的大小。


正确的自引用方式:


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

在结构体自引用使用的过程中,夹杂了 typedef 对匿名结构体类型重命名,也容易出现引入问题。


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

这种也是错误的,因为Node是对前面的匿名结构体类型的重命名产生的,但是在匿名结构体内部提前使用Node类型来创建成员变量,这是不行的。


解决方案如下:定义结构体不要使用匿名结构体了


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

三、结构体内存对齐

【示例】:计算结构体的大小。

struct S
{
  char c1;  // 占1个字节
  int i;  // 占4个字节
  char c2;  // 占1个字节

};

int main()
{
  struct S s = { 0 };
  printf("%zd\n", sizeof(s));
  return 0;
}

在代码中我们看到结构体中有两个char和一个int,那他的大小就是6个字节,但结果真的是这样吗?

388bad244628270fa510bcb559e60394_44e2b83e331c4adab1d6088ccc0d6d96.png

运行之后发现是12个字节,这是为什么呢?

这说明结构体中的成员不是随便放的,这里面是有一定规则的,这就是结构体的内存对齐。


3.1 对齐规则

首先得掌握结构体的对齐规则:


  1. 结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

对齐数 = 编译器默认的⼀个对齐数与该成员变量大小的较小值。

  • VS 中默认的值为 8
  • Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小

3.结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

7f884a18f734906f7315a6ca84f6873e_e3e83d0b72354e698e537475d24f52c8.png

解析:对照规则1,第一个成员对齐到和结构体变量起始位置偏移量为0,也就是图中为0的位置(char占1个字节),其余的成员变量对齐到对齐数整数倍的位置(int占4个字节,VS的默认值为8,4小于8,即这里的对齐数为4),也就是4的整数倍(图中序号4)开始存,第三个成员变量也一样(char占1个字节小于8,即对齐数是1)。最后结构体总大小是最大对期数(第一个和第三个对齐数都是1,第二个对齐数是4)的整数倍,也就是4的倍数,由于已经占了9个字节,所以下一个4的倍数就是12,这里总共浪费了6个字节的空间大小。


【C语言基础】:自定义类型(一)--> 结构体-2

https://developer.aliyun.com/article/1538319

相关文章
|
6天前
|
算法 Java 程序员
面向对象编程(OOP)通过对象组合构建软件,C语言虽是过程式语言,但可通过结构体、函数指针模拟OOP特性
【6月更文挑战第15天】面向对象编程(OOP)通过对象组合构建软件,C语言虽是过程式语言,但可通过结构体、函数指针模拟OOP特性。封装可使用结构体封装数据和方法,如模拟矩形对象。继承则通过结构体嵌套实现静态继承。多态可通过函数指针模拟,但C不支持虚函数表,实现复杂。C语言能体现OOP思想,但不如C++、Java等语言原生支持。
26 7
TU^
|
7天前
|
编译器 C语言 C++
C语言之自定义类型
C语言之自定义类型
TU^
16 2
|
7天前
|
编译器 C语言 C++
【C语言基础】:自定义类型(二) -->联合和枚举
【C语言基础】:自定义类型(二) -->联合和枚举
|
7天前
|
编译器 C语言
【C语言基础】:自定义类型(一)--> 结构体-2
【C语言基础】:自定义类型(一)--> 结构体
|
15天前
|
C语言
C语言学习记录——枚举(定义、与结构体的区别、优点)
C语言学习记录——枚举(定义、与结构体的区别、优点)
16 3
|
15天前
|
存储 编译器 C语言
C语言学习记录——结构体(声明、初始化、自引用、内存对齐、结构体设计、修改默认对齐数、结构体传参)一
C语言学习记录——结构体(声明、初始化、自引用、内存对齐、结构体设计、修改默认对齐数、结构体传参)一
21 2
|
15天前
|
编译器 Linux C语言
C语言学习记录——结构体(声明、初始化、自引用、内存对齐、结构体设计、修改默认对齐数、结构体传参)二
C语言学习记录——结构体(声明、初始化、自引用、内存对齐、结构体设计、修改默认对齐数、结构体传参)二
18 1
|
16天前
|
编译器 C语言
C语言深度理解之——结构体内存对齐
C语言深度理解之——结构体内存对齐
14 1
|
7天前
|
C语言
C语言——结构体
C语言——结构体
5 0
|
2天前
|
算法 Java C语言
Java中的算法与C语言中的函数
Java中的算法与C语言中的函数
8 2