【编程语言】C语言结构体、共用体和枚举

在C语言中,数据类型可分成基本数据类型、构造数据类型、指针数据类型、空类型四大类。本文主要介绍构造数据类型——结构体、共用体。除了这两个之外,还会介绍到枚举。


结构体概述

结构体是一系列具有相同类型或不同类型的数据构成的数据集合,简称结构。在C语言中,可以定义结构体类型,将多个相关的变量包装成一个整体来使用。在结构体中的变量,可以是相同、部分相同或完全不同的数据类型。

结构体类型的定义

结构体类型定义的一般形式如下:

struct 结构体名
{
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
    ...
};

其中,struct是关键字,是结构体类型的标志。

关于结构体定义的注意点:

  • 结构体的成员名可以与程序中其他定义为基本类型的变量名重名,不会出现冲突;
  • 结构体定义最后是以分号结束的,不要遗漏;
  • 如果结构体类型定义在函数内部,则这个类型名的作用域仅为该函数;如果是定义在所有函数的外部,则可在整个程序中使用。

结构体变量的定义

结构体变量定义一共有三种方式,下面将逐个介绍。

  • 先声明结构体类型,再定义结构体变量。比如:
struct 结构体名 结构体变量名列表;

  • 声明结构体类型的同时定义结构体变量。比如:
struct 结构体名
{
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
    ...
}结构体变量名列表;

  • 直接定义结构体变量,即省略结构体名。比如:
struct
{
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
    ...
}结构体变量名列表;

在定义了结构体变量之后,系统将为其分配内存单元。每个结构体变量所占用内存大小为各个成员项所占用的内存大小之和。

结构体类型和结构体变量的区别:

  • 结构体类型是一种数据类型,系统不为其分配存储空间;
  • 结构体变量是实在的变量,系统为其分配存储空间,可以进行赋值、存取或运算。

结构体变量的初始化

在定义结构体变量的同时就对其成员赋初值的操作,就是对结构体变量的初始化。结构体变量的初始化方式与数组类似。在定义结构体变量的同时,把赋给每个成员的初始化用大括号括起来,各个数据之间以逗号隔开。具体形式如下:

struct 结构体名 结构体变量1={成员1初始化值,成员2初始化值...} , 结构体变量2={成员1初始化值,成员2初始化值...} ...;

或者

struct 结构体名
{
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
    ...
}结构体变量1={成员1初始化值,成员2初始化值...} , 结构体变量2={成员1初始化值,成员2初始化值...} ...;

结构体变量的引用

结构体变量的引用分为结构体成员变量的引用和对结构体变量本身的引用。

  • 结构体成员变量的引用

结构体变量包括一个或多个结构体成员变量,引用其成员变量的语法格式如下:

结构体变量名 . 成员名

其中,“.”是专门的结构体成员运算符,用于连接结构体变量名和成员名,属于最高级运算符。

  • 对结构体变量本身的引用

C语言规定,同类型的结构体类型之间可以进行赋值运算,因此这样的赋值运算是允许的:

struct student stu1, stu2;
stu2 = stu1;

但是,在C语言中规定,不允许将一个结构体变量作为整体进行输入或输出操作。因此下面的操作时错误的:

scanf_s("%s,%d,%d" , &stu1);
printf("%s,%d,%d" , stu1);

将结构体变量作为操作对象时,还可以进行一下两种运算:

  • 用sizeof运算符计算结构体变量所占用的内存空间。其一般使用形式如下:
sizeof(结构体变量名);
sizeof(结构体类型名);

  • 用&运算符对结构体变量进行取址运算。


结构体指针

在C语言中可以定义结构体变量,也可以定义结构体类型的指针。当指针变量指向一个结构体变量时,就将这个指针变量称为结构体指针。此时,结构体指针变量的值就是它指向的结构体变量的首地址。

指向结构体变量的指针

和普通的指针变量定义类似,结构体指针的定义也使用*作为指针定义的运算符。其一般定义形式为:

