结构体
前面学过一种构造类型——数组:
构造类型:
不是基本类型的数据结构也不是指针,它是若干个相同或不同类型的数据构成的集合
描述一组具有相同类型数据的有序集合,用于处理大量相同类型的数据运算
有时我们需要将不同类型的数据组合成一个有机的整体, 以便于引用。
如:
一个学生有学号/
姓名
/
性别
/
年龄
/
地址等属性
int num;
char name[20];
char sex;
int age;
char addr[30];
显然单独定义以上变量比较繁琐,数据不便于管理
结构体类型的概念及定义
1
、概念:
结构体是一种构造类型的数据结构,
是一种或多种基本类型或构造类型的数据的集合。
2
、 结构体类型的定义方法
咱们在使用结构体之前必须先有类型,然后用类型定义数据结构
这个类型相当于一个模具
(1).先定义结构体类型,再去定义结构体变量
struct 结构体类型名{
成员列表
};
例 1:
struct stu{
int num;
char name[20];
char sex;
};
//有了结构体类型后,就可以用类型定义变量了
struct stu lucy,bob,lilei;//定义了三个
struct stu
类型的变量
每个变量都有三个成员,分别是 num name sex
咱们可以
暂时认为
结构体变量的大小是它所有成员之和
(2).
在定义结构体类型的时候顺便定义结构体变量,以后还可以定义结构体变量
struct 结构体类型名{
成员列表;
}结构体变量 1,
变量
2;
struct 结构体类型名
变量
3
,变量
4
;
例:
struct stu{
int num;
char name[20];
char sex;
}lucy,bob,lilei;
struct stu xiaohong,xiaoming;
3.
在定义结构体类型的时候,没有结构体类型名,顺便定义结构体变量,
因为没有类型名,所以以后不能再定义相关类型的数据了
struct {
成员列表;
}变量
1
,变量
2;
struct {
int num;
char name[20];
char sex;
}lucy,bob;
以后没法再定义这个结构体类型的数据了,因为没有类型名
4.
最常用的方法
通常咱们将一个结构体类型重新起个类型名,用新的类型名替代原先的类型
步骤 1
:先用结构体类型定义变量
struct stu{
int num;
char name[20];
char sex;
}bob;
步骤 2
:新的类型名替代变量名
struct stu{
int num;
char name[20];
char sex;
}STU;
步骤 3
:在最前面加
typedef
typedef struct stu{
int num;
char name[20];
char sex;
}STU;
注意:步骤
1
和步骤
2
,在草稿上做的,步骤
3
是程序中咱们想要的代码
以后
STU
就相当于
struct stu
STU lucy;
和
struct stu lucy;
是等价的。
结构体变量的定义初始化及使用
1
、结构体变量的定义和初始化
结构体变量,是个变量,这个变量是若干个数据的集合
注:
(1):在定义结构体变量之前首先得有结构体类型,然后在定义变量
(2):在定义结构体变量的时候,可以顺便给结构体变量赋初值,被称为结构体的初始化
(3):结构体变量初始化的时候,各个成员顺序初始化
例1:
struct stu{
int num;
char name[20];
char sex;
};
struct stu boy;
struct stu lucy={
101,
"lucy",
'f'
};
2
、结构体变量的使用
定义了结构体变量后,要使用变量
(1).结构体变量成员的引用方法
结构体变量.
成员名
struct stu{
int num;
char name[20];
char sex;
};
struct stu bob;
bob.num=101;//bob 是个结构体变量,但是 bob.num 是个 int 类型的变量
bob.name 是个字符数组,是个字符数组的名字,代表字符数组的地址,是个常量
bob.name ="bob";//是不可行,是个常量
strcpy(bob.name,"bob");
#include <stdio.h>
struct stu{
int num;
char name[20];
int score;
char *addr;
};
int main(int argc, char *argv[])
{
struct stu bob;
printf("%d\n",sizeof(bob));
printf("%d\n",sizeof(bob.name));
printf("%d\n",sizeof(bob.addr));
return 0;
}
(2).
结构体成员多级引用
#include <stdio.h>
struct date{
int year;
int month;
int day;
};
struct stu{
int num;
char name[20];
char sex;
struct date birthday;
};
int main(int argc, char *argv[])
{
struct stu lilei={101,"lilei",'m'};
lilei.birthday.year=1986;
lilei.birthday.month=1;
lilei.birthday.day=8;
printf("%d %s %c\n",lilei.num,lilei.name,lilei.sex);
printf("%d %d %d\n",lilei.birthday.year,lilei.birthday.month,lilei.birthday.day);
return 0;
}
3
、
相同类型的
结构体变量可以相互赋值
注意:必须是相同类型的结构体变量,才能相互赋值。
#include <stdio.h>
struct stu{
int num;
char name[20];
char sex;
};
int main(int argc, char *argv[])
{
struct stu bob={101,"bob",'m'};
struct stu lilei;
lilei=bob;
printf("%d %s %c\n",lilei.num,lilei.name,lilei.sex);
return 0;
}
结构体数组
结构体数组是个数组,由
若干个相同类型的结构体变量构成的集合
1
、结构体数组的定义方法
struct
结构体类型名 数组名
[
元素个数
];
例 9:
struct stu{
int num;
char name[20];
char sex;
};
struct stu edu[3];//定义了一个
struct stu
类型的结构体数组
edu
,
这个数组有
3
个元素分别是
edu[0]
、
edu[1]
、
edu[2]
1
、结构体数组元素的引用 数组名
[
下标
]
2
、数组元素的使用
edu[0].num =101;//用
101
给
edu
数组的第
0
个结构体变量的
num
赋值
strcpy(edu[1].name,"lucy");
#include <stdio.h>
typedef struct student
{
int num;
char name[20];
float score;
}STU;
STU edu[3]={
{101,"Lucy",78},
{102,"Bob",59.5},
{103,"Tom",85}
};
int main()
{
int i;
float sum=0;
for(i=0;i<3;i++)
{
sum+=edu[i].score;
}
printf("平均成绩为%f\n",sum/3);
return 0;
}
结构体指针
即结构体的地址,结构体变量存放内存中,也有起始地址
咱们定义一个变量来存放这个地址,那这个变量就是结构体指针变量。
1
、结构体指针变量的定义方法:
struct 结构体类型名
*
结构体指针变量名
;
struct stu{
int num;
char name[20];
};
struct stu * p;//定义了一个
struct stu *
类型的指针变量
变量名 是 p
,
p
占
4
个字节,用来保存结构体变量的地址编号
struct stu boy;
p=&boy;
访问结构体变量的成员方法:
boy.num=101;//可以,通过 结构体变量名.成员名
(*p).num=101;//可以,*p 相当于 p 指向的变量 boy
p->num=101;//可以,指针->成员名
通过结构体指针来引用指针指向的结构体的成员,前提是 指针必须先指向一个结构体变量。
结构体指针经常用到的地方:
(1)
:保存结构体变量的地址
typedef struct stu{
int num;
char name[20];
float score;
}STU;
int main()
{
STU *p,lucy;
p=&lucy;
p->num=101;
strcpy(p->name,"baby");
//p->name="baby";//错误,因为 p->name 相当于 lucy.name 是个字符数组的名字,是个常量
}
(2)
:传 结构体变量的地址
#include<stdio.h>
#include<string.h>
typedef struct stu{
int num;
char name[20];
float score;
}STU;
void fun(STU *p)
{
p->num=101;
(*p).score=87.6;
strcpy(p->name,"lucy");
}
int main()
{
STU girl;
fun(&girl);
printf("%d %s %f\n",girl.num,girl.name,girl.score);
return 0;
}
(3)
:传结构体数组的地址
结构体数组,是由多个相同类型的结构体变量构成的。存放在内存里,
也有起始地址,其实就是第
0
个结构体的地址。
#include<stdio.h>
#include<string.h>
typedef struct stu{
int num;
char name[20];
float score;
}STU;
void fun(STU *p)
{
p[1].num=101;
(*(p+1)).score=88.6;
}
int main()
{
STU edu[3];
fun(edu);
printf("%d %f\n",edu[1].num,edu[1].score);
return 0;
}
注意:
(1)
:结构体变量的地址编号和结构体第一个成员的地址编号相同,但指针的类型不同
#include <stdio.h>
struct stu{
int num;
char name[20];
int score;
};
int main(int argc, char *argv[])
{
struct stu bob;
printf("%p\n",&bob);
printf("%p\n",&(bob.num));
return 0;
}
(2)
:结构体数组的地址就是结构体数组中第
0
个元素的地址
#include <stdio.h>
struct stu{
int num;
char name[20];
int score;
};
int main(int argc, char *argv[])
{
struct stu edu[3];
printf("%p\n",edu);//struct stu *
printf("%p\n",&(edu[0]));//struct stu *
printf("%p\n",&(edu[0].num));//int *
return 0;
}
结构体内存分配
1
、结构体内存分配
结构体变量大小是,它所有成员之和。
因为结构体变量是所有成员的集合。
#include<stdio.h>
struct stu{
int num;
int age;
}lucy;
int main()
{
printf("%d\n",sizeof(lucy));//结果为 8
return 0;
}
但是在实际给结构体变量分配内存的时候,是规则的
#include<stdio.h>
struct stu{
char sex;
int age;
}lucy;
int main()
{
printf("%d\n",sizeof(lucy));//结果为 8???
return 0;
}
规则
1
:以多少个字节为单位开辟内存
给结构体变量分配内存的时候,会去结构体变量中找基本类型的成员
哪个基本类型的成员占字节数多,就以它大大小为单位开辟内存,
在 gcc
中出现了
double
类型的,例外
(1):成员中只有
char
型数据 ,以
1
字节为单位开辟内存。
(2):成员中出现了
short int
类型数据,没有更大字节数的基本类型数据。
以 2
字节为单位开辟内存
(3):出现了
int float
没有更大字节的基本类型数据的时候以
4
字节为单位开辟内存。
(4):出现了
double
类型的数据
情况
1
:
在 vc
里,以
8
字节为单位开辟内存。
情况 2
:
在 gcc
里,以
4
字节为单位开辟内存。
无论是那种环境,double
型变量,占
8
字节。
注意:
n 如果在结构体中出现了数组,数组可以看成多个变量的集合。
如果出现指针的话,没有占字节数更大的类型的,以 4
字节为单位开辟内存。
在内存中存储结构体成员的时候,按定义的结构体成员的顺序存储。
例 19:struct stu{
char sex;
int age;
}lucy;
lucy 的大小是 4 的倍数。
规则
2
:字节对齐
(1):
char 1
字节对齐 ,即存放
char
型的变量,内存单元的编号是
1
的倍数即可。
(2):
short int 2
字节对齐 ,即存放
short int
型的变量,起始内存单元的编号是
2
的倍数即可。
(3):
int 4
字节对齐 ,即存放
int
型的变量,起始内存单元的编号是
4
的倍数即可
(4):
long int
在
32
位平台下,
4
字节对齐 ,即存放
long int
型的变量,起始内存单元的编号是
4
的倍数即 可
(5):
float 4
字节对齐 ,即存放
float
型的变量,起始内存单元的编号是
4
的倍数即可
(6):
double
a.vc
环境下
8 字节对齐,即存放
double
型变量的起始地址,必须是
8
的倍数,
double
变量占
8
字节
b.gcc
环境下
4 字节对齐,即存放
double
型变量的起始地址,必须是
4
的倍数,
double
变量占
8
字节。
注意
3
:当结构体成员中出现数组的时候,可以看成多个变量。
注意
4
:开辟内存的时候,从上向下依次按成员在结构体中的位置顺序开辟空间
#include<stdio.h>
struct stu{
char a;
short int b;
int c;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
printf("%p\n",&(temp.c));
return 0;
}
结果分析:
a
的地址和
b
的地址差
2
个字节
b
的地址和
c
的地址差
2
个字节
例 21:temp 的大小为 12 个字节
#include<stdio.h>
struct stu{
char a;
int c;
short int b;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
printf("%p\n",&(temp.c));
return 0;
}
结果分析:
a
和
c
的地址差
4
个字节
c
和
b
的地址差
4
个字节
例 22:
struct stu{
char buf[10];
int a;
}temp;
//temp 占 16 个字节
例 23:
在 vc 中占 16 个字节 a 和 b 的地址差 8 个字节
在 gcc 中占 12 个字节 a 和 b 的地址差 4 个字节
#include<stdio.h>
struct stu{
char a;
double b;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
return 0;
}
为什么要有字节对齐?
用空间来换时间,提高
cpu
读取数据的效率
指定对齐原则:
使用#pragma pack
改变默认对其原则
格式:
#pragma pack (value)时的指定对齐值
value
。
注意:
1.value只能是:
1 2 4 8
等
2.指定对齐值与数据类型对齐值相比取较小值
说明:咱们制定一个value
(1)
:以多少个字节为单位开辟内存
结构体成员中,占字节数最大的类型长度和value
比较,
取较小值,为单位开辟内存
例 24:
#pragma pack(2)
struct stu{
char a;
int b;
}
以
2
个字节为单位开辟内存
#include<stdio.h>
#pragma pack(2)
struct stu{
char a;
int b;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
return 0;
}
temp
的大小为
6
个字节
a
和
b
的地址差
2
个字节
例 25:
#pragma pack(8)
struct stu{
char a;
int b;
} ;
以
4
个字节为单位开辟内存
#include<stdio.h>
#pragma pack(8)
struct stu{
char a;
int b;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
return 0;
}
temp
的大小为
8
个字节
a
和
b
的地址差
4
个字节
(2)
:字节对齐
结构体成员中成员的对齐方法,各个默认的对齐字节数和value
相比,
#include<stdio.h>
#pragma pack(2)
struct stu{
char a;
int b;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
return 0;
}
b成员是2字节对齐,a和b的地址差2个字节
#include<stdio.h>
#pragma pack(8)
struct stu{
char a;
int b;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
return 0;
}
a
和
b
都按原先的对齐方式存储
如:如果指定对齐值:
设为
1
:则
short
、
int
、
float
等均为
1
设为
2
:则
char
仍为
1
,
short
为
2
,
int
变为
2
位域(位段)
概述
注意:
1、不能对位域取地址
2、给位域赋值 不要超过位域的大小(会溢出)
示例
typedef struct data06
{
unsigned char a:2;
unsigned char b:2;
unsigned char c:2;
unsigned char d:2;
}Data06;
void fun03()
{
printf("%ld\n",sizeof(Data06));//长度1字节
}
示例
2:
另起一个单元存储
typedef struct data07
{
unsigned char a:2;
unsigned char b:2;
unsigned char c:2;
unsigned char :0;
unsigned char d:2;
}Data07;
void fun04()
{
printf("%ld\n",sizeof(Data07));//长度2
}
示例
3:
无意义位段
typedef struct data08
{
unsigned char a:2;
unsigned char b:2;
unsigned char :2;
unsigned char d:2;
}Data08;
void fun05()
{
printf("%ld\n",sizeof(Data08));//长度1
}
共用体
关键字
:union
特点:
所有成员共用一块空间。
//a b c共用同一块空间(最大的成员类型决定)。
union Data09
{
char a;
short b;
int c;
};
void fun06()
{
union Data09 d01;
printf("%ld\n",sizeof(d01));//长度4
}
虽然共用体成员在同一块空间,但是每个成员操作空间的大小 是由成员自身类型决定。
union Data09
{
char a;
short b;
int c;
};
void fun07()
{
union Data09 d01;
d01.a = 10;
d01.b = 20;
d01.c = 30;
printf("%d\n",d01.a+d01.b+d01.c);
}
union Data09
{
char a;
short b;
int c;
};
void fun08()
{
union Data09 d01;
d01.a = 0x01;
d01.b = 0x0102;
d01.c = 0x01020304;
printf("%d\n",d01.a+d01.b+d01.c);
}
枚举
关键字
:enum
特点:
限定其值的范围
概念
:
枚举就是将枚举变量可以赋的值一一列举出来
语法 :
enum 枚举名称
{
值1,
值2,
值3,
...
值n
}
注意
:
枚举中的值为符号常量,
也是枚举变量可以赋的值
枚举列表中的值从0
递增
enum Type
{
OK,
ERROR,
OVER
};
void fun08()
{
enum Type t1 = OK;
enum Type t2 = ERROR;
enum Type t3 = OVER;
printf("%d %d %d\n",OK,ERROR,OVER);
}
enum Type2
{
NUM01,
NUM02 = 3,
NUM03
};
void fun09()
{
enum Type2 t1 = NUM01;
enum Type2 t2 = NUM02;
enum Type2 t3 = NUM03;
printf("%d %d %d\n",NUM01,NUM02,NUM03);
}