您的位置:首页 > 科技 > 能源 > 微信公众号公众平台_网站手机优化_最新黑帽seo教程_杭州关键词优化外包

微信公众号公众平台_网站手机优化_最新黑帽seo教程_杭州关键词优化外包

2025/2/23 22:38:01 来源:https://blog.csdn.net/2302_78794424/article/details/144918280  浏览:    关键词:微信公众号公众平台_网站手机优化_最新黑帽seo教程_杭州关键词优化外包
微信公众号公众平台_网站手机优化_最新黑帽seo教程_杭州关键词优化外包

目录

前言

结构体

1含义

2语法

3匿名结构体

4结构体自引用

5结构体的定义与初始化

6内存对齐

7修改对齐数

8结构体传参

位段

1含义

2位段的内存分配

​编辑3位段的问题 

4位段的应用

枚举

1含义

2定义

3枚举优点

4枚举使用

联合

1含义

2定义

3特点

4计算

通讯录


前言

C语言的结构体知识学好了的话对后面学习C++类对象就简单很多了

结构体

1含义

结构体是一些值的集合,这些值称为成员变量;每个成员变量可以是不同类型的变量。

2语法

struct tag//类型名
{member-list;//成员变量
}variable-list;//可在此时定义出结构体类型的变量 它是全局变量

假如我们用结构体来描述一个学生:

struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}S1;//分号不能丢

3匿名结构体

也就是结构体不完全声明

//匿名结构体类型
struct
{int a;char b;float c;
}x;

匿名结构体省略了tag(结构体类型名),而且它只能用一次(声明后立即定义,不能声明后定义)

能否用指针来接收匿名结构体变量?

不能,编译器会认为这个两个类型(vs2019编都编不过,vs2022能编过!)

4结构体自引用

struct Node
{int data;struct Node next;
};int main()
{printf("%d", sizeof(struct Node));return 0;
}

上面这种引用行吗?

编译器认为:你在内部定义了同一个结构体成员,那这个成员变量里又包含了同一个结构体成员...这个大小是无穷大的

所以正确的结构体自引用

struct Node
{int data;struct Node* next;
};

这种在链表中用的最多

其它的自引用定义:

typedef struct Node
{int data;N* next;
}N;

这种写法是错误的:结构体typedef之前内部并不知道

//正确写法
typedef struct Node
{int data;struct Node* next;
}N;

5结构体的定义与初始化

struct Poin
{int x;int y;
}p1 = {1,2};//可以在这里初始化struct stu
{int date;struct Poin p;
}s1;//全局变量int main()
{struct stu s2 = { 100,{20,30} };//局部变量 按顺序初始化//struct stu s2 = { .p={20,30},.date=100 };//指定成员初始化printf("%d %d %d\n", s2.date, s2.p.x, s2.p.y);return 0;
}

6内存对齐

计算结构体的大小:这个非常重要的一个内容!!!

来看下面的一段代码: 

struct s1
{char a;int b;char c;
};struct s2
{char a;char c;int b;
};int main()
{printf("s1的大小:%d\n", sizeof(struct s1));printf("s2的大小:%d", sizeof(struct s2));return 0;
}

int是4字节,char是1字节,两个int和一个char加起来不是6字节吗?

不同的结构体放着相同的变量,只是顺序不同,为什么大小不一样?

介绍一个宏:offsetof,用来查看结构体变量相对于内存的偏移位置

按照打印出来的内存偏移位置进行安排成员变量的放置

一共9个字节就能结束了,偏偏还要在浪费3个字节,前面也浪费3个字节,为什么?                    这时我们来看看内存对齐的规则:

1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到对齐数整数倍的地址处。

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
在VS中默认的值为8 而gcc是没有默认对齐数的
3. 结构体总大小为最大对齐数(每个成员变量计算出来的对齐数的最大值)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处

结构体嵌套的结构体大小:

#include<stdio.h>struct S3//总大小:16
{double d;//8 8 -> 8char c;  //1 8 -> 1int i;   //4 8 -> 4
};
struct S4
{char c1;     //1  8 -> 1struct S3 s3;//16 8 -> 8double d;    //8  8 -> 8
};
int main()
{printf("%d\n", sizeof(struct S4));return 0;
}

那为什么存在内存对齐? 

1. 平台原因(移植原因):
可能硬件平台只能在某些地址处取某些特定类型的数据(否则硬件中断);
2. 性能原因:
为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问

这本质上就是以空间换时间来提高效率!

那在设计结构体时:既要省空间,又要提高效率:

尽量让字节小的成员变量放在一起

#include<stdio.h>struct s1
{char c1;int i;char c2;
};struct s2
{char c1;char c2;int i;
};int main()
{printf("%d %d", sizeof(struct s1), sizeof(struct s2));return 0;
}

7修改对齐数

