深究C语言-5结构体后续(除结构体外的自定义类型)

简介: c99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。

上一篇我们说了好多关于结构体和链表的知识,但是还有一些知识我还是没有说到,今天来给大家梳理一下。


一,柔性数组


一,定义


c99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。


下面有两种定义方式。下标可以不写,也可以写作0;


struct s1 {
  int i;
  int a[];
};
struct s2 {
  int i;
  int a[0];
};


二,特点


ea20a59fd16c4d1688b898a23d3b1c9f.png

ee6800ed6013438397ac5bd372b8f6ac.png


三,使用


首先先看下面这两段代码


1.


#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
typedef struct s1 {
  int i;
  int a[];
}pnode;
int main() {
  //期待a数组的大小是10个整形
  pnode* ps = (pnode*)malloc(sizeof(pnode)+10*sizeof(int));
  //判断
  if (!ps)
    exit(1);
  //初始化数据
  ps->i = 10;
  for (int i = 0; i < 10; i++) {
    ps->a[i] = i;
  }
  //空间不足,运用realloc函数来增加。
  pnode* pss = (pnode*)realloc(ps,sizeof(pnode) + 20 * sizeof(int));
  //如果,分配内存成功,就把该指针的地址给ps,
  //要搞清楚这个函数的用法
  if (pss != NULL) {
    ps = pss;
  }
  //使用
  //释放
  free(ps);
  //释放后一定要记得置空
  ps = NULL;
  return 0;
}


2.


#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
typedef struct s1 {
  int i;
  int *a;
}pnode;
int main() {
  pnode* ps = (pnode*)malloc(sizeof(pnode));
  //判断
  if (!ps)
    exit(1);
  //a此时并没有指向,是一个野指针,故现在要对其申请内存空间
  //给它10个整形的空间
  //这个地方也有不同的地方
  //这里是为了结构体指针变量ps指向的a指针申请内存空间,强制转换为int*类型。
  ps->a = (int *)malloc(10 * sizeof(int));
  //判断
  if (ps->a == NULL) {
    exit(1);
  }
  //初始化数据
  ps->i = 10;
  for (int i = 0; i < 10; i++) {
    ps->a[i] = i;
  }
  //空间不足,运用realloc函数来增加。这里我们需要单独对ps->a的地址来进行申请
  int* ptr = (int*)realloc(ps->a, 10 * sizeof(int));
  //判断,如果分配内存成功,就把该指针的地址给ps,
  if (ptr != NULL) {
    ps->a = ptr;
  }
  //使用
  //释放
  //这里的释放是有东西的,要先释放ps->a,因为如果先释放了ps,*a就成了野指针,他指向的空间已经被释放了,故不能再次使用。
  free(ps->a);
  ps->a = NULL;
  free(ps);
  ps = NULL;
  return 0;
}


9bdb0146674c4caf90970c3409182156.png


注意:


根据上面的两段代码,我们来看一看realloc这个函数。


21c1fa0b329b400fb0d4ecae132b798a.png


ptr必须是以前通过动态存储分配得到的指针。size是现在需要的空间大小,如果分配失败,返回NULL,同时原来ptr指向的存储块的内容不变。


如果成功,返回一片能存放大小为size的区块,并保证该块的内容与原块一致,如果size小于原来的区块大小,则内容为原块前size范围内的内容。如果新块更大,则原有数据存在新块的前一部分。后一部分记得要初始化。


如果分配成功,原存储块的内容就很可能被改变了,因此不允许再使用ptr去使用它。


二,位段


1de0011f835e41e183f1ff30de36fb31.png


成员也可以是char,char类型也是以整型的方式存储的。


ee9e23613b4a4bbaa07f22a91e5881db.png

3342c1192fd741059486e5711603c242.png


这个数字代表的是比特


cdddb4949b3148ceb633bed4b1589e67.png


该位段的大小为8个字节,由上可知,位段的大小空间开辟是根据需求以四个字节(int)或者一个字节(char)的方式来开辟的。


刚开始的时候开辟了四个字节用来存放,存完三个变量后,剩余的空间已经不足以用来存放d变量,故,需要再开辟一个int的空间,用来存放,所以,该位段的大小是8个字节。


再看,存放完a,b,c三个变量后,剩余15个比特位,C语言中并未说明这剩余的部分到底使不使用,所以一般使用位段时是不跨平台的。


8d68a295a46f4b4788f2dfe86e630613.png


需要注意的是,成员变量的大小不能大于一个int,因为充满了太多的不确定因素。


ce2f280365e74da7abe6e41b9560a7e5.png


三,枚举和联合体(共用体)


一,枚举


d6e922f1c7a547c6aab43640a61c9343.png


顾名思义,就是一一列举。


括号中间的就是它的成员常量,是枚举可能的取值,并不是说都会取到,一次使用只能取到其中一个常量。


f0bdaeff70694f97abf033ec82b3391a.png


枚举类型的成员都是常量,通常是指无符号整型,既然是常量,那么,他们都是有相应的值的。一般来说,都是从0开始,向后递增


52403d8b6bab4a7294d272effb2c2ac6.png


我们也可以根据自己的需要,对成员常量进行赋值。


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


0a955cac15d54a1783860b14a0710eb4.png


对于具体而言,枚举到底有什么用?以下面这个计算器的模拟为例。我设置了一个枚举类型,使程序的可读性增加。


