首页 > 其他分享 >C语言中的自定义类型

C语言中的自定义类型

时间:2024-03-29 23:30:20浏览次数:27  
标签:struct 自定义 int C语言 枚举 内存 类型 对齐 结构

在C语言中有三种常见的自定义类型:结构体,联合体,枚举。

1.1  结构体

1.1.1 结构体的声明

struct tag{

    member-list; //成员清单

}variable-list;  //变量清单

例如:我们创建一个结构体的变量,来描述一个学生。

struct Student{
    char name[20];
    int age;
    char sex[5];
    char id[20];
};

1.1.2 结构体的创建与初始化

#include<stdio.h>

struct Student {
    char name[20];
    int age;
    char sex[5];
    char id[20]; 
};

int main(){
   struct Student s1={"张三",18,"男","2110000"};//按照顺序初始化
   struct Student s2={.name="李四",.id="9850000",.age=18,.sex="女"};
  //不按照顺序初始化
    
   return 0;
}

1.1.3 结构体的不完全初始化

    我们在创建一个结构体的时候,允许对结构体进行不完全的初始化。省掉结构体名称  tag。但是我们要在结构体的尾部创建变量,并且在后续程序中不能再创建相同结构体的变量了。

#include<stdio.h>

int main(){
   struct {
      int a;
      double x;
      chua c;
   }x;
   //这里创建一个结构体的时候创建了一个变量x,在后续的程序中不能再创建与x相同类型的结构体变量了。
   return 0;
}

 1.1.4 结构体的自引用

    我们在创建一个结构体变量的时候,能不能将同意类型的结构体当成自己的成员呢?如下:

struct Stu{

      int data;

       struct Stu next; 

};

    如果上述代码可行的话,那么sizeof(struct Stu)的大小多大呢?我们不难发现我们无法计算出其大小,所以我们不能采用这种方式。但是我们可以用以下方式来实现结构体的自引用: 

 我们用存储下一个结构体变量的方式来实现自引用。

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


1.1.5 结构体的大小与内存对齐

我们现在已经掌握了结构体的基本使用方式,那么结构体在内存中的大小是多少呢?

那么我们必须要掌握一个十分重要的知识: 结构体的内存对齐。

结构体的对齐规则:                                                                                                                                                 1.结构体的第一个变量对齐到结构体变量偏移位置为0的内存处。                                                             2.其他的成员对齐到“对齐数”的整数倍的地方。

  对齐数 = 编译器中默认的对齐数 与 变量大小中的较小值

  ( 在VS中编译器默认的对齐数是8,在Linux的gcc环境下没有默认对齐数。)

3.结构体的大小为最大对齐数(所有变量中最大的对齐数)的整数倍。

4.如果结构体中有结构体的嵌套,嵌套的结构体的最大对齐数是其中最大的对齐数。外层结构体的大小满足规则3.

下面以两个例子来深入体会结构体的对齐规则。

struct SSY{
   char a;
   int b;
   double c;
};//exmple 1.

struct SSS{
   int a;
   char ch[5];
   struct SSY w;
};//exmple 2.

 

 上图为struct SSY在内存之中的存储,类似我们可以得到struct SSS的内存。

那为什么结构体要内存对齐呢?

(1)性能原因

 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

(2)平台原因

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

 总体来说:结构体的内存对齐就是以空间换时间。

1.1.6 结构体传参

我们用一段代码来引入:

#include<stdio.h>

struct SSY{
    char a;
    int b;
    double c;
};

void Print1(struct SSY i){
  printf("%c %d %lf ",i.a ,i.b ,i.c);
}

void Print2(struct SSY *j){
   printf("%c %d %lf " ,j->a ,j->b ,j->c);
}

int main(){
     struct SSY z={'t',4,2.5};
     Print1(z);
     Print2(&z);
     return 0;
}

我们比较一下 Print1 和Print2 函数哪一个更好呢?

答案是:Print2.

我们在进行函数传参的时候要进行压栈的操作,要在栈区拷贝一份临时空间,当我们的结构体过大的时候我们的代码性能就会变差,所以我们在进行结构体传参数的时候进行传地址的操作。

2.2联合体

2.2.1 联合体的声明

联合体像结构体一样,联合体也是由一个或者多个变量构成,但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫:共用体。我们给联合体的其中一个成员赋值时,其他成员的值也跟着变。

#include<stdio.h>

//联合体的声明
union SSY{
  char i;
  int j;
};

int main(){
  union SSY s;//定义一个联合体
  s={1};//给联合体进行赋值
  printf("%zd",sizeof(s));
//计算联合体的大小,结果为:4
  return 0;
}

那我们计算的结果为什么就是4呢?

2.2.2 联合体的内存大小计算

我们通过对联合体和结构体的对比来看看联合体的大小。


struct s{
  char c;
  int i;
};

union u{
  char c;
  int i;
};

联合体的大小至少是最大成员的大小,当最大成员的大小不是最大的对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

#include<stdio.h>

int main(){
  union un{
     char c;
     int i;
};
  union un u={0};
  u.i=0x11223344;
  u.c=0x55;
  printf("%u",u.i);
 //结果为:11223355
  return 0;
}

我们由这个例子可以体会到其中的奥妙,对于此结构体 union un u在内存中的存储如下图:

3.3 枚举