如果对应VS的默认对齐数不满意(最好不修改!),可以自己设定对齐数

#pragma pack(1)
//在#pragma的范围内的对齐数是1
struct s3
{char c1;int i;char c2;
};
#pragma()struct s4
{char c1;int i;char c2;
};int main()
{printf("s3=%d s4=%d", sizeof(struct s2), sizeof(struct s3));return 0;
}

8结构体传参

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;
}

关于结构体传参,尽量使用传址传参

因为一个结构体大小可能很大,如果传值传参会加大系统资源的开销,导致性能的下降;而使用传址传参时数据不希望进行修改,要把const加上

位段

1含义

位段是一种特殊的自定义类型;

对空间的利用精打细算

a 位段的成员必须是 int、char(整形家族);
b 位段的成员名后边有一个冒号和一个数字

struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};

与结构体相比大小方面有什么不同吗?

struct A//如果是结构体
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};struct B
{int _a;int _b;int _c;int _d;
};int main()
{printf("A=%d B=%d", sizeof(struct A),sizeof(struct B));return 0;
}

答案:位段比结构体大小要小,更节省空间了!那这是怎么实现的呢?

2位段的内存分配

位段后面的数字代表要分配多少个bit空间给该成员;

位段的空间是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的

#include<stdio.h>struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};int main()
{struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;return 0;
}

3位段的问题 

1. int 位段被当成有符号数还是无符号数
2. 位段中最大位的数目

(16位机器最大16,32位机器最大32,如果写成27,在16位机器会出问题)
3. 位段中的成员在内存中从左向右分配,还是从右向左分配

(在VS时从右向左)
4. 开辟的空间无法容纳下个位段时,是舍弃后另外开辟空间还是利用

(在VS时舍弃剩余的位)

4位段的应用

应用于各种协议(信息传到下一层时需要用到)

枚举

1含义

枚举就是一一列举;在现实生活中把一个星期进行枚举:星期一,星期二,星期三...                    那么枚举怎么用代码来表示呢?

2定义

enum Day//星期
{Mon = 1,Tues,Wed,Thur,Fri,Sat,Sun
};

enum Day是一个枚举类型

枚举括号内的内容都是常量

枚举默认从0开始(与数组一样);但你也可以自己修改(Mon = 1)

3枚举优点

以上对于星期的枚举:我们也可以用#define Mon 1,#define Tues 2来表示,那是不是就说枚举就可以被替换了?

