C语言自定义数据类型(五)共用体类型

news/2024/11/30 7:45:16/

目录

一、什么是共用体类型

二、引用共用体变量的方式

三、共用体类型数据的特点

3.1特点

3.2举例说明


一、什么是共用体类型

有时想用同一段内存单元存放不同类型的变量。例如,把一个短整型变量、一个字符型变量和一个实型变量放在同一个地址开始的内存单元中。以上 3 个变量在内存中占的字节数不同,但都从同一地址开始(图中设地址为 1000)存放,也就是使用覆盖技术,后一个数据覆盖了前面的数据。这种使几个不同的变量共享同一段内存的结构,称为 “ 共用体 ” 类型的结构。

定义共用体类型变量的一般形式为:

union共用体名
{

        成员表列

}变量表列;

union Data
{int i;		//表示不同类型的变量i,ch,f可以存放到同一段内存空间中char ch;float f;
}a, b, c;

也可以将类型说明与变量定义分开:

union Data			//声明共用体类型
{int i;char ch;float f;
};
union Data a, b, c;	//用共同体类型定义变量

即先声明一个 union Data 类型,再将a,b,c定义为 union Data 类型的变量。当然也可以直接定义共用体变量,例如:

union				//没有定义共用体类型名
{int i;char ch;float f;
}a, b, c;

可以看到,“ 共用体 ” 与 “ 结构体 ”的定义形式相似。但它们的含义是不同的。

结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。而共用体变量所占的内存长度等于最长的成员的长度。例如,上面定义的 “ 共用体 ” 变量a,b,c各占 4 个字节(因为一个 float 型变量占4个字节),而不是各占 4+1+4 = 9 个字节。

二、引用共用体变量的方式

只有先定义了共用体变量才能引用它,但应注意,不能引用共用体变量,而只能引用共用体变量中的成员。例如,前面定义了 a,b,c为共用体变量,下面的引用方式是正确的:

a.i           (引用共用体变量中的整型变量i)
a.ch          (引用共用体变量中的字符变量ch)
a.f           (引用共用体变量中的实型变量f)

不能只引用共用体变量,例如下面的引用是错误的:

printf("%d", a);

因为 a 的存储区可以按不同的类型存放数据,有不同的长度,仅写共用体变量名 a,系统无法知道究竟应输出哪一个成员的值。

三、共用体类型数据的特点

3.1特点

在使用共用体类型数据时要注意以下一些特点:

(1)同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一个成员,而不是同时存放几个。其道理是显然的,因为在每一个瞬时,存储单元只能有唯一的内容,也就是说,在共用体变量中只能存放一个值。如果有以下程序段:

union Date
{int i;char ch;float f;
}a;
a.i = 97;

表示将整数 97 存放在共用体变量中,可以用以下的输出语句:

printf("%d", a.i);       (输出整数97)
printf("%c”, a.ch);      (输出字符'a')
printf("%f",a.f);       (输出实数0.000000)

其执行情况是:由于 97 是赋给 a.i 的,因此按整数形式存储在变量单元中,最后一个字节是“ 01100001 ”。如果用 “%d" 格式符输出 a.i,就会输出整数 97。如果想用 “%c” 格式符输出 a.ch,系统会把存储单元中的信息按字符输出 'a'。如果想用 “ %f ” 格式符输出 a.f,系统会将存储单元中的信息按浮点数形式来处理,其数值部分为0,故输出0.000000。

(2)可以对共用体变量初始化,但初始化表中只能有一个常量。

下面用法不对:

union Data
{int i;char ch;float f;
}a = { 1,'a',1.5 };				//不能初始化3个成员,它们占用同一段存储单元
union Data a = { 16 };			//正确,对第1个成员初始化
union Data a = { .ch = 'j ' };	//C99允许对指定的一个成员初始化

(3)共用体变量中起作用的成员是最后一次被赋值的成员,在对共用体变量中的一个成员赋值后,原有变量存储单元中的值就取代。

如果执行以下赋值语句:

a.ch = 'a';
a.f = 1.5;
a.i = 40;

在完成以上 3 个赋值运算以后,变量存储单元存放的是最后存入的 40,原来的 'a' 和 1.5 都被覆盖了。此时如用 “printf("%d", a.i); ” 输出 a.i 的值是 40。而用 “ printf("%e", a.ch); ”,输出的不是字符 'a',而是字符 ' ( ' 。因为在共用的存储单元中,按整数形式存放了 40,现在要按 %c 格式输出 a.ch,系统就到共用的存储单元去读数据,将存储单元中的内容按存储字符数据的规则解释,40 是字符 ' ( ' 的 ASCII 码,因此输出字符 ' ( ' 。

因此在引用共用体变量时应十分注意当前存放在共用体变量中的究竟是哪个成员的值。

(4)共用体变量的地址和它的各成员的地址都是同一地址。

例如&a.i,&a.ch,&a.f 都是同一值,其原因是显然的。

(5)不能对共用体变量名赋值,也不能企图引用变量名来得到一个值。

例如,下面这些都是不对的:

a = 1;        //不能对共用体变量赋值
m = a;        //企图引用共用体变量名以得到一个值赋给整型变量m

C99 允许同类型的共用体变量互相赋值。如:

b = a;            //a和b是同类型的共用体变量,合法

(6)以前的 C 规定不能把共用体变量作为函数参数,但可以使用指向共用体变量的指针作函数参数。C99允许用共用体变量作为函数参数。

(7)共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。

往往在数据处理中,有时需要对同一段空间安排不同的用途,这时用共用体类型比较方便,能增加程序处理的灵活性。

3.2举例说明

举例:有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。要求用同一个表格来处理。

解题思路:

可以看出:学生和教师的数据的项目大多数是相同的,但有一项不同。现要求把它们放在同一表格中。如果 job 项为 s (学生),则第 5 项为 class(班)。即 Li 是 101 班的。如果 job 项是 t (教师),则第 5 项为 position (职务)。Wang 是 prof (教授)。显然对第 5 项可以用共用体来处理(将 class 和  position 放在同一段存储单元中)。

先输入人员的数据,然后再输出。可以写出算法。按此写出程序,为简化起见,只设两个人(一个学生、一个教师)。

#include<stdio.h>
union Categorys			//声明共用体
{int classes;char position[10];
};struct Stu_Tea			//声明结构体
{int num;char name[10];char sex;char job;union Categorys category;
};struct Stu_Tea person[2];	//定义结构体数组int main()
{for (int i = 0; i < 2; i++){printf("请输入两个人的信息:\n");scanf_s("%d %s %c %c", &person[i].num, &person[i].name, 10, &person[i].sex, 1, &person[i].job, 1);if (person[i].job == 's')		//如果是学生{scanf_s("%d", &person[i].category.classes);}else if (person[i].job == 't')	//如果是老师{scanf_s("%s", &person[i].category.position, 10);}else							//如果都不是{printf("输入错误\n");}}printf("\nNo.   name        sex  job  class/position\n");for (int i = 0; i < 2; i++){if (person[i].job == 's')		//若是学生{printf("%-6d %-10s %-4c %-4c %-10d\n", person[i].num, person[i].name, person[i].sex, person[i].job, person[i].category.classes);}else							//若是教师{printf("%-6d %-10s %-4c %-4c %-10s\n", person[i].num, person[i].name, person[i].sex, person[i].job, person[i].category.position);}}return 0;
}

运行结果:

程序分析:

在程序运行过程中需要输入数据,在输入前 4 项数据(编号﹑姓名、性别、职业)时,对于学生和教师来说,输入的数据类型是一样的,但在输入第 5 项数据(人员类别)时二者就有区别了,对于学生应输人班级号(整数),对于教师则应输入职位(字符串),程序应作分别处理。

在程序中是这样处理的:先输人前 4 项数据,然后用 if 语句检查刚才输入的职业( job 成员),如果是 's',表示是学生,则第 5 项应输入一个班级号(整数),用输入格式符 %d 把一个整数送到共用体数组元素中的成员 category.class中。如果职业是 't',表示是教师,则输入第 5 项时应该用输入格式符 %s 把一个字符串(职位)送到共用体数组元素中的成员 category.position 中。请注意:这样处理后,结构体数组元素 person[0] 中的共用体成员 category 的存储空间中,存放的是整数,而 person[1] 中的共用体成员 category 的存储空间中,存放的是字符串。

在输出数据时的处理方法是类似的,如果是学生,第 5 项以整数形式输出班号,如果是教师,则第 5 项以字符串形式输出职位。在 printf 语句中,格式符 “ %-6d " 表示以十进制整数形式输出,占 6 列,数据向左对齐,其他如 %-10s,%-4c,%-4c,%-10s 的含义与此类似。

在数据处理中,用同一个栏目来表示不同内容的情况是不少的。这个例子是比较简单的,但通过此例可以看到,如果善于利用共用体,会使程序的功能更加丰富和灵活。


http://www.ppmy.cn/news/34579.html

相关文章

I2C和SPI总线以及通信

通讯属性 概括 Serial/parallel 串行/并行Synchronous/asynchronous 同步/异步Point-to-point / bus 点对点 总线Half-duplex/full-duplex 半双工/全双工Master-slave/ equal partners 主从/对等single-ending / differential 单端/差分 点对点和总线 点对点通讯 只有两个通…

linux简单入门

目录Linux简介Linux目录结构Linux文件命令文件处理命令文件查看命令常用文件查看命令Linux的用户和组介绍Linux权限管理Linux简介 Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹&#xff0…

js 往指定位置添加对象

var arr [1,2,2,3,4,5] arr.splice(2,0,4) // 第一个参数表示index&#xff08;索引&#xff09; 第二个参数要移除的个数&#xff0c; 第三个参数表示要添加的元素 // 返回的结果为空&#xff0c; 改变原来的数组 // 最终 arr [1,2,4,2,3,4,5]

网络安全工程师做什么?

​ 网络安全很复杂。数字化转型、远程工作和不断变化的威胁形势需要不同的工具和不同的技能组合。 系统必须到位以保护端点、身份和无边界网络边界。负责处理这种复杂安全基础设施的工作角色是网络安全工程师。 简而言之&#xff0c;网络安全工程师是负责设计和实施组织安全系…

ICG-PEG-Folate,吲哚菁绿-聚乙二醇-叶酸;科研用试剂的用途和作用

ICG-PEG-Folate,吲哚菁绿-聚乙二醇-叶酸 中文名称&#xff1a;吲哚菁绿-聚乙二醇-叶酸 英文名称&#xff1a;ICG-PEG-FA 性状&#xff1a;粉末或固体 溶剂&#xff1a;溶于DMSO、DMF等常常规溶剂 稳定性&#xff1a;-20℃&#xff0c;干燥避光 分子量&#xff1a;1000、2…

Rust实战(3):使用AI对Rust四则运算代码提问

Rust实战3:使用AI对Rust四则运算代码提问 提问:请生成rust四则运算解释器代码提问:请解释这个rust代码 "io::stdout().flush().unwrap();" 里 `unwrap` 的作用,请使用中文回复提问:请实现下rust的 Result<T, E> 类,并给测试用例提问:请解释rust里mut关键…

20230323英语学习

Why Can You “Hear the Ocean” in Seashells? 为啥能在贝壳里“听见海的声音”&#xff1f; We’re told a number of stories as kids. One of the more harmless of these little lies is the one about seashells.You know the one: hold up a seashell to your ear, an…

c++ 一些常识 2

前言 今天主要讲类相关概念。 构造和析构函数是否可以抛出异常 在构造函数中抛出异常&#xff0c;控制权会转出构造函数之外&#xff0c;对象的析构函数不会被调用&#xff0c;造成内存泄漏。 如果析构函数中抛出异常&#xff0c;而且没有在当地捕捉&#xff0c;析构函数便执…