51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

联合体和枚举

  1. 联合体 {#1-联合体} ===============

在 C 语言中,联合体又叫共用体(Union)是一种特殊的数据类型,定义联合体的语法如下:

|---------------------|---------------------------------------------------| | 1 2 3 4 5 6 | union 联合体名 { 成员类型 成员名1; 成员类型 成员名2; ... }; |

定义联合体变量的语法如下:

|-----------|-------------------------| | 1 | union 联合体名 变量名; |

从语法上来看联合体和结构体非常的类似只是将关键字struct替换成了union,但是,二者在使用的时候有很大区别,其特点如下:

  • 联合体的不同成员共享同一块内存,它们的值互相覆盖。
  • 联合体的大小由最大成员的大小确定。

下面是一个示例,展示如何定义联合体并使用联合体变量:

|------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | // 定义一个联合体类型 union Data { unsigned char c; unsigned int i; unsigned short s; char str[20]; }; int main() { // 定义联合体变量 union Data data; // 打印所有成员的地址 printf("data.c address = %p\n", &data.c); printf("data.i address = %p\n", &data.i); printf("data.s address = %p\n", &data.s); printf("data.str address = %p\n", &data.str); // 打印联合体大小 printf("union size = %zd\n", sizeof(data)); // 设置联合体成员的值并打印 data.i = 10; printf("整数值: %d\n", data.i); data.s = 314; printf("浮点数值: %d\n", data.s); strcpy(data.str, "Hello, Dabing!"); printf("字符串值: %s\n", data.str); data.i = 0xffabcd98; printf("data.c = %x\n", data.c); printf("data.s = %x\n", data.s); printf("data.i = %x\n", data.i); printf("========================\n"); data.c = 0; printf("data.c = %x\n", data.c); printf("data.s = %x\n", data.s); printf("data.i = %x\n", data.i); return 0; } |

程序输出的结果如下:

|---------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | data.c address = 000000875EEFFB38 data.i address = 000000875EEFFB38 data.s address = 000000875EEFFB38 data.str address = 000000875EEFFB38 union size = 20 整数值: 10 浮点数值: 314 字符串值: Hello, Dabing! data.c = 98 data.s = cd98 data.i = ffabcd98 ======================== data.c = 0 data.s = cd00 data.i = ffabcd00 |

通过输出的结果可以证明以下结论:

  • 联合体中各个数据成员共用同一块内存,因为他们的起始地址是相同的
  • 联合体各个数据成员之间会发生数据覆盖
  • 联合体的大小等于占用存储空间最大的成员的大小

另外,还有一个细节需要说明:联合体中的各个成员的类型可能不同,所以在取数据的时候操作的内存大小也不尽相同。

  • data.i = 0xffabcd98; 给整形成员赋值,使用了4个字节的内存
  • printf("data.c = %x\n", data.c); 从一个字节的内存中读数据
    • 0xffabcd98低地址位的一个字节中存储的数据是0x98
  • printf("data.s = %x\n", data.s);从两个字节的内存中读数据
    • 0xffabcd98低地址位的两个字节中存储的数据是0xcd98
  • printf("data.i = %x\n", data.i);从四个字节的内存中读数据

联合体应用:验证当前主机的大小端(字节序)

大小端(Endianness)指的是在多字节数据类型(如整数)在内存中存储时的字节顺序。

  • 大端字节序(Big Endian)是指高位字节存储在低地址
  • 小端字节序(Little Endian)是指低位字节存储在低地址。

举个例子,假设我们有一个 4 字节的整数值 0x12345678 在内存中存储的情况如下:

  • 大端字节序:地址由低到高的顺序依次存储为 12 34 56 78
  • 小端字节序:地址由低到高的顺序依次存储为 78 56 34 12

不同的处理器架构在存储数据时可能采用不同的字节序。例如,x86 架构使用小端字节序,而 PowerPC 架构使用大端字节序。因此,在处理跨平台数据交换时需要注意字节序的问题。

示例代码如下:

|------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #include <stdio.h> union MyData { unsigned int data; struct { unsigned char byte0; unsigned char byte1; unsigned char byte2; unsigned char byte3; }byte; }; int main() { union MyData num; num.data = 0x12345678; if (0x78 == num.byte.byte0) { printf("Little endian\n"); } else if (0x78 == num.byte.byte3) { printf("Big endian\n"); } return 0; } |

由于联合体内部的databyte成员是共用同一块内存,并且二者占用的内存大小相同,所以通过结构体成员byte就可以非常轻松的取出data中各个字节的值,程序中是对最高位的字节值(byte.byte3)和最低位的字节值(byte.byte0)进行了判断。

  1. 枚举 {#2-枚举} =============

枚举(Enumeration)是一种在编程语言中表示一组具名常量的数据类型。枚举常常用于定义一组相关的离散值,比如颜色、星期几、月份等。在 C 语言中,可以使用 enum 关键字定义枚举类型。

定义枚举类型的语法如下:

|-----------|-----------------------------------| | 1 | enum 枚举名 { 值1, 值2, ... }; |

枚举值默认从 0 开始,依次递增。可以显式指定枚举值的值,如 MONDAY = 1, TUESDAY = 2, ...。并且枚举值是常量,不能在程序中用赋值语句再对它赋值。

下面是一个示例,展示如何定义和使用枚举:

|---------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include <stdio.h> // 定义一个枚举类型 enum Weekday { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }; int main() { // 定义枚举变量 enum Weekday today; // 设置枚举变量的值 today = WEDNESDAY; // 使用 switch 语句根据枚举变量的值进行处理 switch (today) { case MONDAY: printf("Today is Monday.\n"); break; case TUESDAY: printf("Today is Tuesday.\n"); break; case WEDNESDAY: printf("Today is Wednesday.\n"); break; case THURSDAY: printf("Today is Thursday.\n"); break; case FRIDAY: printf("Today is Friday.\n"); break; case SATURDAY: printf("Today is Saturday.\n"); break; case SUNDAY: printf("Today is Sunday.\n"); break; default: printf("Invalid day.\n"); } return 0; } |

在上述示例中,使用 enum 关键字定义了一个名为 Weekday 的枚举类型,其中包含了一周的七个枚举值。然后,在 main 函数中,声明了一个名为 today 的枚举变量,并通过赋值将其设置为 WEDNESDAY。接着,使用 switch 语句根据枚举变量的值进行处理,并打印出对应的结果。

其实枚举类型就是一种特殊的整形,使用枚举类型在程序中可以增加代码的可读性,使得相关常量的含义更加清晰。

  1. typedef {#3-typedef} =======================

typedef 是 C 语言中的一个关键字,用于为已存在的数据类型定义新的名称(alias)不能创建新类型。它能够简化复杂的类型声明,提高代码的可读性和可维护性。

typedef 关键字的语法:

|-----------|---------------------------| | 1 | typedef 原类型 新类型名; |

typedef#define用法上有相似之处,但是也有很多不同:

  • typedef仅限于数据类型,而不能是表达式或具体的值
  • #define发生在预处理,typedef发生在编译阶段

3.1 基础类型 {#3-1-基础类型}

下面是一个示例,展示如何使用 typedef 来创建新的类型别名:

|------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #include <stdio.h> typedef int INT; typedef char BYTE; typedef BYTE T_BYTE; typedef unsigned char UBYTE; typedef struct type { UBYTE a; INT b; T_BYTE c; }TYPE, * PTYPE; int main() { TYPE t; t.a = 254; t.b = 10; t.c = 'c'; PTYPE p = &t; printf("%u, %d, %c\n", p->a, p->b, p->c); return 0; } |

在上面的示例代码中给一些基础数据类型定义了别名,然后再基于这些别名定义了相关的变量,很容易理解,不再过多进行解释。

3.2 复合类型 {#3-2-复合类型}

除此之外还可以给结构体、联合体和枚举等数据类型创建新的类型别名。

当使用typedef来定义结构体时,可以为结构体类型提供一个简短、易于使用的别名。下面是一个示例,展示如何使用typedef定义结构体:

|------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <stdio.h> typedef struct { int x; int y; } Point; int main() { // 使用结构体别名创建变量 Point p1; p1.x = 10; p1.y = 20; // 输出结构体变量的值 printf("Point: (%d, %d)\n", p1.x, p1.y); return 0; } |

在上述示例中,使用了匿名的结构体,即在定义结构体时未指定结构体名字。然后,我们使用typedef关键字将这个匿名结构体定义的结构体类型起一个别名Point

使用tpedef定义联合体别名和定义结构体别名语法完全相同,这里就不再举例子了,下面在列举一个定义枚举别名的例子,示例代码如下:

|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include <stdio.h> typedef enum { RED, GREEN, BLUE } Color; int main() { // 使用枚举别名创建变量 Color c = RED; // 输出枚举变量的值 switch (c) { case RED: printf("Color: RED\n"); break; case GREEN: printf("Color: GREEN\n"); break; case BLUE: printf("Color: BLUE\n"); break; } return 0; } |

在上述示例中,我们使用typedef关键字将enum定义的枚举类型起一个别名Color。枚举类型中包含了三个枚举常量REDGREENBLUE

通过使用typedef来定义结构体的别名,可以使代码更加简洁和可读性更高,特别是在结构体类型较为复杂或频繁使用时。

3.3 函数指针 {#3-3-函数指针}

在C语言中,typedef可以用来给一个已有类型起别名,其中也包括函数指针类型。使用typedef来定义函数指针类型可以简化代码,并使其更加易读。下面是一种常见的使用方法:

|-----------|-------------------------------------------| | 1 | typedef int (*FuncPtr)(int, int); |

上述代码定义了一个名为FuncPtr的函数指针类型,该函数指针可以指向返回类型为int接受两个int类型参数的函数。可以根据实际需求调整返回类型和参数类型。

可以使用该函数指针类型来定义函数指针变量并赋值,例如:

|------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h> typedef int (*FuncPtr)(int, int); int add(int a, int b) { return a + b; } int main() { FuncPtr ptr = add; int result = ptr(3, 4); return 0; } |

  • 第11行:定义函数指针变量,并将add函数的地址赋值给函数指针ptr
  • 第12行:调用函数指针ptr,相当于调用add函数

回调函数是一种常见的编程技术,它允许你将一个函数作为参数传递给另一个函数,并在需要的时候被调用。

在C语言中,回调函数通常与函数指针一起使用。你可以将一个函数的指针作为参数传递给另一个函数,在适当的时候调用该函数指针,以执行特定的操作。

下面是一个简单的示例,演示了回调函数的用法:

|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <stdio.h> // 定义函数指针别名 typedef int (*funcPtr)(int, int); int add(int a, int b) { return a + b; } int subtract(int a, int b) { return a - b; } int result(funcPtr func, int a, int b) { int res = func(a, b); res += 100; return res; } int main() { int res = result(add, 100, 200); printf("Result of addition: %d\n", res); res = result(subtract, 500, 200); printf("Result of subtraction: %d\n", res); return 0; } |

通过使用回调函数,你可以将特定的操作从一个函数中分离出来,并通过回调函数的方式动态地执行这些操作。这使得代码更加灵活和可扩展。

赞(0)
未经允许不得转载:工具盒子 » 联合体和枚举