a 使用枚举方便调试(而#define在调试前就被替换成常量);

b 枚举有类型检查,更严谨写;

b 使用枚举能增加代码的可读性与可维护性;

c 枚举内定义变量不存在命名冲突(而#define可能有命名污染问题)

4枚举使用

enum Color//颜色
{RED=1,GREEN=2,BLUE=4
};
int main()
{enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异return 0;
}

联合

1含义

联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,这些成员公用同一块空间(所以联合也叫共用体)。

2定义

#include<stdio.h>
//联合类型的声明
union Un
{char c;int i;
};
int main()
{union Un un;printf("%d\n", sizeof(un));return 0;
}

3特点

a 由于联合类型共有内存空间,所以比其它的结构体而言节省空间

b 成员共有内存空间,所以定义变量时成员不能同时出现(后一个会改变前一个的数据)

使用场景1:判断大小端(之前通过地址方式访问判断)

#include<stdio.h>
int sys_call()
{//匿名联合union{char c;int i;}Un;Un.i = 1;// 01 00 00 00 还是 00 00 00 01//(c与i公用1字节的内存空间)return Un.c;
}int main()
{if (sys_call()){printf("小端\n");}else{printf("大端\n");}return 0;
}

使用场景2:设计一个礼物清单 

struct git_list
{double price;char name[20];char author[20];int page;char form[20];double size;char flavour[20];char materials[20];
};//利用联合体设计清晰且省空间
struct git_list
{double price;char name[20];union{char author[20];int page;}book;union{char form[20];double size;}mug;union{char flavour[20];char materials[20];}cooike;
};

4计算

a 联合的大小至少是最大成员的大小;
b 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

所以:联合大小 != 最大成员大小

#include<stdio.h>union Un1
{char c[5];int i;
};int main()
{printf("%d\n", sizeof(union Un1));return 0;
}

你可能会因为大小是5(char c[5]一共是5字节,比i 4字节大),但实际上:

最大是5没错,但它不是最大对齐数(max(char,int) = 4)的整数倍,进行要进行对齐

通讯录

实现一个通讯录,具备以下功能:

1增加联系人(名字,年龄,性别,电话,地址);

2删除联系人的信息;

3查找联系人的信息;

4更改联系人的信息;

5打印通讯录;

6对通讯录进行排序;

//Contact.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>#define Max_Size 100
#define Max_NameSize 20
#define Max_SexSize 5
#define Max_CallSize 20
#define Max_AddrSize 30enum
{ext,add,del,check,modify,print,sort,
};typedef struct Mess
{char name[Max_NameSize];int age;char sex[Max_SexSize];char call[Max_CallSize];char addr[Max_AddrSize];
}Mess;typedef struct Contact
{int sz;Mess message[Max_Size];
}Contact;void Add(Contact* con);
void Del(Contact* con);
void Check(const Contact* con);
void Modify(Contact* con);
void Print(const Contact* con);
void Sort(Contact* con);//Contact.c
#include"contact.h"void Add(Contact* con)
{assert(con);if (con->sz == Max_Size){printf("Contact Is Full\n");return;}printf("Input Name:");gets(con->message[con->sz].name);//scanf("%s", &con->message[con->sz].name);printf("Input Age:");scanf("%d", &con->message[con->sz].age);//如果输入的是非法字符串?getchar();//清空缓冲区的'\n'printf("Input Sex:");gets(con->message[con->sz].sex);//scanf("%s", &con->message[con->sz].sex);printf("Input Call:");gets(con->message[con->sz].call);//scanf("%s", &con->message[con->sz].call);printf("Input Addr:");gets(con->message[con->sz].addr);//scanf("%s", &con->message[con->sz].addr);con->sz++;printf("Add Finish\n");
}void Print(const Contact* con)
{assert(con);if (con->sz == 0){printf("Contact Is Empty\n");return;}printf("%-20s %-5s %-5s %-11s %-30s\n", "name", "age", "sex", "call", "addr");for (int i = 0; i < con->sz; i++){printf("%-20s %-5d %-5s %-11s %-30s\n",con->message[i].name, con->message[i].age, con->message[i].sex,con->message[i].call,con->message[i].addr);}
}static int Find(const Contact* con,char Name[])
{for (int i = 0; i < con->sz; i++){if (strcmp(con->message[i].name,Name)==0)//用strcmp比{printf("Find Sucess\n");return i;}}printf("Find Error\n");return -1;
}void Del(Contact* con)
{assert(con);char InputName[Max_NameSize];printf("Input Del Name:");gets(InputName);int DelPos = Find(con, InputName);if (DelPos == -1) return;for (int i = DelPos; i < con->sz - 1; i++){con->message[i] = con->message[i + 1];//后一个往前一个覆盖}con->sz--;printf("Del Sucess\n");}void Check(const Contact* con)
{assert(con);printf("Input Check Name:");char Check_Name[Max_NameSize];gets(Check_Name);int pos = Find(con, Check_Name);if (pos == -1) return;printf("Check Result:\n");printf("%-20s %-5s %-5s %-11s %-30s\n", "name", "age", "sex", "call", "addr");printf("%-20s %-5d %-5s %-11s %-30s\n",con->message[pos].name, con->message[pos].age,con->message[pos].sex, con->message[pos].call,con->message[pos].addr);
}void Modify(Contact* con)
{assert(con);char Modify_Name[Max_NameSize];printf("Input Modify Name:");gets(Modify_Name);int pos = Find(con, Modify_Name);if (pos == -1) return;printf("Modify Name:");gets(con->message[pos].name);printf("Input Modify Age:");scanf("%d", &con->message[pos].age);getchar();printf("Input Modify Sex:");gets(con->message[pos].sex);printf("Input Modify call:");gets(con->message[pos].call);printf("Input Modify addr:");gets(con->message[pos].addr);printf("Modify Sucess\n");
}int cmp(const void* a, const void* b)
{return ((*((Contact*)a)).message->age - (*((Contact*)b)).message->age);
}void Sort(Contact* con)
{qsort(con, con->sz, sizeof(con->message[0]), cmp);printf("排序完成\n");
}//test.c
#include"contact.h"void memu()
{printf("********************************\n");printf("*****   0.exit    1.add    *****\n");printf("*****   2.del     3.check  *****\n");printf("*****   4.modify  5.print  *****\n");printf("*****         6sort        *****\n");
}int main()
{Contact con;//对con空间进行清理memset(&con, 0, sizeof(con));int input;do{memu();printf("Please Select:");//防止读到字符串时发生死循环input = 123456;//防止下一次输入的是字符后input还是上一次的值scanf("%d", &input);getchar();//输入字符串时:清空缓冲区的'\n'switch (input){case ext:printf("Process Exit");break;case add:Add(&con);break;case del:Del(&con);break;case check:Check(&con);break;case modify: Modify(&con);break;case print:Print(&con);break;case sort:Sort(&con);break;default:printf("Select Error,Please Try Again\n");break;}}while(input);return 0;
}

但上面的通讯录无法做到:

通讯录满了无法再存信息(学习动态内存管理后解决);

关闭程序时通讯录信息还存在(学习完文件操作后解决);

以上便是全部内容,有问题欢迎在评论区指正,感谢观看!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com