C语言 共用体和typedef
目录
共用体概述
声明共用体
声明共用体变量
调用共同体变量的成员
->操作符
为什么使用typedef
typedef使用格式
举 例
共用体概述
有时需要一种数据结构,不同的场合表示不同的数据类型。比如, 如果只用一种数据结构表示学生的“成绩”,这种结构就需要有时是 整数(80、90),有时是字符('A'、'B'),又有时是浮点数(80.5、60.5)。
C 语言提供了共用体类型(Union 结构),用来自定义可以灵活变更的 数据结构。它内部可以包含各种属性,但同一时间只能有一个属 性,因为所有属性都保存在同一个内存地址,后面写入的属性会覆 盖前面的属性。这样做的最大好处是 节省内存空间 。
“共用体”与“结构体”的定义形式相似,但它们的含义是不同的。
结构体变量所占内存长度是各成员占的内存长度之和;每个成员 分别占有其自己的内存单元。
共用体变量所占的内存长度等于最长的成员的长度;几个成员共 用一个内存区。
声明共用体
格式:
union 共用体类型名称{
数据类型 成员名1;
数据类型 成员名2;
…
数据类型 成员名n;
};
举例:
union Data {
short m;
float x;
char c;
};
上例中, union 命令定义了一个包含三个属性的数据类型 Data。虽 然包含三个属性,但是同一时间只能取到一个属性。最后赋值的属 性,就是可以取到值的那个属性。
声明共用体变量
方式1:先定义共用体类型,再定义共用体变量
union Data {
short m;
float x;
char c;
};
int main()
{
union Data data1;
}
方式2:定义共用体类型的同时定义共用体变量
union Data1
{
short a;
float b;
char c;
}data2,data3;
以共用体变量a为例,它由3个成员组成,分别是m、x和c,编译 时,系统会按照最长的成员为它分配内存,由于成员x的长度最长, 它占4个字节,所以共用体变量a的内存空间也为4个字节。
union
{
short m;
float x;
char c;
} a, b;
int main()
{
printf("%d\n",sizeof(a)); //只取最长的float,4个字节
return 0;
}
调用共同体变量的成员
#include <stdio.h>
union Data
{
short a;
float b;
char c;
};
int main()
{
//方式1
data.a = 12;
//方式2
union Data data4 = {.a = 12};
//方式3
union Data data5 = {12};//可以理解为给首元素赋值(默认)
//错误的方式
union Data data6 = {.a = 12,.b = 12.3F,.c = 'A'};
printf("%c\n",data6.c);//输出的是A
printf("%f\n",data6.b);//输出是0.000000
}
->操作符
#include <stdio.h>
union Data
{
short a;
float b;
char c;
}
int main()
{
union Data data1;
data1.a = 12;
union Data *data_ptr;
data_ptr = &data1;
// ->符号的使用:针对的是共用体指针变量的使用
printf("%d \n",data1.a);
printf("%d \n",(*data_ptr).a);
printf("%d \n",data_ptr->a);
}
补充说明:
不能对共用体变量名赋值,也不能企图引用变量名来得到一个 值。只能引用共用体变量中的成员。
C99允许同类型的共用体变量互相赋值。
printf("%d",a); //错误的
printf("%d",a.i); //正确的
C99允许用共用体变量作为函数参数。
b = a; //a和b是同类型的共用体变量,合法
共用体类型可以出现在结构体类型定义中,也可以定义共用体数 组。反之,结构体也可以出现在共用体类型定义中,数组也可以 作为共用体的成员。
题目:现有一张关于学生信息和教师信息的表格。 学生信息包括姓名、编号、性别、职业、分数; 教师的信息包括姓名、编号、性别、职业、教学科目。 请看下面的表格,请使用共用体编程完成。
#include <stdio.j>
#include <stdlib.h>
sttuct Person
{
char name[20];
int id;
char gender;
char profession;
union
{
float score;
char course;
}sc;
};
int main()
{
int i;
struct Person person[n];
for(i=0;i<n;i++)
{
printf("%s %d %s %s \n",person[i].name,&person[i].id,person[i].gender);
if (persons[i].profession == 's')
{
//如果是学生
printf("请输入学生成绩:");
scanf("%f", &persons[i].sc.score);
}
else
{
//如果是老师
printf("请输入老师课程:");
scanf("%s", persons[i].sc.course);
}
}
为什么使用typedef
C语言允许为一个数据类型起一个新的别名,就像给人起“绰号”一 样。
起别名的目的不是为了提高程序运行效率,而是为了 编码方便 。例 如,有一个结构体的名字是 student,定义一个结构体变量stu1,代 码如下:
struct student stu1;
typedef使用格式
用typedef声明数组类型、指针类型,结构体类型、共用体类型等, 使得编程更加方便。
1、为某个基本类型起别名
//1.场景1:给基本数据类型起别名
typedef int Integer;
typedef unsigned char Byte;
//测试1:
int i = 10;
Integer j =10;
unsigned char c1 = 10;
Byte c2 = 10;
2、为结构体、共用体起别名
//2.场景2:给结构体、共用体起别名
//写法1
struct student
{
char nane[20];
int age;
};
typedef struct student MyStudent;//或者放上面做全局变量
//写法2
typedef struct //匿名结构体
{
char name[20];
int age;
}MyDog;
typedef union Date
{
char c;
float f;
short s;
}MyDate;
//测试2
typedef struct student MyStudent;
struct student s1;
MyStudent s2;
//struct Dog dog1;
MyDog dog2;
union Date data1;
MyDate data2;
3、为指针起别名
//3.场景3:给指针类型起别名
typedef int * INT_PTR;//别名是INT_PTR对应是int类型的指针
typedef char * String;
//测试3
int num = 10;
int * int_ptr1;
int_ptr1 = #
printf("&d \n",*int_ptr1);
INT_PTR int_ptr2;
int_ptr2 = #
printf("&d \n",*int_ptr2);
char * str1 = "hello";
String str2 = "hello";
4、为数组类型起别名
//4.场景4:给数组类型起别名
typedef int five_ints[5];//别名就是five_ints对应的是长度为5的int类型数组
typedef int * PTR_INTS[2];//PTR_INTS是int * [2]的别名
//测试4
int arr[5] = {1,2,3,4,5};
five_ints arr2 = {1,2,3,4,5};
//测试指针数组
int a = 10,b = 20;
int * arr3[2] = {&a,&b};
PTR_INTS arr4 = {&a,&b};
5.为函数类型起别名
//5.场景5:给函数类型起别名
typedef int (*PTR_FUNC)(int,int);// 别名就是PTR_FUNC对应的是两个形参是int而且返回值也是int的类型的函数指针(可以理解成给指针起了别名)
int max(int a,int b)
{
int c = (a > b)? a : b;
return c;
}
int min(int a,int b)
{
int c = (a < b)? a : b;
return c;
}
//测试5
//复习:函数指针的使用
int (*ptr_compare)(int,int);
ptr_compare = &max;
//调用函数指针对应的函数
int result = (*ptr_compare)(10,15);//先取结果=max,再赋俩值,再返回int值
printf("%d ",result);
//使用别名
PTR_FUNC ptr_func = &min;
int result1 = (*ptr_func)(12,24);
printf("%d",result1);
举 例
#include <stdio.h>
//函数
int add(int m,int n)
{
int sum = m + n;
return sum;
}
//数组
char str[3][30] ={"烧肉","123","12581"};
//使用typedef
typedef int (*PTR_FUNC)(int,int);//现在是一个类型
typedef char (*PTR_ARR)[30];
int main()
{
//调用函数 : 使用函数指针
int (*ptr_add)(int,int);
ptr_add = &add;
int sum = (*ptr_add)(10,20);
printf("sum = %d\n",sum);
//遍历数组:使用数组指针
//char *ptr_arr[30];//定义了一个指针数组,数组长度是30,每个元素是指针
char (*ptr_arr)[30];//定义了一个数组指针,这个指针指向了长度30的char类型数组
ptr_arr = str;//将str[0]的首地址赋给ptr_arr指针
for(int i = 0;i <3;i++)
{
printf("str[%d]: %s \n",i,*(ptr_arr+i));
}
//##############使用typedef声明的类型进行调用##############
//调用函数
PTR_FUNC ptr_add1 = &add;
int sum1 = (*ptr_add1)(100,200);
printf("sum1 = %d\n",sum1);
//遍历数组
PTR_ARR ptr_arr1 = str;
for(int i = 0;i <3;i++)
{
printf("str[%d]: %s \n",i,*(ptr_arr1+i));
}
return 0;
}
小 结
(1) typedef的方法实际上是为特定的类型指定了一个同义字 (synonyms)。
(2) 用typedef只是对已经存在的类型指定一个新的类型名,而没有创 造新的类型。
(3) typedef与#define是不同的。#define是在 预编译时处理 的,它只 能作简单的字符串替换,而typedef是在 编译阶段处理 的,且并非 简单的字符串替换。
(4) 当不同源文件中用到同一类型数据(尤其是像数组、指针、结构 体、共用体等类型数据)时,常用typedef 声明这些同一的数据类 型。
技巧:可以把所有的typedef名称声明单独 放在一个头文件 中,然 后在需要用到它们的文件中用#include指令把它们包含到文件中。这 样编程者就不需要在各文件中自己定义typedef名称了。
(5) 使用typedef名称有利于 程序的通用与移植 。有时程序会依赖于 硬件特性,用typedef类型就便于移植。 某一个值在不同计算机上的类型,可能是不一样的。
int i = 100000;
上面代码在32位整数的计算机没有问题,但是在16位整数的计算机 就会出错。C 语言的解决办法,就是提供了类型别名,在不同计算 机上会解释成不同类型,比如 int32_t 。
int32_t i = 100000;
上例将变量 i 声明成 int32_t 类型,保证它在不同计算机上都是32位 宽度,移植代码时就不会出错。
这一类的类型别名都是用 typedef 定义的。下面是类似的例子。
typedef long int ptrdiff_t;
typedef unsigned long int size_t;
typedef int wchar_t;
这些整数类型别名都放在头文件 stdint.h ,不同架构的计算机只需 修改这个头文件即可,而无需修改代码。