本博文为半摘记性质。
——
声明:部分知识点及例程修改自https://www.runoob.com/cprogramming/c-tutorial.html 菜鸟教程、http://c.biancheng.net/c/ C语言中文网,另有部分零散资料转自互联网,内容有一定改动,并非全文转载。
本人尊重各位的知识成果,大幅引用的文章原文网址已在各小节末尾给出。
本文要点:
结构体可以在main 函数里面定义,也可以在main函数之外定义。在main函数外定义属于全局变量,可被多个函数使用;在main函数里面定义属于局部变量,只能在main函数中使用。
定义结构体类型
定义一个结构体类型的一般形式为:
struct 结构名
{
成员列表};
其中成员列表中每个成员的形式为:
类型说明符 成员名;
//此结构体的声明包含了其他的结构体
struct COMPLEX
{
char string[100];
struct SIMPLE a;
};
//此结构体的声明包含了指向自己类型的指针
struct NODE
{
char string[100];
struct NODE *next_node;
};
定义结构体变量
结构体变量的几种定义模式如下:
struct 结构体名{
成员列表;
}变量名表;
struct {
成员列表;
}变量名表;
struct 结构体名{
成员列表;
}; //分号不可省
之后如果要定义结构体变量:
struct 结构体名 结构体变量名;
——
对结构体变量中成员的引用形式为:
结构体变量名.成员名 //成员访问运算符(.)
==关于符号 -> ==
如果我们在C语言中定义了一个结构体,然后申明一个指针指向这个结构体,那么我们要用指针取出结构体中的数据,就要用到“->”
结构指针->结构成员
例:
struct Data A = {
1,2,3}; ——声明变量专A
int x; ——声明一个变量x
p = &A ; ——让p指向A
x = p->a; ——取出p所指向的结构体中包含的数据项a赋值给x
由于此时p指向A,因而 p->a == A.a,也就是1。
C语言中->是什么意思啊?
——
结构体变量的初始化
定义时初始化
结构体变量在定义时允许初始化,在花括号内按照成员的定义顺序,分别给出变量中各成员的值。
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {
"C 语言", "RUNOOB", "编程语言", 123456};
或struct Books book = {
"C 语言", "RUNOOB", "编程语言", 123456};
结构体变量作为函数参数
可以把结构体变量作为函数参数,传参方式与其他类型的变量或指针类似。
struct Books
{
...
};
/* 函数声明 */
void printBook( struct Books book );//括号内同结构体变量名定义形式
——
/*主函数内*/
struct Books Book1; //定义结构体变量
printBook( Book1 );//引用函数
指向结构的指针
结构体名和类型名是等价的,是一种类型说明符,只有在它定义了变量后系统才分配内存空间,不是地址。
同样,结构体变量名不是地址,要加取值符号&,相当于基本数据类型的变量名。
结构体每个成员地址同理。
引用结构体变量地址的指针为结构体指针,定义形式为:
struct 结构体名 *指针变量名;
——例
struct Books
{
math;
...
}Book1;
struct Books *struct_pointer;//定义
struct_pointer = &Book1;//在上述定义的指针变量中存储结构变量的地址
使用时:
printBook( struct_pointer );
等同于printBook( &Book1 );//这里省略步骤直接取了地址
为了使用指向该结构的指针访问结构的成员,一般引用形式是:
(*指针变量名).成员名
等价于:指针变量名->成员名
——例
(*struct_pointer).math
struct_pointer->math
结构作为函数参数和结构指针作为函数参数的对比
结构体数组同数值型数组定义、初始化、用法类似。
常用结构体数组来表示具有相同数据结构的一个群体,结构体数组的每一个元素相当于一个结构体变量。
结构体数组的一般定义形式为:
struct 结构体名 结构体数组名[元素个数];
例struct Books Book[10];
对结构体数组元素成员的引用形式为:
结构体数组名[元素下标].成员名
对结构体数组的初始化形式为:
struct STU stu[5] = {
{
"小红", 22, 'F', "Z1207031"}, {
"小明", 21, 'M', "Z1207035"}, {
"小七", 23, 'F', "Z1207022"}, {
"小欣", 20, 'F', "Z1207015"}, {
"小天", 19, 'M', "Z1207024"}};
枚举与结构构造类似,放置一起讨论。
枚举(enum)是 C 语言中的一种基本数据类型。枚举在C/C++/c#中,是一个被命名的整型常数的集合,第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。
其定义形式为:
enum 枚举名 {
枚举成员1,枚举成员2,……};
例:enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
枚举成员是该枚举类型的命名常数,枚举中每个成员(标识符)结束符是","(逗号) 不是";"(冒号), 最后一个成员可省略","。
可以在定义枚举类型时改变枚举元素的值,而后面的值为前一元素加 1:
enum season {
spring, summer=3, autumn, winter};
spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5。
注意可以赋值为负值,这是因为在C 语言中,枚举类型是被当做 int 或者 unsigned int 类型来处理的。
同结构体变量的定义。
enum 枚举名{
枚举成员列表;
}变量名表;
enum {
枚举成员列表;
}变量名表;
enum 枚举名{
枚举成员列表;
}; //分号不可省
之后如果要定义枚举变量:
enum 枚举名 枚举变量名;
与结构体不同的是,枚举变量没有初始化,只有枚举类型有初始化,这是因为枚举元素是常量。
——
只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量:
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
int main()
{
enum DAY day;
day = WED;//√
day = 3;//×
printf("%d",day);
return 0;
}
——
输出结果为3
共用体(union)有时也称联合体,结构体和共用体的语法类似,但有本质的不同。
共用体的定义形式为:
union 共用体名{
成员列表
};
结构体和共用体的区别在于:
结构体的各个成员会占用不同的内存,互相之间没有影响,结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙);
而共用体占用的内存等于最长的成员占用的内存。
特别注意:
共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员;
这是因为共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把其它成员的值覆盖掉。
详细来讲,这里所谓的共享不是指把多个成员同时装入一个联合变量内,而是指该联合变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。
http://c.biancheng.net/view/2035.html C语言共用体(C语言union用法)详解
同结构体变量、枚举变量类似。
enum 共用体名{
共用体成员列表;
}变量名表;
enum {
共用体成员列表;
}变量名表;
enum 共用体名{
共用体成员列表;
}; //分号不可省
之后如果要定义共用体变量:
enum 共用体名 共用体变量名;
————
对共用体变量中成员的引用形式为:
共用体变量名.成员名 //成员访问运算符(.)
个人认为最重要的好处就是使用共用体能够减少占用的内存空间,特别是对于内存空间有限的嵌入式设备。举个例子,有这么一个表格:
姓名 | 职业 | 工作单位地址 | 学校地址 |
---|---|---|---|
张三 | 卖艺 | 天桥 | |
赵四 | 学生 | 职校 |
在这个表格中【工作单位地址】和【学校地址】只要填一个就好,填哪一个取决于你的职业是否为学生。如果使用共用体记录数据,就可以节省另一个单元格内的空间,程序可编为:
struct{
char name[20];
char job[20];
union{
char school[20];
char workplace[20];
} sc;
信息的存取一般以字节为单位,有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。
C语言允许在一个 结构体 中 以位为单位 来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。
位域的定义形式为:
struct 结构体名(可省)
{
type [member_name] : width ;//成员n类型 成员n: 成员n位数 ;
例:unsigned n: 4;//unsigned若省略后一个关键字,大多数编译器都会认为是unsigned int
unsigned n;//可以省略位数,此时仅为普通的结构体成员
};
在嵌入式设备中常见的形式为:
struct
{
unsigned b0:1;
unsigned b1:1;
unsigned b2:1;
unsigned b3:1;
unsigned b4:1;
unsigned b5:1;
unsigned b6:1;
unsigned b7:1;
}bit_byte;
冒号(:)后面的数字用来限定成员变量占用的位数,其取值范围有限,过大会导致溢出,位域的宽度不能超过它所依附的数据类型的长度。
可用的数据类型
只有有限的几种数据类型可以用于位域。在 C99中,这几种数据类型是 int、signed int 、unsigned int(int 默认就是 signed int)和 Bool 。不过编译器在具体实现时进行了扩展,额外支持了 char、signed char、unsigned char 以及 enum 类型。
存储规则
基于位域的特性,使用&获取位域成员的地址是没有意义的。
无名位域
位域成员可以没有名称,只给出数据类型和位宽,一般用来作填充或者调整成员位置,不能被使用。
其定义形式为:
struct 结构体名(可省)
{
type : width ;
例:unsigned : 4;
};