C语言自定义类型篇——自定义类型:结构体,枚举,联合(下)

简介: 笔记

枚举


枚举类型的定义

enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
enum Color//颜色
{
RED,
GREEN,
BLUE
};

在枚举里面,每个常量后面用逗号不用分号

1.png

枚举常量是有值的,枚举的取值从0开始,一次往下增1

2.png

枚举常量的数值可以进行修改,当用枚举创建一个变量时才会占用空间


枚举的优点

枚举的优点:

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

3.png

4.png


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

5.png



#define定义的参数,调试时看不到参数具体内容,会直接完成替换,调试过程中看不到M的值,m会直接被替换成M

6.png7.png


C语言下编译器不会报错,C++语法下编译器会报错

3. 防止了命名污染(封装)

4. 便于调试

5. 使用方便,一次可以定义多个常量


联合(共用体)


联合也是一种特殊的自定义类型

这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间(所以联合也叫共用体)。

比如:


//联合类型的声明
union Un
{
char c;
int i;
};
//联合变量的定义
union Un un;

8.png


这里是4,是因为共用同一块空间

9.png

c所用空间为1


i所用空间为1,2,3,4


联合体的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。


11.png

12.png

用联合体进行大小端判断

int check_sys()
{
  union Un
  {
  char c;
  int i;
  }u;
  u.i = 1;
  return u.c;
}
int main()
{
  if (check_sys())
  printf("小端");
  else
  printf("大端");
  return 0;
}
13.png

联合大小的计算


14.png

union Un
{
  char arr[5];
  int i;
};
int main()
{
  printf("%d\n", sizeof(union Un));
  return 0;
}

这里发生了对齐,arr每个元素类型是char,占一个字节,对齐数为1,i对齐数为4,所以答案是8

15.png

这里arr占1,2,3,4,5,i占1,2,3,4,由于要对齐所以多开辟了三个空间,只不过没用而已

16.png

这里a1,a2,a3,a4,a5都占用一个字节就是1,i占用四个字节,1,2,3,4

17.png

数组在创建时会一次性占用自身大小的字节数,而不是数组内容公用一个空间


18.png

19.pngshort数组占用1-14,i占用1,2,3,4,要对齐即4的倍数,所以占用16个字节 ,short数组的对齐数是2,


习题1

在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是( )


struct A
{
 int a;
 short b;
 int c;
 char d;
};
struct B
{
 int a;
 short b;
 char c;
 int d;
};

16和12

两个结构体都是向int看齐。结构体A中,a独自对齐一个4字节,b+c超过了4字节,所以b独自对齐一个4字节,c独自对齐一个4字节,剩下一个d独自对齐一个4字节,共16字节。结构体B中,a独自对齐一个四字节,b+c+d才超过了4字节,所以b和c一起对齐一个4字节,d单独对齐一个4字节,共12字节,故选C。


习题2

下面代码的结果是:( )


#pragma pack(4)/*编译选项,表示4字节对齐 平台:VS2013。语言:C语言*/
int main(int argc, char* argv[])
{
  struct tagTest1
  {
    short a;
    char d; 
    long b;   
    long c;   
  };
  struct tagTest2
  {
    long b;   
    short c;
    char d;
    long a;   
  };
  struct tagTest3
  {
    short c;
    long b;
    char d;   
    long a;   
  };
  struct tagTest1 stT1;
  struct tagTest2 stT2;
  struct tagTest3 stT3;
  printf("%d %d %d", sizeof(stT1), sizeof(stT2), sizeof(stT3));
  return 0;
}
#pragma pack()


12 12 16


三个结构体都向最长的4字节long看齐。第一个a+d+b才超过4字节,所以a和d一起对齐一个4字节,剩下两人独自占用,共12字节,第二个同理c,d合起来对齐一个四字节,也是12字节。第三个因为c+b,d+a都超过4字节了,所以各自对齐一个4字节,共16字节。故选A。


习题3

有如下宏定义和结构定义


#define MAX_SIZE A+B
struct _Record_Struct
{
  unsigned char Env_Alarm_ID : 4;
  unsigned char Para1 : 2;
  unsigned char state;
  unsigned char avail : 1;
}*Env_Alarm_Record;
struct _Record_Struct *pointer = (struct _Record_Struct*)malloc
(sizeof(struct _Record_Struct) * MAX_SIZE);

当A=2, B=3时,pointer分配( )个字节的空间。

9

说明:结构体向最长的char对齐,前两个位段元素一共4+2位,不足8位,合起来占1字节,最后一个单独1字节,一共3字节。另外,#define执行的是查找替换, sizeof(struct _Record_Struct) * MAX_SIZE这个语句其实是3*2+3,结果为9,故选D。


习题4

下面代码的结果是( )


int main()
{
  unsigned char puc[4];
  struct tagPIM
  {
    unsigned char ucPim1;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
  }*pstPimData;
  pstPimData = (struct tagPIM*)puc;
  memset(puc,0,4);
  pstPimData->ucPim1 = 2; 
  pstPimData->ucData0 = 3;
  pstPimData->ucData1 = 4;
  pstPimData->ucData2 = 5;
  printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]);
  return 0;
}


02 29 00 00

puc是一个char数组,每次跳转一个字节,结构体不是,它只有第一个元素单独享用一字节,其他三个元素一起共用一字节,所以puc被结构体填充后,本身只有两个字节会被写入,后两个字节肯定是0,至此AD排除,然后第一个字节是2就是2了,第二个字节比较麻烦,首先ucData0给了3其实是越界了,1位的数字只能是0或1,所以11截断后只有1,同理ucData1给的4也是越界的,100截断后是00,只有5的101是正常的。填充序列是类似小端的低地址在低位,所以排列顺序是00 101 00 1。也就是0010 1001,即0x29,故选B。