struct 结构体类型名 *结构体指针变量名;

此时通过结构体指针变量来访问成员变量的一般形式如下:

结构体指针变量名 -> 成员名

C语言规定,结构体指针变量名通过“->”访问成员名,在内存中都会被转化为(*结构体指针变量名).成员名的方式访问。也就是说,以下三种对成员变量的访问效果是一致的:

结构体变量名 . 成员名
结构体指针变量名 -> 成员名
( *结构体指针变量名) . 成员名

指向结构体数组的指针

结构体指针也可以指向一个已经定义的结构体数组。结构体指针可以指向结构体数组的首地址,也可以指向结构体数组的某个元素的地址。可以通过结构体指针值的递增和递减改变其所指的元素。

指向结构体数组的结构体指针变量使用起来并不复杂,但是要注意区分以下情况:

假设p是指向一个结构体数组的指针变量,num为该结构体的某个成员,那么

p->num++            /* 等价于(p->num)++,先取成员值,对成员值++ */
++p->num            /* 等价于++(p->num),先对成员值++,再取成员值 */
(p++)->num            /* 先取成员值,再指针++ */
(++p)->num            /* 先指针++,再取成员值 */

应该注意的是,一个结构体指针变量虽然可以用来访问结构体变量或者结构体数组元素的成员,但是不能使它指向一个成员。也就是说,不允许取一个成员的地址来赋予它。比如:

p = &stu[0].sex;            /* 错误 */
p = stu;            /* 正确,赋予结构体指针变量数组的名称 */
p = &stu[0];            /* 正确,赋予结构体指针变量数组首元素的地址 */

例子:

#include <stdio.h>

int main()
{
	struct ucode
	{
		char u1;
		int u2;
	}tt[4] = { {'a',97},{'b',98},{'c',99},{'d',100} };

		struct ucode *p = tt;

		printf("%c %d\n", p->u1, p->u2);
		printf("%c\n", (p++)->u1);
		printf("%c %d\n", p->u1, p->u2++);
		printf("%d\n", p->u2);
		printf("%c %d\n", (++p)->u1, p->u2);
		p++;
		printf("%c %d\n", ++p->u1, p->u2);

	return 0;
}

这段程序的运行结果为:

a 97
a
b 98
99
c 99
e 100
请按任意键继续. . .

结构体与函数

作为函数参数

在函数之间直接传递结构体类型的数据属于传值调用方式;在函数之间直接传递结构体指针类型的数据属于传址调用方式。

作为函数返回值

C语言中,一个函数只能有一个返回值。但是如果函数确实需要带回多个返回值,可以利用全局变量解决,或者使用指针作为函数的参数解决。在学习了结构体之后,可以在被调函数中返回一个结构体类型的数据,而多个返回值都存放在这个结构体类型中。这样也是非常方便的。


共用体

共用体的概念

C语言除了提供结构体这种可包含多种类型数据的构造类型,还提供了一种从形式上看和结构体非常相似的构造类型——共用体。

在C语言中,可以定义不同数据类型的数据共用同一段内存空间,以满足某些特殊的数据处理要求,这种数据构造类型就是共用体。共用体又称联合体,和结构体类型一样,它也是由各种不同类型的数据组成的,这些数据称为共用体的成员。成员既可以拥有相同的数据类型,也可以拥有不同的数据类型。

不同的是,在共用体中,C语言编译系统使用了覆盖技术,使共用体的所有成员在内存中具有相同的首地址,共占用一段内存空间,这些数据可以相互覆盖,在不同的时间保存不同的数据类型和不同长度的成员的值。也就是说,在某一时刻,只有最新存储的数据是有效的。运用此种类型数据的优点是节省存储空间。

共用体的定义

共用体类型定义的一般形式如下:

union 结构体名
{
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
    ...
};

其中:union是C语言中的关键字,表明是在进行一个共用体类型的定义。

共用体变量的定义、初始化和引用

共用体变量的定义,和结构体变量的定义类似,定义共用体变量的方法也有三种:

