自己做一网站 多做宣传.,网站优化流程图,哈尔滨建站优化定制,长春火车站电话人工服务文章目录 前言Ⅰ 结构体的声明和定义⒈结构体声明⒉结构体定义⒊特殊的声明 Ⅱ 结构体的自引用Ⅲ 结构体初始化Ⅳ 访问结构体成员Ⅴ 结构体内存对齐⒈结构体内存对齐规则⒉分析结构体大小⒊嵌套结构体内存大小⒋内存对齐存在的原因 Ⅵ 修改默认对齐数Ⅶ 结构体传参 前言
C 语言… 文章目录 前言Ⅰ 结构体的声明和定义⒈结构体声明⒉结构体定义⒊特殊的声明 Ⅱ 结构体的自引用Ⅲ 结构体初始化Ⅳ 访问结构体成员Ⅴ 结构体内存对齐⒈结构体内存对齐规则⒉分析结构体大小⒊嵌套结构体内存大小⒋内存对齐存在的原因 Ⅵ 修改默认对齐数Ⅶ 结构体传参 前言
C 语言本身提供了一些基础的内置数据类型例如
char //字符型数据
short //短整型数据
int //整型数据
long //长整型数据
long long //更长的整型数据
float //单精度浮点型数据
double //双精度浮点型数据
......但是很多时候需要描述的对象并没有那么简单这些对象不是简单的一个 int 或者 char 就能完整描述出来的。因为当我们描述这些复杂对象的时候就像自定义函数一样也会有自定义数据类型。
结构的基础知识
结构是一些值的集合这些值称为成员变量。结构的每个成员可以是不同类型的变量。
Ⅰ 结构体的声明和定义
⒈结构体声明
1. 语法格式
struct 结构体名称
{结构体成员 1;结构体成员 2;结构体成员 3;......结构体成员 n;
};2. 结构体的嵌套
在声明结构体时结构体成员即可以是任何一种基本的数据类型也可以是另一个结构体如果是后者那么就相当于是结构体的嵌套。
3. 声明结构体的位置
结构体声明即可以在所有函数的外面也可以单独放在一个函数内部使用。如果是后者则该结构体就指针在该函数中被定义。
4. 声明结构体实例
一本书的结构体声明应该是这样的
struct Book
{char title[120];char author[40];float price;unsigned int date;char publisher[40];
};⒉结构体定义
根据自定义的结构体类型来创建一个结构体变量称为结构体的定义。结构体声明只是进行一个框架的描述定义一个真正的结构体类型变量之前它并不会在内存中分配空间存储数据。
1. 定义结构体变量的语法
struct 结构体名称 结构体变量名2. 定义结构体变量的位置 在声明时定义结构体变量在声明结构体同时定义一个结构体变量此时的结构体变量就是一个全局变量。 在函数内部定义结构体变量在函数内部根据声明的结构体类型定义一个结构体变量此时的结构体变量就是一个局部变量。
struct Book
{char title[120];char author[40];float price;
}book; //1. 此时 book 被定义为全局变量int main() //别把 main 函数不当函数
{struct Book book; //2. 此时 book 被定义为局部变量......return 0;
}⒊特殊的声明
在声明结构体的时候也可以选择进行不完全声明。即省略结构体类型名。
匿名结构体类型
只能在声明时定义结构体变量且该结构体变量只能用一次。
struct
{char title[120];char author[40];float price;
}book;//book 这个结构体变量只能被使用一次Ⅱ 结构体的自引用
结构体自己找到一个与自己同类型的另一个数据称为结构体的自引用。在数据结构中的链表中就会使用这种方式。
链表的基础知识
在内存中并不是所有相同类型的数据都是连续存放在内存中的。有时想要找到那些不知道藏在那个角落里的数据就要使用链表的形式了。链表除了链尾结点外每个结点都包含着指向下一个同类型结点的地址。 链表结点的设计
struct Node
{int data; //数据域该结点本身应该存储的数据struct Node* next; //指针域存储下一个结点的地址
};Ⅲ 结构体初始化
在定义一个变量或数组的时候可以对其进行初始化。
int a 520;
int arr { 5,2,0 };同样的在定义结构体变量的时候也可以对其进行初始化。
struct Book
{char title[128];char author[40];float price;
};int main()
{struct Book book { 《C primer plus》,史蒂芬·普拉达,108};return 0;
}结构体嵌套初始化
当结构体 A 中包含另一个结构体 B 时对其 B 要在对 A 初始化的大括号内嵌套一个大括号用以初始化 B。
struct Name
{char title[128];char author[40];
};struct Book
{struct Name name;float price;
};int main()
{struct Book book { {《C primer plus》,史蒂芬·普拉达},108 };return 0;
}Ⅳ 访问结构体成员
访问结构体的内容有两种方式 结构体变量 . 结构体成员结构体变量使用 . 操作符访问结构体成员的内容。结构体指针 - 结构体成员指向结构体变量的结构体指针使用 - 操作符访问结构体成员的内容。 Ⅴ 结构体内存对齐
计算结构体的大小
结构体的大小和结构体内的每个结构体成员的类型有关。但是不同数据类型的结构体成员在结构体内的位置也影响着整个结构体的大小。结构体的大小并不是把所有结构体成员的大小加起来。
struct S1
{char a; //1 字节int b; //4 字节char c; //1 字节
};struct S2
{char a; //1 字节char b; //1 字节int c; //4 字节
};S1 和 S2 的结构体成员都是相同的只是成员位置的不同就直接导致结构体的大小相差了 4 个字节。结果不一样的原因是编译器对结构体的成员进行了对齐说白了对齐是为了让 CPU 可以更快的读取和处理数据。
⒈结构体内存对齐规则
第一个结构体成员位于结构通变量偏移量为 0 的位置。 偏移量 其他成员变量要对齐到某个数字对齐数的整数倍的地址。结构体的总大小为最大对齐数每个成员变量都有一个对齐数的整数倍。如果嵌套了结构体的情况嵌套的结构体对齐到字节的最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数包含嵌套结构体的对齐数的整数倍。
偏移量
开辟空间时某个字节的地址相对于起始地址所偏移的字节个数称之为偏移量。例如 第一个字节的的地址相较于起始位置的地址一个字节都没偏移所以第一个字节处为偏移量 0。 第二个字节的地址相较于起始位置的地址偏移了一个字节第二个字节的空间为偏移量 1 处。
对齐数
编辑器默认VS 中的对齐数默认为 8的一个对齐数与该成员大小的较小值。例如 int 变量的对齐数为 4比默认对齐数 8 小所以此时的对齐数为 4。 char 变量的对齐数为 1比默认对齐数 8 小所以此时的对齐数为 1。 其他编译器上对齐数就是变量的自身大小。
⒉分析结构体大小 1. 分析 s1 的大小 2. 分析 s2 的大小 ⒊嵌套结构体内存大小
结构体嵌套时的内存对齐规则
如果嵌套了结构体的情况嵌套的结构体对齐到字节的最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数包含嵌套结构体的对齐数的整数倍。
struct S3
{double a; //8 字节char b; //1 字节int c; //4 字节
};struct S4
{char d; //1 字节struct S3 s3; //16字节double e; //8 字节
};s3 的大小看完 s1 和 s2 的分析之后后应该能自己分析出来就不过多赘述了。主要是分析嵌套了 S3 的 s4 的大小是怎么来的。
分析 s4 的大小 ⒋内存对齐存在的原因
1. 平台原因移植原因
不是所有的硬件平台都能访问任意地址上的任意数据的某些硬件平台只能在某些地址处取某些特定类型的数据否则抛出硬件异常。
2 性能原因
数据结构尤其是栈应该尽可能地在自然边界上对齐。因为为了访问未对齐的内存处理器需要作两次内存访问而对齐的内存访问仅需一次访问。
如何同时满足内存对齐节省空间
设计结构体的时候将占用空间小的成员放在一起。 Ⅵ 修改默认对齐数
使用 #pragma 这个预处理指令可以修改默认对齐数。
struct S //此时默认对齐数为 8
{int i; //偏移量 0 ~ 3double d; //偏移量 8 ~ 15
};int main()
{printf(%d\n, sizeof(struct S)); //16return 0;
}1. 修改默认对齐数
#pragma pack(4) //将默认对齐数改为 4
struct S
{int i; //偏移量 0 ~ 3double d; //偏移量 4 ~ 11
};
#pragma pack() //将默认对齐数回复成 8int main()
{printf(%d\n, sizeof(struct S)); //12return 0;
}2. 取消内存对齐
将默认对齐数改为 1这样不管是什么类型数据的对齐数和默认对齐数比较都是默认对齐数小就会直接使用默认对齐数为当前对齐数。
#pragma pack(1) //只要是 1 的倍数都可以存放
struct S
{char a;//0int b;//1 ~ 4char c;//5
};
#pragma pack() //将默认对齐数回复成 8int main()
{printf(%d\n, sizeof(struct S));//6return 0;
}Ⅶ 结构体传参
结构体传参分为传结构体变量和传结构体地址。结构体传参时尽量选择传址调用传结构体地址。
struct Book
{char title[128];char author[40];
};void print1(struct Book book)
{printf(|-----传结构体变量-----|\n);printf(书名%s\n, book.title);printf(作者%s\n, book.author);printf(|----------------------|\n);
}void print2(struct Book* p)
{printf(|-----传结构体地址-----|\n);printf(书名%s\n, p-title);printf(作者%s\n, p-author);printf(|----------------------|\n);
}int main()
{struct Book book { 《C primer plus》,史蒂芬·普拉达};print1(book); //传结构体变量print2(book);//传结构体地址return 0;
}