20.png

习题5

在VS2013下,默认对齐数为8字节,这个结构体所占的空间大小是( )字节


typedef struct{
  int a;
  char b;
  short c;
  short d;
}AA_t;


A.16


B.9


C.12


D.8


12

习题6

#include <stdio.h>
union Un
{
  short s[7];
  int n;
};
int main()
{
  printf("%d\n", sizeof(union Un));
  return 0;
}

A.14


B.4


C.16


D.18


结构体向int对齐,7个short一共是14字节,对齐后是16字节。n是单独的4字节,由于是union,所以n与s共用空间,只取最长的元素,故占用16字节


习题7

在X86下,小端字节序存储,有下列程序


#include<stdio.h>
int main()
{
  union
  {
    short k;
    char i[2];
  }*s, a;
  s = &a;
  s->i[0] = 0x39;
  s->i[1] = 0x38;
  printf(“%x\n”,a.k);
  return 0;
}

输出结果是( )


A.3839


B.3938


C.380039


D.不确定


union只有2字节,2字节的十六进制只有4位,所以答案CD排除。而位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,所以是3839,故选A。

21.png

习题8

关于动态内存函数的说法错误的是:( )


A.malloc函数向内存申请一块连续的空间,并返回起始地址


B.malloc申请空间失败,返回NULL指针


C.malloc可以向内存申请0字节的空间


D.malloc申请的内存空间,不进行释放也不会对程序有影响


D


说明:不释放会产生内存碎片,小型程序可以不关注,但是在中大型程序上影响极其深刻。故选D。AB是函数的基本功能,C选项比较特殊,malloc(0)是允许的,也会返回一个指针,只是没有空间所以不可使用而已。



习题9

下面代码的结果是:( )


enum ENUM_A
{
  X1,
  Y1,
  Z1 = 255,
  A1,
  B1,
};
enum ENUM_A enumA = Y1;
enum ENUM_A enumB = B1;
printf("%d %d\n", enumA, enumB);

A.1, 4


B.1, 257


C.2, 257


D.2, 5


枚举默认从0开始,所以X1是0,故Y1是1,给了数字后会根据数字向后推,那么Z1是255,A1是256,所以B1是257,故选B。



习题10

关于动态内存相关函数说法错误的是:( )


A.malloc函数和calloc函数的功能是相似的,都是申请一块连续的空间。


B.malloc函数申请的空间不初始化,calloc函数申请的空间会被初始化为0


C.realloc函数可以调整动态申请内存的大小,可大可小


D.free函数不可以释放realloc调整后的空间


realloc在操作过程中是释放旧空间分配并返回新空间,所以返回的新空间也是需要释放的,故选D。AB是malloc和calloc的区别。C是realloc的基础功能。


 


相关文章
|
30天前
|
存储 C语言
如何在 C 语言中实现结构体的深拷贝
在C语言中实现结构体的深拷贝,需要手动分配内存并逐个复制成员变量,确保新结构体与原结构体完全独立,避免浅拷贝导致的数据共享问题。具体方法包括使用 `malloc` 分配内存和 `memcpy` 或手动赋值。
42 10
|
29天前
|
安全 编译器 Linux
【c语言】轻松拿捏自定义类型
本文介绍了C语言中的三种自定义类型:结构体、联合体和枚举类型。结构体可以包含多个不同类型的成员,支持自引用和内存对齐。联合体的所有成员共享同一块内存,适用于判断机器的大小端。枚举类型用于列举固定值,增加代码的可读性和安全性。文中详细讲解了每种类型的声明、特点和使用方法,并提供了示例代码。
24 3
|
29天前
|
存储 大数据 编译器
C语言:结构体对齐规则
C语言中,结构体对齐规则是指编译器为了提高数据访问效率,会根据成员变量的类型对结构体中的成员进行内存对齐。通常遵循编译器默认的对齐方式或使用特定的对齐指令来优化结构体布局,以减少内存浪费并提升性能。
|
1月前
|
编译器 C语言
共用体和结构体在 C 语言中的优先级是怎样的
在C语言中,共用体(union)和结构体(struct)的优先级相同,它们都是用户自定义的数据类型,用于组合不同类型的数据。但是,共用体中的所有成员共享同一段内存,而结构体中的成员各自占用独立的内存空间。
|
1月前
|
存储 C语言
C语言:结构体与共用体的区别
C语言中,结构体(struct)和共用体(union)都用于组合不同类型的数据,但使用方式不同。结构体为每个成员分配独立的内存空间,而共用体的所有成员共享同一段内存,节省空间但需谨慎使用。
|
1月前
|
编译器 C语言 C++
C语言结构体
C语言结构体
28 5
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
35 3
|
C语言
《C语言及程序设计》实践项目——枚举应用
返回:贺老师课程教学链接 【项目1-对称点】 设计函数,可以按指定的方式,输出一个平面点的对称点 下面给出枚举类型定义和main函数(测试函数),请写出output函数的实现。 #include&lt;stdio.h&gt; enum SymmetricStyle {axisx, axisy, point};//分别表示按x轴, y轴, 原点对称三种方式 void
1093 0
|
14天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
29 6
|
1月前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
39 10
下一篇
无影云桌面