『C语言进阶』自定义类型详解(一)

简介: 『C语言进阶』自定义类型详解

前言

C语言提供了一些由系统已定义好的数据类型,如:int,float,char等,用户可以在程序中用它们定义变量,解决一般的问题,但我们要处理的问题往往比较复杂,只有系统提供的类型还不能满足应用的需求,C语言允许用户根据自己建立一些数据类型,并用它来定义变量,比如结构体,枚举,联合体这些自定义类型

接下来小羊就带铁汁们学习关于结构体,枚举以及联合体的相关内容~~


一、结构体

1.1 结构体的基础知识

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

声明结构体类型的一般形式:

struct 结构体名

{

//成员变量,是用来描述结构体对象的相关属性

成员列表

};

1.2 结构体的声明

1.先声明结构体类型,再定义该类型的变量

struct stu
{     
  char name[20];    
  int stuid[20];    
  int age;
    int height;
};
int main()
{
    struct stu s1;
    struct stu s2;
    return 0;
} 

2.在声明类型的同时定义:

struct stu
{     
  char name[20];    
  int stuid[20];    
  int age;
    int height;
}s1,s2;

这种定义结构体的一般形式为:

struct 结构体名

{

成员列表;

}变量名列表;

1.3 结构体的特殊声明

匿名结构体类型

#include<stdio.h>
struct
{
  char a;
  int b;
  double c;
}d;//结构体必须在这里命名,而且只能使用一次
struct
{
  char a;
  int b;
  double c;
}* ps;
int main()
{
  ps = &d;//这里不能这样使用,这是非法的
  //编译器认为等号两边是不同的结构体类型
  return 0;
}

仔细观察上面的这两个结构是不是除了变量名不一样,其他都完全一样

那问题来了,这两个结构体是相同的结构体吗???

答案当然不是,省略掉结构体标签后,编译器会认为这两个结构体是不一样的,即使这两个结构体成员是一样的,编译器也会把上面的两个结构体声明当成完全不同的类型

1.4 结构体的自引用

struct Node
{
     int Data;           
     struct Node* Next; 
};
#include<stdio.h>
typedef struct Node//重命名这个结构体为Node
{
    int Data;            
    struct Node* Next; 
}Node;
int main()
{
    struct Node n1 = { 0 };//写法一
    Node n2 = { 0 };//写法二
    return 0;
}

1.5 结构体的初始化和访问

初始化:

struct Student
{
  int num;
  char name[20];
  char sex[5];
  int age;
}s1 = { 1,"李四","女",19 };
int main()
{
  struct Student s2 = { 2,"张三","男",18 };
  printf("%d %s %s %d\n", s1.num, s1.name, s1.sex, s1.age);
  printf("%d %s %s %d\n", s2.num, s2.name, s2.sex, s2.age);
  return 0;
}

结构体嵌套初始化:

struct Point
{
    int a;
    double b;
};
struct Node
{
  int data;
  struct Point p;
  struct Node* next;
}n1 = { 10, {4,5}, NULL };

结构体访问:

  • p.成员名
  • (*p).成员名
  • p->成员名

1.6 结构体内存对齐

结算结构体的大小,是一个很热门的考点:结构体内存对齐

#include<stdio.h>
struct S1
{
  char s1;
  int i;
  char s2;
};
struct S2
{
  int i;
  char s1;
  char s2;
};
int main()
{
  printf("%d\n", sizeof(struct S1));
  printf("%d\n", sizeof(struct S2));
  return 0;
}

运行结果:

12
8

按照我们现在的所认知,char是一个字节,int是两个字节,所以这两个结构体大小不应该是1+1+4=6个字节吗??但运行起来的结果却跟我们想的不一样,那么就让铁汁们带着疑问往下看~~

我们可以通过宏(offsetof)来观察结构体中各成员变量距起始位置的偏移量

offsetof:计算结构体成员变量相较于结构体起始位置的偏移量

#include<stdio.h>
#include<stddef.h>
struct S1
{
  char s1;
  int i;
  char s2;
};
int main()
{
  printf("%d\n", sizeof(struct S1));
  printf("%d\n", offsetof(struct S1,s1));
  printf("%d\n", offsetof(struct S1, i));
  printf("%d\n", offsetof(struct S1, s2));
  return 0;
}

运行结果:

12
0
4
8

好,接下来小羊来解开小伙伴们的疑惑~

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

  • 第一个成员在与结构体变量偏移量为0的地址处。
  • 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。 VS中默认的值为8 Linux中没有默认对齐数,对齐数就是成员自身的大小
  • 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  • 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

第一个成员是s1,所以在偏移量为0处,它的对齐数是1,占一个字节;

第二个成员是i,它的对齐数是4,所以从偏移量为4开始,占四个字节;

第一个成员是s2,它的对齐数是1,所以从偏移量为8开始,占一个字节;

为什么存在内存对齐?

  1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总体来说:

结构体的内存对齐是拿空间来换取时间的做法,我们让占用空间小的成员尽量集中在一起,就可以节省空间。

1.7 修改默认对齐数

#include <stdio.h>
#pragma pack(2)//设置默认对齐数为2
struct S1
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
    printf("%d\n", sizeof(struct S1));
    return 0;
}

运行结果:

8

在不改变对齐数的情况下,运行结果应该是12

结构体在对齐方式不合适的时候,我们可以自己改变默认对齐数

1.8 结构体传参

#include<stdio.h>
struct S
{
  int a;
  int b;
};
struct S s = { 1,9 };
void Print(struct S* ps)
{
  printf("%d\n", ps->b);
}
int main()
{
  Print(&s);
  return 0;
}

函数传参的时候,参数需要压栈,会有时间和空间上的系统开销

如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销过大,会导致性能的下降

所以结构体传参的时候,传结构体地址

『C语言进阶』自定义类型详解(二):https://developer.aliyun.com/article/1392702

相关文章
|
5月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
22天前
|
安全 编译器 Linux
【c语言】轻松拿捏自定义类型
本文介绍了C语言中的三种自定义类型:结构体、联合体和枚举类型。结构体可以包含多个不同类型的成员,支持自引用和内存对齐。联合体的所有成员共享同一块内存,适用于判断机器的大小端。枚举类型用于列举固定值,增加代码的可读性和安全性。文中详细讲解了每种类型的声明、特点和使用方法,并提供了示例代码。
21 3
|
1月前
|
存储 编译器 Linux
C语言——自定义类型
C语言——自定义类型
|
1月前
|
存储 编译器 C语言
【C语言】自定义类型:联合与枚举的简明概述
【C语言】自定义类型:联合与枚举的简明概述
|
3月前
|
存储 编译器 C语言
【C语言篇】自定义类型:联合体和枚举详细介绍
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。
45 1
|
3月前
|
存储 安全 编译器
C语言自定义类型
C语言自定义类型
33 10
|
5月前
|
编译器 测试技术 C语言
【C语言】:自定义类型:结构体的使用及其内存对齐
【C语言】:自定义类型:结构体的使用及其内存对齐
68 7
|
5月前
|
编译器 C语言 C++
【C语言基础】:自定义类型(二) -->联合和枚举
【C语言基础】:自定义类型(二) -->联合和枚举
|
5月前
|
编译器 C语言
【C语言基础】:自定义类型(一)--> 结构体-2
【C语言基础】:自定义类型(一)--> 结构体
|
5月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
49 0