目录
本章主要讲解重点:
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
struct tag
{
member-list;
}variable-list;
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
对于匿名结构在声明的时候省略掉了结构体标签(tag),也就是只能在声明的时候进行操作(声明外再次使用无法进行调用(没有名称))
//在上面代码的基础上,下面的代码为err
p = &x;
//编译器会把上面的两个声明当成完全不同的两个类型
在链表中我们需要用到的就是结构的自引用
//创建链表节点
struct Node
{
int data;
struct Node* next;
};
typedef struct
{
int data;
Node* next;
}Node;
//只有在重命名后才能使用重命名名
//正确写法:
typedef struct Node
{
int data;
struct Node* next;
}Node;
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
struct student
{
}stu;
int main (void)
{
printf ("sizeof (stu) = %d\n", sizeof (stu));
return 0;
}
输出结果:
在C中, sizeof (stu) = 0
在C++中, sizeof (stu) = 1
对于空结构体不同编译器理解不同,所以大小不一(可能0或者1(作为占位符))
struct中的各成员变量的存储地址有一套对齐的机制(让CPU能够更舒服地访问变量)
结构体的内存对齐是拿空间来换取时间的做法
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
//输出结果:12
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));
//输出结果:8
struct S3
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S3));
//输出结果:16
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("%d\n", sizeof(struct S4));
//输出结果:48
尽量让占用空间小的成员尽量集中在一起(既满足对齐,又节省空间)(如示例1与示例2)
使用#pragma 这个预处理指令来改变我们的默认对齐数
#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
//输出的结果是什么?
printf("%d\t", sizeof(struct S1));
printf("%d\t", sizeof(struct S2));
return 0;
}
//输出结果:12 6
结构在对齐方式不合适的时候,我么可以自己更改默认对齐数
计算结构体中某变量相对于首地址的偏移,并给出说明
#include<stdio.h>
#include<stddef.h>
struct s
{
char c;
int i;
double d;
};
int main()
{
// offsetof其实是一个宏,用来表示成员相对于结构体的偏移量
//而且offsetof的参数传的是一个类型,更加说了offsetof是一个宏
printf("%d\n", offsetof(struct s, c));// 0
printf("%d\n", offsetof(struct s, i));// 4
printf("%d\n", offsetof(struct s, d));// 8
return 0;
}
struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
//创建临时结构体来接收(消耗空间)
void print1(struct S s)
{
printf("%d\n", s.num);
}
//结构体地址传参
//指针接收
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
其实效果都一样,两者都可以选择,但是推荐结构体传址
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少一个其他成员
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct data
{
int len; //一般用来表示字符数组的字符个数
char name[];//空间大小为0
}S;
int main(void)
{
S s;
printf("sizeof(s)=%d\n",sizeof(s));//输出为4,即是int类型大小
int len = 10; //申请空间
struct data *p =(struct data*)malloc(sizeof(s)+sizeof(char)*len);
//判断是否申请成功&请空处理
p->len = len;
strcpy(p->name,"xxxxxx"); //字符串赋值需要用strcpy
printf("%s\n",p->name);
//释放指针p
free(p);
return 0;
}
//输出结果:xxxxxx
在C++里struct关键字与class关键字一般可以通用
struct的成员默认情况下属性是public的,而class成员却是private的
位段的声明和结构是类似的
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
注:在vs平台下,其他平台不确定(可以自己进行测试)
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在
枚举型是一个集合(可以一一列举的类似元素),其元素(枚举成员)是一些命名的整型常量(元素之间用逗号隔开)
enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
enum Color//颜色
{
RED,
GREEN,
BLUE
};
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};
我们可以使用 #define 定义常量,为什么非要使用枚举?
预处理指令#define:
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN 7
枚举类型能完成同样的工作(更加简洁便捷):
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
#define是在预处理阶段直接进行替换,并且不进行类型检查, 枚举则是在程序运行之后才起作用
#define定义的类型存储在代码段 枚举常量存储在数据段的静态存储区里
#define可以赋值多种类型数据 枚举变量的大小只能为整型数据(例如:0、1、2…)(enum当我们不主动对它进行赋值时,第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1,#define则不会)
#define宏一次只能定义一个 枚举可以一次定义大量相关的常量
一般在编译器里,可以调试枚举常量,但是不能调试宏常量
枚举量具有类型,宏没有类型;枚举常量属于常量,宏定义不是常量
联合也称为共用体,很明显意思是多个变量共用一个空间,所以不能同一时间使用多个变量
//联合类型的声明
union Un
{
char c;
int i;
};
//联合变量的定义
union Un un;
因为联合的成员是共用同一块内存空间的,所以联合变量至少是最大成员的大小(联合至少得有能力保存最大的那个成员)
//在上述代码的基础上 计算联合变量的大小
printf("%d\n", sizeof(un));
//输出结果:4
任何成员变量都是从低地址开始使用
union Un
{
int i;
char c;
};
union Un un;
// 下面输出的结果是一样的吗?
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
//相同 都是从联合变量的低地址开始使用
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
//0x11223355
判断当前计算机的大小端存储
#include<stdio.h>
union var{
char c[4];
int i;
};
int main(){
union var data;
data.c[0] = 0x04;//因为是char类型,值对应ascii
data.c[1] = 0x03;//16进制便于直接与内存中的值对比
data.c[2] = 0x02;
data.c[3] = 0x01;
//数组先使用低地址再使用高地址,内存内容依次为:04,03,02,11(共四字节)
//而把四个字节作为一个整体,对于小端来说:低地址放在低权位
//读取出来则是:0x01020304
//反之则是大端存储模式
printf("%x\n",data.i);//共用空间
}
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1));
//输出结果:8
printf("%d\n", sizeof(union Un2));
//输出结果:16