做网站水晶头,网页视频制作,上海工程有限公司,域名交易网站建设一.前言引入.
我们知道在C语言中有内置类型#xff0c;如#xff1a;整型#xff0c;浮点型等。但是只有这些内置类 型还是不够的#xff0c;假设我想描述学⽣#xff0c;描述⼀本书#xff0c;这时单⼀的内置类型是不⾏的。描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体…一.前言引入.
我们知道在C语言中有内置类型如整型浮点型等。但是只有这些内置类 型还是不够的假设我想描述学⽣描述⼀本书这时单⼀的内置类型是不⾏的。描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体重等描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问 题增加了结构体共用体等⾃定义的数据类型让程序员可以⾃⼰创造适合的类型。今天我们来讲讲结构体。 二.结构体介绍. 定义结构体是⼀些值的集合这些值称为成员变量。结构的每个成员可以是不同类型的变量如 标量、数组、指针甚⾄是其他结构体。 结构体的声明 struct tag
{member-list;
}variable-list; 例如描述一个学生 struct Student
{char name[20];//名字int age;//年龄char sex[5];//性别char id[15];//学号
}s1;//s1为全局变量
int main()
{struct Student s2;//局部变量return 0;
} 结构体特殊声明 在声明结构的时候可以不完全的声明 //在声明结构的时候可以不完全的声明,即匿名结构体类型
struct
{int a;char b;float c;
}s1; 请判断下面代码是否正确 #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
//在声明结构的时候可以不完全的声明,即匿名结构体类型
struct
{int a;char b;float c;
}s1;
struct
{int a;char b;float c;
}s2[20], * p;
int main()
{p s1;return 0;
} 警告 编译器会把上⾯的两个声明当成完全不同的两个类型所以是⾮法的。 匿名的结构体类型如果没有对结构体类型重命名的话基本上只能使⽤⼀次 结构体变量的定义 struct Point
{int x; int y;
}s1;//结构体变量的定义
struct Point s2;//结构体变量的定义
int main()
{struct Point s3;//结构体变量的定义return 0;
} 结构体变量的初始化 struct Stu
{char name[20];int age;
};
int main()
{struct Stu s1 { zhangsan,18 };///结构体变量初始化 return 0;
} 镶嵌初始化 struct Point
{int x;int y;
};
struct Stu
{char name[20];int age;struct Point s2;
};
int main()
{struct Stu s1 { zhangsan,18,{2,3} };///结构体变量镶嵌初始化 return 0;
} 三.结构成员访问操作符. 3.1.结构体成员的直接访问 使⽤⽅式结构体变量.成员名 结构体成员的直接访问是通过点操作符.访问的。点操作符接受两个操作数. struct Point
{int x;int y;
}s1 {3,5};
int main()
{struct Point s2 { 2,4 };printf(%d %d\n, s1.x, s1.y);printf(%d %d\n, s2.x, s2.y);return 0;
} 3.2.结构体成员的间接访问 使⽤⽅式结构体指针-成员名 有时候我们得到的不是⼀个结构体变量⽽是得到了⼀个指向结构体的指针. #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
struct Point
{int x;int y;
}s1 { 3,5 };
int main()
{struct Point s2 { 2,3 };struct Point* pi1 s1;struct Point* pi2 s2;printf(%d %d\n, pi1-x, pi1-y);printf(%d %d\n, pi2-x, pi2-y);pi1-x 10;pi1-y 20;printf(%d %d\n, pi1-x, pi1-y);return 0;
} 综合使用结构体用例 #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
#include string.h
struct Stu
{char name[10];int age;
};
void Print(struct Stu s2)
{printf(%s %d\n, s2.name, s2.age);
}
void Set(struct Stu* s3)
{s3-age 15;strcpy(s3-name, 李四);
}
int main()
{struct Stu s1 { zhangsan,18 };Print(s1);Set(s1);Print(s1);return 0;
} 结果 四.结构体的⾃引⽤. 五.结构体的内存对齐. 计算结构体的⼤⼩ 例如 #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
#include string.h
struct s1
{char a;int b;char c;
};
struct s2
{char a;char b;int c;
};
int main()
{printf(%zd\n, sizeof(struct s1));printf(%zd\n, sizeof(struct s2));return 0;
} 如果我给你一份这样的代码请问你认为结果如何呢两者一样吗 答案如下 和你想的一样吗 如果不一样不妨来看看我的解读 结构体的对⻬规则 1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处 2. 其他成员变量要对⻬到某个数字对⻬数的整数倍的地址处。 对⻬数 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。 VS 中默认的值为 8 Linux中 gcc 没有默认对⻬数对⻬数就是成员⾃⾝的⼤⼩ 3. 结构体总⼤⼩为最⼤对⻬数结构体中每个成员变量都有⼀个对⻬数所有对⻬数中最⼤的的 整数倍。 4. 如果嵌套了结构体的情况嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处结构 体的整体⼤⼩就是所有最⼤对⻬数含嵌套结构体中成员的对⻬数的整数倍 例题1 #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
struct s1
{//1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处char a;//对齐偏移量为0位置处char一个字节//其他成员变量要对⻬到某个数字对⻬数的整数倍的地址处。//对⻬数 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。 //对齐数VS 中默认的值为 8 char b;//对齐偏移量为1位置处char一个字节即 1 8 1第一个为成员变量的对齐数第二个为VS对齐数第三个为该行对齐数int c;//对齐偏移量为4位置处int四个字节即 4 8 4
};
int main()
{//结构体总⼤⼩为最⼤对⻬数结构体中每个成员变量都有⼀个对⻬数所有对⻬数中最⼤的的整数倍,即1 1 4 8 -8printf(%zd\n, sizeof(struct s1));//结果为8return 0;
} 结果为 例题二 struct s2
{//1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处char a;//对齐偏移量为0位置处char一个字节//其他成员变量要对⻬到某个数字对⻬数的整数倍的地址处。//对⻬数 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。 //对齐数VS 中默认的值为 8 int b;//对齐偏移量为4位置处,四个字节int: 4 8 4第一个为成员变量的对齐数第二个为VS对齐数第三个为该行对齐数char c;//对齐偏移量为9位置处char 1个字节1 8 1};
int main()
{//结构体总⼤⼩为最⼤对⻬数结构体中每个成员变量都有⼀个对⻬数所有对⻬数中最⼤的的整数倍,即1 4 1 -4的整数倍为12(原来的数据已经达到偏移量为9了printf(%zd\n, sizeof(struct s2));//结果为12return 0;
} 结果 例题三 #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
struct s3
{//下面简写//前三个为成员大小 VS大小 该行大小偏移量的位置double d;//8 8 8,0-7char c;//1 8 1,8int i;//4 8 4.12-15
};
int main()
{printf(%zd\n, sizeof(struct s3));//结果8 1 4-8 15-16return 0;
} 结果 例题四 #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
练习4-结构体嵌套问题
struct s3
{//下面简写//前三个为成员大小 VS大小 该行大小偏移量的位置double d;char c;int i;
};
struct s4
{char c1;//1 8 1,0//4. 如果嵌套了结构体的情况嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处结构体的整体⼤⼩就是所有最⼤对⻬数含嵌套结构体中成员的对⻬数的整数倍struct s3 a;//16 8 8,8-23double d;//8 8 8,24-31
};
int main()
{printf(%zd\n, sizeof(struct s4));//1 16 8-16 31-32return 0;
} 你是否想过这样一个问题为什么存在内存对⻬呢? 平台原因 (移植原因) 不是所有的硬件平台都能访问任意地址上的任意数据的某些硬件平台只能在某些地址处取某些特定类型的数据否则抛出硬件异常。 性能原因 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于为了访问未对⻬的内存处理器需要作两次内存访问⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数那么就可以⽤⼀个内存操作来读或者写值了。否则我们可能需要执⾏两次内存访问因为对象可能被分放在两个8字节内存块中。 总体来说结构体的内存对⻬是拿空间来换取时间的做法。 那在设计结构体的时候我们既要满⾜对⻬⼜要节省空间 重点如何做到 让占⽤空间⼩的成员尽量集中在⼀起 重点如何做到 让占⽤空间⼩的成员尽量集中在⼀起 重点如何做到 让占⽤空间⼩的成员尽量集中在⼀起 说三遍 相信你现在一定会开头的那题了 那么我们可以自己设置对齐数吗答案是当然可以啦 #pragma 这个预处理指令可以改变编译器的默认对⻬数 #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
#pragma pack(1)
练习4-结构体嵌套问题
struct s3
{//下面简写//前三个为成员大小 VS大小 该行大小偏移量的位置double d;char c;int i;
};
struct s4
{char c1;//1 8 1,0//4. 如果嵌套了结构体的情况嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处结构体的整体⼤⼩就是所有最⼤对⻬数含嵌套结构体中成员的对⻬数的整数倍struct s3 a;//16 8 8,8-23double d;//8 8 8,24-31
};
int main()
{printf(%zd\n, sizeof(struct s4));//1 16 8-16 31-32return 0;
} 修改之后结果为 #pragma pack()//取消设置的对⻬数还原为默认 这样当结构体在对⻬⽅式不合适的时候我们可以⾃⼰更改默认对齐数。 六.结构体传参和对比 1.传数据 #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
struct S
{int data[1000];int num;
};
void Print(struct S s1)
{printf(%d\n, s1.num);
}
int main()
{struct S s1 { {1,2,3,4}, 1000 };Print(s1);return 0;
} 结果 2.传地址 #define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
struct S
{int data[1000];int num;
};
void Print(struct S* s1)
{printf(%d\n, s1-num);
}
int main()
{struct S s1 { {1,2,3,4}, 1000 };Print(s1);return 0;
} 结果 对比两者你认为哪个好呢 函数传参的时候参数是需要压栈会有时间和空间上的系统开销。 如果传递⼀个结构体对象的时候结构体过⼤参数压栈的的系统开销⽐较⼤所以会导致性能的下降。 结论 结构体传参的时候要传结构体的地址。 最后学习进步