#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void menu(void) {
  //提供菜单
  printf("************请选择****************\n");
  printf("******1.Add*********2.Sub*********\n");
  printf("******3.Mul*********4.Div*********\n"); 
  printf("************0.exit****************\n");
  printf("**********************************\n");
}
//写计算器要用的函数
int Add(int x, int y) {
  return x + y;
}
int Sub(int x, int y) {
  return x - y;
}
int Mul(int x, int y) {
  return x * y;
}
int Div(int x, int y) {
  return x / y;
}
enum s{
    exit,
    Add,
    Sub,
    Mul,
    Div
}
int main() {
  //因为计算器可以反复使用,所以用循环来写
  //至于用while还是do-while都是看自己喜好
  do {
    //在循环中定义变量,每次循环开始时初始化数据,就不会影响后面的运算
    int x, y, ret;
    x = y = ret = 0;
    int input=0;
    //输出菜单
    menu();
    //选择函数来运行程序,达到自己的目的
    printf("请选择:>");
    scanf("%d",&input);
    //因为选项很多,太过复杂。选择用switch结构
    switch (input) {
    case Add:
      printf("请输入2个操作数:>");
      scanf("%d%d", &x, &y);
      //调用函数
      ret = Add(x, y);
      printf("ret=%d\n", ret);
      break;
    case Sub:
      printf("请输入2个操作数:>");
      scanf("%d%d", &x, &y);
      ret = Sub(x, y);
      printf("ret=%d\n", ret);
      break;
    case Mul:
      printf("请输入2个操作数:>");
      scanf("%d%d", &x, &y);
      ret = Mul(x, y);
      printf("ret=%d\n", ret);
      break;
    case Div:
      printf("请输入2个操作数:>");
      scanf("%d%d", &x, &y);
      ret = Div(x, y);
      printf("ret=%d\n", ret);
      break;
    //如果输入为0,程序结束,直接跳出
    case exit:printf("程序结束");
      break;
    //如果输入的是其他数,给用户继续选择的机会。
    default:printf("选择错误,请重新选择");
      break;
    }
  } while (input);
  return 0;
}


还有一些操作


056a582c15aa49f5bcb2e1411bfadce4.png


我们可以直接把枚举类型变量定义为某一个变量,但是,不能直接赋值为一个整数,类型转换会导致数据的丢失。


d6a385663b314a8ea5bea99ba21bdfb1.png


也可以写一个限定符,限定blue就是枚举类型s的一个成员。


我们要知道的是,枚举类型的成员变量只能是int或者size_t int,所以,不管枚举类型中含有多少个成员,大小永远是4;


二,联合体(共用体)


一,定义


84bf2dd9d6b9464a8536134dfd3d9e7b.png


二,特点


915a6540ace444ea8ee33f42ca81b89f.png


8e4e66300a304087be205fed39511b0b.png


我们尝试来给新建的联合体变量赋值,我们发现,i和c里面存放了相同的值,这也就是所谓的共用体。


4464f02870c242719bd4bf197189c3a2.png


在联合体中最好是不要单独对其中的每个元素赋值,会影响其他成员的值。


7fb96087192e46328896c7ce2ac1951f.png


三,联合体的计算


bccfe72a51ae46d389d24e90ddc9d060.png

ccc2d2511aa24b608677dfd5b077f675.png


以这个题目为例,这个联合体的大小就是8.


好了,大概就说这么多,已经足够大家使用和学习了。


祝诸君学业有成,诸事顺利。

目录
相关文章
|
2天前
|
程序员 C语言
【C语言】初步认识结构体
【C语言】初步认识结构体
15 0
|
2天前
|
C语言
C语言之分支结构
C语言之分支结构
24 0
|
17小时前
|
存储 C语言
C语言结构体—自定义类型—struct
C语言结构体—自定义类型—struct
7 0
|
2天前
|
存储 Serverless C语言
每天一道C语言编程(结构体的运用):这是一年的第几天?
每天一道C语言编程(结构体的运用):这是一年的第几天?
6 0
|
2天前
|
算法 C语言
C语言易混淆、简单算法、结构体题目练习、常见关键字总结-2
C语言易混淆、简单算法、结构体题目练习、常见关键字总结
|
2天前
|
算法 编译器 API
C语言易混淆、简单算法、结构体题目练习、常见关键字总结-1
C语言易混淆、简单算法、结构体题目练习、常见关键字总结
|
2天前
|
存储 编译器 程序员
C语言:自定义类型 - 结构体 & 联合体 & 枚举
C语言:自定义类型 - 结构体 & 联合体 & 枚举
12 2
|
2天前
|
存储 编译器 C语言
[C语言]自定义类型(结构体~枚举~联合体)
[C语言]自定义类型(结构体~枚举~联合体)
|
2天前
|
存储 算法 程序员
【C言专栏】C 语言结构体的应用与实践
【4月更文挑战第30天】C语言中的结构体是自定义数据类型的关键,它组合不同类型的數據以创建新类型,尤其适合处理复杂对象如学生信息。通过定义结构体如`struct Student`,包含名字、学号和成绩,可以方便地实例化和访问成员。结构体在链表实现、函数参数传递和数组中都有广泛应用,如表示链表节点和处理批量数据。理解并熟练运用结构体对于C语言编程至关重要,能提升代码效率和可读性。
|
2天前
|
C语言
【精通C语言】:分支结构if语句的灵活运用
【精通C语言】:分支结构if语句的灵活运用
26 1