第一项
C 共用体
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。
可以定义一个带有多成员的共用体,任何时候只能有一个成员带有值。
共用体提供了一种使用相同的内存位置的有效方式。
定义共用体
必须使用 union 语句,方式与定义结构类似。
union 语句定义了一个新的数据类型,带有多个成员。
union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];
union tag 是可选的,每个 member definition 是标准的变量定义,比如 int i; 或者 float f; 或者其他有效的变量定义。在共用体定义的末尾,最后一个分号之前,可以指定一个或多个共用体变量,这是可选的。
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
printf( "Memory size occupied by data : %d\n", sizeof(data));
return 0;
} /定义一个名为 Data 的共用体类型,有三个成员 i、f 和 str/
Data 类型的变量可以存储一个整数、一个浮点数,或者一个字符串。
一个变量(相同的内存位置)可以存储多个多种类型的数据。
可以根据需要在一个共用体内使用任何内置的或者用户自定义的数据类型。
共用体占用的内存应足够存储共用体中最大的成员。
eg:
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
printf( "Memory size occupied by data : %d\n", sizeof(data));
return 0;
}
Memory size occupied by data : 20
访问共用体成员
,使用成员访问运算符(.)访问共用体的成员。
其是共用体变量名称和我们要访问的共用体成员之间的一个句号。
使用 union 关键字来定义共用体类型的变量。
eg:
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 1;
data.f = 2;
strcpy( data.str, "C Programming");
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
return 0;
}
data.i : 1917853763
data.f : 4122360580327794900000000000000.000000
data.str : C Programming
可以看到共用体的 i 和 f 成员的值有损坏,最后赋给变量的值占用了内存位置,这是 str 成员能够完好输出的原因。
同一时间只使用一个变量
eg:
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
printf( "data.i : %d\n", data.i);
data.f = 220.5;
printf( "data.f : %f\n", data.f);
strcpy( data.str, "C Programming");
printf( "data.str : %s\n", data.str);
return 0;
}
data.i : 1
data.f : 2.000000
data.str : C Programming
所有的成员都能完好输出
第二项
C 位域
C 语言的位域(bit-field)是一种特殊结构体成员,允许我们按位对成员进行定义,指定其占用的位数。
结构包含多个开关的变量,变量值为 TRUE/FALSE
struct { unsigned int widthValidated; unsigned int heightValidated; } status;
此结构需要 8 字节的内存空间。
在结构内使用这样的变量,可定义变量的宽度来告诉编译器,将只使用这些字节。
struct { unsigned int widthValidated : 1; unsigned int heightValidated : 1; } status;
,status 变量将占用 4 字节的内存空间,只有 2 位被用来存储值。用了 32 个变量,每一个变量宽度为 1 位, status 结构将使用 4 个字节,只要您再多用一个变量,如果使用了 33 个变量,那么它将分配内存的下一段来存储第 33 个变量,这个时候就开始使用 8 个字节。
eg:
#include <stdio.h>
#include <string.h>
/* 定义简单的结构 */
struct
{
unsigned int widthValidated;
unsigned int heightValidated;
} status1;
/* 定义位域结构 */
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status2;
int main( )
{
printf( "Memory size occupied by status1 : %d\n", sizeof(status1));
printf( "Memory size occupied by status2 : %d\n", sizeof(status2));
return 0;
}
Memory size occupied by status1 : 8
Memory size occupied by status2 : 4
位域的特点和使用方法如下:
- 定义位域时,可以指定成员的位域宽度,即成员所占用的位数。
- 位域的宽度不能超过其数据类型的大小,位域必须适应所使用的整数类型。
- 位域的数据类型可以是
int
、unsigned int
、signed int
等整数类型,也可以是枚举类型。 - 位域可以单独使用,可以与其他成员一起组成结构体。
- 位域的访问是通过点运算符(
.
)来实现的,与普通的结构体成员访问方式相同。
位域声明
"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
- 用 1 位二进位存放一个开关量时,只有 0 和 1 两种状态。
- 读取外部文件格式——可以读取非标准的文件格式。
位域的定义和位域变量的说明
struct 位域结构名 { 位域列表 ‘‘type [member_name] : width ;’’ };
变量
元素 | 描述 |
---|---|
type | 只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。 |
member_name | 位域的名称。 |
width | 位域中位的数量。宽度必须小于或等于指定类型的位宽度 |
详解 | struct bs{相邻位域字段的类型相同,且其位宽之和小于类型的 sizeo f大小,后面的字段将紧邻前一个字段存储,直到不能容纳为止。 |
带有预定义宽度的变量被称为位域。
位域可以存储多于 1 位的数。
struct
{
unsigned int age : 3;
} Age; /定义一个宽度为3的位域/
age 变量将只使用 3 位来存储这个值,如果使用超过 3 位,则无法完成。
int a:8;
int b:2;
int c:6;
}data; /定义 struct bs 的结构体/
data 为 bs 的结构体变量,共占四个字节:
struct packed_struct {
unsigned int f1:1;
unsigned int f2:1;
unsigned int f3:1;
unsigned int f4:1;
unsigned int type:4;
unsigned int my_int:9;
} pack;
packed_struct 的结构体,包含了六个成员变量,pack 为 packed_struct 的结构体变量。
packed_struct 包含了 6 个成员:四个 1 位的标识符 f1、2、3、4,一个 4 位的 type 和一个 9 位的 my_int。
eg- 1
#include <stdio.h>
struct packed_struct {
unsigned int f1 : 1; // 1位的位域
unsigned int f2 : 1; // 1位的位域
unsigned int f3 : 1; // 1位的位域
unsigned int f4 : 1; // 1位的位域
unsigned int type : 4; // 4位的位域
unsigned int my_int : 9; // 9位的位域
};
int main() {
struct packed_struct pack;
pack.f1 = 1;
pack.f2 = 0;
pack.f3 = 1;
pack.f4 = 0;
pack.type = 7;
pack.my_int = 255;
printf("f1: %u\n", pack.f1);
printf("f2: %u\n", pack.f2);
printf("f3: %u\n", pack.f3);
printf("f4: %u\n", pack.f4);
printf("type: %u\n", pack.type);
printf("my_int: %u\n", pack.my_int);
return 0;
} /定义 packed_struct 的结构体/
main 函数中,创建了一个 packed_struct 类型的结构体变量 pack,分别给每个位域成员赋值。
使用 printf 语句打印出每个位域成员的值。
f1: 1 f2: 0 f3: 1 f4: 0 type: 7 my_int: 255
eg- 2
#include <stdio.h>
#include <string.h>
struct
{
unsigned int age : 3;
} Age;
int main( )
{
Age.age = 4;
printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
printf( "Age.age : %d\n", Age.age );
Age.age = 7;
printf( "Age.age : %d\n", Age.age );
Age.age = 8; // 二进制表示为 1000 有四位,超出
printf( "Age.age : %d\n", Age.age );
return 0;
}
Sizeof( Age ) : 4 Age.age : 4 Age.age : 7 Age.age : 0
计算字节数:
eg-3
#include <stdio.h>
struct example1 {
int a : 4;
int b : 5;
int c : 7;
};
int main() {
struct example1 ex1;
printf("Size of example1: %lu bytes\n", sizeof(ex1));
return 0;
}
example1 结构体包含三个位域成员 a,b 和 c,它们分别占用 4 位、5 位和 7 位。
sizeof 运算符计算出 example1 结构体的字节数
Size of example1: 4 bytes
对于位域的定义尚有以下几点说明:
-
一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。
struct bs{
unsigned a:4;
unsigned :4; /* 空域 */
unsigned b:4; /* 从下一单元开始存放 */
unsigned c:4
}
-
此位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。
-
位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的
struct k{
int a:1;
int :2; /* 该 2 位不能使用 */
int b:3;
int c:2;
};
位域在本质上就是一种结构类型,其成员是按二进位分配的。
位域的使用
位域的使用和结构成员的使用相同
位域变量名.位域名 位域变量名->位域名
位域允许用各种格式输出。
eg:
#include <stdio.h>
int main(){
struct bs{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
bit.b=7; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
bit.c=15; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
printf("%d,%d,%d\n",bit.a,bit.b,bit.c); /* 以整型量格式输出三个域的内容 */
pbit=&bit; /* 把位域变量 bit 的地址送给指针变量 pbit */
pbit->a=0; /* 用指针方式给位域 a 重新赋值,赋为 0 */
pbit->b&=3; /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
pbit->c|=1; /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c); /* 用指针方式输出了这三个域的值 */
}
定义了位域结构 bs,三个位域为 a、b、c。说明 bs 类型的变量 bit 和指向 bs 类型的指针变量 pbit。表示位域也是可以使用指针的。
第三项
C typedef
typedef 关键字,可使用它来为类型命名。
typedef unsigned char BYTE; /为单字节数字定义了一个术语 BYTE/
标识符 BYTE 可作为类型 unsigned char 的缩写
BYTE b1, b2;
或
typedef unsigned char byte;
可以使用 typedef 来为用户自定义的数据类型命名。可以对结构体使用 typedef 来定义一个新的数据类型名字,然后使用这个新的数据类型来直接定义结构变量。
eg:
#include <stdio.h>
#include <string.h>
typedef struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} Book;
int main( )
{
Book book;
strcpy( book.title, "A");
strcpy( book.author, "B");
strcpy( book.subject, "C");
book.book_id =0
printf( "书标题 : %s\n", book.title);
printf( "书作者 : %s\n", book.author);
printf( "书类目 : %s\n", book.subject);
printf( "书 ID : %d\n", book.book_id);
return 0;
}
书标题 :A 书作者 : B 书类目 : C 书 ID : 0
typedef 对比#define
- typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
- typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。
eg:
#include <stdio.h>
#define TRUE 1
#define FALSE 0
int main( )
{
printf( "TRUE 的值: %d\n", TRUE);
printf( "FALSE 的值: %d\n", FALSE);
return 0;
}
TRUE 的值: 1 FALSE 的值: 0