目录
本章主要讲解重点:
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
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));
//输出结果:8struct S3
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S3));
//输出结果:16struct 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