目录
结构体诞生 构造新类型!
结构体构造类型 typedef
(1)typedef 使用
(2)typedef 与 define
结构体初始化&赋值
1、初始化
手动 scanf 赋值
结构体类型作参数和返回值
1、结构体变量 作参数和返回值
2、结构体指针作参数 ——提升效率
.结构体数组
结构体 sizeof 大小 以及内存对齐
(1)什么是内存对齐 (见下图)
(2)对齐规则
结构体使用注意事项
道家思想:“道生一,一生二,二生三,三生万物”
也就是 : 从单变量->数组->结构体
1:1 一个变量,一个类型 单一类型变量
N:1 N个变量,一个类型 数组
N:M N个变量,M个类型 结构体
为了更方便的描述事情,C语言对变量类型 开放权限,允许在基础类型的基础上进行自由构造。
typedef struct stu { int num; char name[20]; char sex; float score; }Stu; Stu s1; //定义 与 int a; 地位等价1、先用原类型定义变量
2、定义之前加 typedef
3、将原变量的名字 换成你需要的类型名
注意:typedef 只是对现有类型 取别名,不能创造新类型。
Define 构成的语句在预处理阶段——进行文本替换,不过脑子的那种替换
typedef 构成C语言的语句;参与编译
#define pointer char* pointer m,n; printf("\nsizeof(m) = %d\t sizeof(n) = %d\n",sizeof(m),sizeof(n)); typedef pointer char* pointer m,n; printf("\nsizeof(m) = %d\t sizeof(n) = %d\n",sizeof(m),sizeof(n));
(1)和 %c 打交道的时候 容易出错
在输入num %d的数值之后,空格或者换行都会占据 %c 的位置!使 %c 成功被忽略。在连续输入时,%c前边加一个空格解决此类问题。
(2)取地址符 &
结构体定义时,name为数组类型,num、sex、score都是基本类型。 取地址时,数组不用 &符号,其他得用。
分两种:一种为结构体变量整个作参数并返回;一种是以结构体指针形式作参并返回。
结构体作参数或返回值,会发生同类型复制(本质是赋值)。同类型结构体间,是可以相互赋值的。 BUT 当结构体内的变量数目较大的时候,会进行大量的内存拷贝。可能会导致栈的进程空间不足而崩溃。
#include <stdio.h> typedef struct _Mycomplex { float real; float imag; }Mycomplex; Mycomplex ADDcomplex (Mycomplex pa,Mycomplex pb) { Mycomplex t; t.real = pa.real + pb.real; t.imag = pa.imag + pb.imag; return t; } int main() { //结构体变量 作参数 Mycomplex s1,s2,res; s1.real = 1; s1.imag = 2; s2.real = 3; s2.imag = 4; res = ADDcomplex(s1,s2); printf("%.2f+%.2f",res.real,res.imag); return 0; }
传结构体的成本是很高的,用指针作参数有一个好处,就是避免了同类型复制,无论结构体多大,只传 4 个字节的指针。 (注意:结构体变量的 点成员运算符 和 地址的 指向成员运算符 的使用)
#include <stdio.h> typedef struct _Mycomplex { float real; float imag; }Mycomplex; Mycomplex ADDcomplex (Mycomplex *pa,Mycomplex *pb) { Mycomplex t; t.real = pa->real + pb->real; t.imag = pa->imag + pb->imag; return t; } int main() { //传地址 Mycomplex s1 = {1,2},s2 = {3,4},res; //s1.real = 6; //s1.imag = 2; //s2.real = 3; //s2.imag = 4; //要么在结构体初始化时赋值,要么对结构体成员单独赋值。不存在 s1 = {1,2}; res = ADDcomplex(&s1,&s2); printf("%.2f+%.2f",res.real,res.imag); return 0; } //传地址与返回地址 /* Mycomplex * ADDcomplex (Mycomplex *pa,Mycomplex *pb) { Mycomplex *t; t->real = pa->real + pb->real; t->imag = pa->imag + pb->imag; return t; } int main() { //传地址 Mycomplex s1 = {1,2},s2 = {3,4}; Mycomplex * res; res = ADDcomplex(&s1,&s2); printf("%.2f+%.2f",res->real,res->imag); return 0; } */
结构体数组的本质还是一维数组,只不过一维数组的每一个成员又是结构体。
#include <stdio.h> typedef struct stu { int num; char name[20]; char sex; float score; }Stu; int main() { //结构体数组 Stu s[] ={{101,"jiaomingxin",'m',100},{102,"jackma",'m',99},{103,"mars",'f',20}}; int n = sizeof(s)/sizeof(*s); for(int i=0; i<n; i++) { printf("s[%d]num --> %d\n",i,s[i].num); printf("s[%d]name --> %s\n",i,s[i].name); printf("s[%d]sex --> %c\n",i,s[i].sex); printf("s[%d]score --> %.1f\n",i,s[i].score); puts(""); } return 0; }首成员在低地址,尾成员在高地址
x86(linux 默认#pragma pack(4), window 默认#pragma pack(8))。linux 最大支持 4 字节对齐。 方法: ①取 pack(n)的值(n= 1 2 4 8--),取结构体中类型最大值 m。两者取小即为外对齐大 小 Y= (m<n?m:n)。 ②将每一个结构体的成员大小与 Y 比较取小者为 X,作为内对齐大小. ③所谓按 X 对齐,即为地址(设起始地址为 0)能被 X 整除的地方开始存放数据。 ④外部对齐原则是依据 Y 的值(Y 的最小整数倍),进行补空操作。
#include <stdio.h> #include <string.h> #pragma pack(8) typedef struct _staff { char sex; int age; short num; }Staff; #if 0 x86(linux 默认#pragma pack(4), window 默认#pragma pack(8))。linux 最大支持 4 字节对齐。 具体操作步骤: ①取 pack(n)的值(n= 1 2 4 8--),取结构体中类型最大值 m。两者取小即为外对齐大 小 Y= (m<n?m:n)。 ②将每一个结构体的成员大小与 Y 比较取小者为 X,作为内对齐大小. ③所谓按 X 对齐,即为地址(设起始地址为 0)能被 X 整除的地方开始存放数据。 ④外部对齐原则是依据 Y 的值(Y 的最小整数倍),进行补空操作。 以此为例: #pragma pack(8) typedef struct _staff { char sex; int age; short num; }Staff; 1、pack n = ? 结构体中类型最大的 m Y=min(m,n) 外对齐 n=8;m=4 --> Y=4 2、每个成员 char sex; 1 与Y相比取小值为X X = 1 int age; 4 4 short num; 2 2 存储位置 3、按X对齐 sex 0 - 1 (1字节) 往后找能被1整除的 那就1了 (设起始地址为 0) age 1 - 5 (4字节) 往后招能被4整除的 到了 8 num 8 - 10(2字节) OK存完了 4、外部对齐原则是依据 Y 的值(Y 的最小整数倍),进行补空操作。 Y=4 4的最小整倍数,10不是,找到12 分析完毕! #endif int main() { Staff s; printf("sizeof(Staff) = %d\n",sizeof (Staff)); printf("sizeof(s) = %d\n\n",sizeof (s)); printf("&(s) = %p\n",&s); printf("&(sex) = %p\n",&s.sex); printf("&(age) = %p\n",&s.age); printf("&(num) = %p\n",&s.num); return 0; }1、向未分配空间的结构体指针拷贝!
应用指针之前,一定要确保指针已有空间!否则没有指向合法的地址,考进去就是一堆乱码,并且后续也无法正常访问空间。
#include <stdio.h> #include<string.h> #include <stdlib.h> typedef struct stu { char *name; }Stu; int main() { Stu s; // strcpy(s.name,"mars"); // printf("s. name = %s\n",s.name); //错误1 向未分配空间的指针赋值 s.name = (char *)malloc(20); strcpy(s.name,"mars"); printf("s. name = %s\n",s.name); Stu *p; p = (Stu *)malloc(sizeof(Stu *)); //为整个结构体指针分配空间 p->name = (char *)malloc(20); //还得为name指针分配空间 strcpy(p->name,"marsss"); printf("s. name = %s",p->name); free(p->name); //先释放内层空间 free(p); //后释放外层的 return 0; }注意:
1)结构体中,包含指针,注意指针的赋值,切不可向未知区域拷贝
2)name 指针未初始化时,并没有指向一个合法的地址,这时候其内部存的只是一些乱码。所以在调用 strcpy 函数时,会将字符串 "mars" 往乱码所指的内存上拷贝,内存 name 指针根本就无权访问,导致出错
在所有涉及指针的时候,要么先定义的时候初始化,要么在使用之前进行malloc分配堆空间。
3)多层指针每个都要初始化。为指针变量 (Stu) p 分配了内存,但是同样没有给 name 指针分配内存。(p只是申请了name指针的指针类型的4个字节,与2)情况一样崩溃)错误与上面上一种情况一样,解决的办法也一样。这里用了一个 malloc 给人一种错觉,以为也给name 指针分配了内存。
4)记得:完了要释放结构体内指针所指向的空间,由内而外逐个释放。-->内存泄漏(先释放p,p.name就找不到了 也就无法释放,造成内存泄漏)