3.3.1 枚举的声明

枚举顾名思义就是一一列举,我们可以定义一个变量将它可能出现的值进行一一列举。

enum DAY{
   Mon,
   Tues,
   Wen,
   Thur,
   Fri,
   Sat,
   Sun,
};

enum SEX{
  male,
  female;
};

这里我们举了两个例子,一个是今天是星期几,第二个是性别。其中enum DAY 和enum SEX是枚举类型,{}中的值是枚举常量。枚举常量在枚举类型中的编号默认是从0开始的,但是我们也可以人为的对其进行修改。

3.3.2 枚举类型的优点

我们从枚举类型的定义可以知道枚举和 # define很相似,其实枚举就是 # define 的plus版本。

那么我们为什么要用枚举呢?

<1>.增加代码的可读性和可维护性。

<2>.和#define定义的标识符比较枚举有类型检查,更加严谨。

<3>.便于调试,在编译器的预处理阶段会删除 # define定义的符号。

<4>.使用方便,一次可以定义多个常量。

<5>.枚举类型遵循作用域的规则。

标签:struct,自定义,int,C语言,枚举,内存,类型,对齐,结构
From: https://blog.csdn.net/2301_80758704/article/details/137060773

相关文章

  • 数据结构之————线性表ADT、以数组存储方式实现抽象类型的一个实例
    前言:基础填坑1、ADT在文章开始前,我们要弄明白什么是ADT(AbstractDataType)抽象数据类型1、ADT是用户定义的数据类型,它包含一组数据以及在这组数据上进行的操作。只定义操作的行为,没有具体的实现细节2、它存在的目的是使我们能够独立于程序的实现细节来理解数据结构的特......
  • 【零基础C语言】文件操作
    目录 理解文件操作 什么是文件 程序文件 数据文件 文件名字 二进制文件和文本文件 文件的打开和关闭 文件的打开和关闭操作实验1,打开一个文件并且输入26个字母 打开读取文件text.txt,并且将它拷贝进text_cpy.txt 使用fputs和fgets函数 使用fprint......
  • 【零基础C语言】预处理解析
     预定义符号 c语言中设置一些预定的符号,我们可以直接使用//列:__FILE__//进⾏编译的源⽂件__LINE__//⽂件当前的⾏号__DATE__//⽂件被编译的⽇期__TIME__//⽂件被编译的时间__STDC__//如果编译器遵循ANSIC,其值为1,否则未定义 /续行符号intmain(){ printf......
  • 【零基础C语言】动态内存管理
     目录1.动态内存分配的意义 2.malloc函数 3.free函数 4.calloc函数 5.realloc函数 6.常见的动态内存错误 6.1对NULL指针的解引⽤操作6.2对动态开辟空间的越界访问6.3对非动态开辟内存使⽤free释放 6.4使⽤free释放⼀块动态开辟内存的⼀部分 6.5......
  • 用C语言实现汉诺塔游戏
    汉诺塔游戏。游戏目标是将A柱子上的盘子移动到C柱子上,且每次小的盘子要放在大的盘子上面。如只有一个盘子则直接移至C柱子。以如图所示为例子。3个盘子要移动至C柱子,具体步骤为:A到C,A到B,C到B,A到C,B到A,B到C,A到C。总共7步。也就是先把A柱子两个盘子(n-1)通过C柱子移......
  • 简单介绍c语言程序的编译与链接
    程序运行的背后程序在运行时经历了四个步骤,分别是预编译(Prepressing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。预编译预编译也称预处理,源代码文件(.c)和相关的头文件(.h)等被预编译器cpp预编译成一个.i文件。编译编译过程就是将预处理后的文件进行一系列......
  • postgresql自定义函数实现功能有两个数组arr1,arr2,返回第一个数组中不在第二个数组的
    CREATEORREPLACEFUNCTIONarray_difference(arr1text[],arr2text[])RETURNStext[]AS$$DECLAREresult_arrtext[];BEGIN--初始化结果数组为一个空数组result_arr:='{}';--遍历第一个数组中的每个元素FORiIN1..array_leng......
  • postgresql自定义函数实现三个数组存在相同数据,且在第四个数组中不存在的数据
    --使用postgresql语言写一个函数,实现以下功能:--1有管理权限用户数组、列表权限用户数组、查看权限用户数组、无权限用户数组四个用户数组--2当无权限用户数组存在用户数据时,如果管理权限用户数组,列表权限用户数组,查看权限用户数组中存在相同的用户数据,并且和无权限用户数......
  • C语言 结构体和联合体、解释C语言中的结构体及其用途。
    一、结构体和联合体结构体和联合体在C语言中都是重要的数据类型,但它们在使用和特性上有所区别。结构体是由一批数据组合而成的结构型数据,由一组称为成员(或称为域,或称为元素)的不同数据组成,其中每个成员可以具有不同的类型。结构体中的每个数据成员都占用自己的内存空间,结构体......
  • 描述C语言中的循环结构(for,while,do-while)
    一、描述C语言中的循环结构(for,while,do-while)C语言提供了三种主要的循环结构:for循环、while循环和do-while循环。这些循环结构允许你重复执行一段代码,直到满足特定的条件为止。for循环for循环通常用于已知循环次数的场景。它的基本语法如下:c复制代码for(初始化;条件;更......