union 共用体名
{
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
    ...
}共用体 变量名列表;
union 共用体名 共用体变量名列表;
nuion 共用体名
{
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
    ...
}共用体变量名列表;
union
{
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
    ...
}共用体变量名列表;

当一个共用体变量被定义后,编译程序会自动给变量分配存储空间,其长度为共用体数据成员中所占内存空间最大的成员的长度。

共用体变量的初始化,和结构体变量的初始化截然不同。由于共用体的成员共占用一个首地址,所以在任何时刻只能存放其中一个成员的值。所以,在对共用体变量进行定义并初始化时,只能对其中的第一个成员赋初值,即给其赋予一个和第一个成员类型相同的常量,初值需要用大括号括起来。

也就是说,共用体变量初始化的时候只能给第一个成员赋初值,但是其他时候可以通过对共用体变量成员的引用来进行赋值。同时,不能直接引用共用体变量,只能引用变量中的成员,除非将一个共用体变量赋值给另一个同类型的共用体变量。比如:

union mark
{
    int score;
    char degree[4];
}liming={90};                /* 正确 */
liming=100;                /* 错误 */
liming.score=100;                /* 正确 */

共用体指针变量

和普通的指针变量定义类似,共用体指针的定义也使用*作为指针定义的运算符。其一般定义形式为:

union 共用体类型名 *共用体指针变量名;

此时通过共用体指针变量来访问成员变量的一般形式如下:

共用体指针变量名 -> 成员名

C语言规定,共用体指针变量名通过“->”访问成员名,在内存中都会被转化为(*共用体指针变量名).成员名的方式访问。也就是说,以下三种对成员变量的访问效果是一致的:

共用体变量名 . 成员名
共用体指针变量名 -> 成员名
( *共用体指针变量名) . 成员名


结构体和共用体的区别

  • struct和union都是由多个不同的数据类型成员组成,但在任何同一时刻,union中只存放了一个被选中的成员,而struct的所有成员都存在;
  • 在struct中,各成员都占有自己的内存空间,它们是同时存在的,一个struct变量的总长度等于所有成员长度之和。在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度;
  • 对于union的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于struct的不同成员赋值是互不影响的。


枚举

在实际问题中,有些变量的取值被限定在一个有限的范围内。为此,C语言提供了一种称为“枚举”的类型,在枚举类型的定义中列举出所有的可能的取值,被说明为该枚举类型的变量取值不能超过定义的范围。

枚举类型定义的一般形式为:

enum 枚举名
{
    枚举值[=整型常量],
    枚举值[=整型常量],
    ...
    枚举值[=整型常量],
};

如果枚举枚举值没有初始化,也就是声明中[=整型常量]没有定义,则从第一个枚举值开始,顺序赋给标识符0,1,2,3,4……。但是当枚举中的某个成员赋值后,其后面的成员按顺序依次加1。

枚举类型声明的时候,有几点需要注意:

  • 枚举成员之间用逗号分隔,不是分号;
  • 枚举成员初始化时可以赋予负数,但是依次加1的规律不变。

枚举变量定义的方式和结构体、共用体类似:

enum 枚举名 枚举变量名列表;
enum 枚举名
{
    枚举值[=整型常量],
    枚举值[=整型常量],
    ...
    枚举值[=整型常量],
}枚举变量名列表;

枚举变量使用的时候,有几点需要注意:

  • 枚举值是常量,不是变量,不能在程序中用赋值语句再对它进行赋值;
  • 只能把枚举值赋予给枚举变量,不能把元素的数值直接赋予枚举变量。比如:
enum weekday { sun,mou,tue,wed,thu,fri,sat };    /* 定义枚举类型 */
enum weekday a,b,c;            /* 定义枚举变量 */
a = sun;                /* 正确 */
b = 1;                /* 错误,不能把元素的值直接赋予枚举变量 */
c = (enum weekday)2;            /* 正确,将顺序号为2的强制转换并赋值 */

猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/80253595