#includestdio.h#includestring.h/*int check_sys() { int i 1; return (*(char*)i); }*///char*占1个字节int*占4个字节*(char*)i表示取i的地址然后强制类型转换为char*再取值返回的是i的低位字节的值如果是小端模式则返回1如果是大端模式则返回0//用char*类型指针去访问int类型的变量访问的是int类型变量的低位字节如果是小端模式则低位字节的值为1如果是大端模式则低位字节的值为0/*unsigned char i 0;*///unsigned char占1个字节范围是0~255i的初始值为0intmain(){//memcpy void * memcpy ( void * destination, const void * source, size_t num );//从source处拷贝num个字节的数据到destination/*int arr1[] { 1,2,3,4,5,6,7,8,9,10 }; int arr2[10] { 0 }; memcpy(arr2, arr1, 20); int i 0; for (i 0; i 10; i) { printf(%d , arr2[i]); }*///memmove void * memmove ( void * destination, const void * source, size_t num );可重叠/*int arr1[] { 1,2,3,4,5,6,7,8,9,10 }; memmove(arr1 2, arr1, 20); int i 0; for (i 0; i 10; i) { printf(%d , arr1[i]); }*///memset void * memset ( void * ptr, int value, size_t num );把指针内的num个字节的字符变为value//memcmp int memcmp ( const void * ptr1, const void * ptr2, size_t num );对比prt1和prt2num个字节的变量/*char buffer1[] DWgaOtP12df0; char buffer2[] DWGAOTP12DF0; int n; n memcmp(buffer1, buffer2, sizeof(buffer1)); if (n 0) printf(%s 大于%s.\n, buffer1, buffer2); else if (n 0) printf(%s 小于%s.\n, buffer1, buffer2); else printf(%s 和%s⼀样.\n, buffer1, buffer2);*///数据在内存中的储存/*int a 0x11223344; printf(0x%p\n, a);*///对于32位的整型变量a内存中储存的顺序是44 33 22 11大小端储存模式//大端储存模式 是指数据的低位字节内容保存在内存的⾼地址处⽽数据的⾼位字节内容保存在内存的低地址处。//小端储存模式 是指数据的低位字节内容保存在内存的低地址处⽽数据的⾼位字节内容保存在内存的⾼地址处。/*int ret check_sys(); if (ret 1) { printf(大端\n); } else { printf(大端\n); }*//*char a -1; signed char b -1; unsigned char c -1; printf(a %d, b %d, c %d, a, b, c);*///a -1, b -1, c 255//char类型的变量a和b都是有符号的所以它们的值都是-1而c是无符号的所以它的值是255/*char a -128; printf(%u\n, a);*///4294967296-128128的补码/*char a 128; printf(%u\n, a);*///超出范围是1 0000 0000 是-128的补码打印出来是4294967168/*char a[1000]; int i; for (i 0; i 1000; i) { a[i] -1 - i; } printf(%d, strlen(a));*///strlen()函数计算字符串的长度遇到\0结束-1是0xff-2是0xfe-3是0xfd...-128是0x80-129是0x7f...-255是0x00所以strlen(a) 255//int count 0;// for (i 0; i 255; i)// {// printf(hello world\n);//打印256次hello world但是由于i是unsigned char类型范围是0~255当i255时再加1就会溢出变为0所以循环会继续执行打印hello world直到i再次达到255死循环。// count;// }// printf(%d\n, count);/*unsigned int i; for (i 9; i 0; i--) { printf(%u\n, i); }*///i是unsigned int类型范围是0~4294967295当i0时再减1就会溢出变为4294967295所以循环会继续执行打印4294967295直到i再次达到0死循环。//int a[4] { 1, 2, 3, 4 };//int* ptr1 (int*)(a 1);//a是数组a的地址a 1是数组a的地址加上一个数组的大小即指向数组a的下一个地址强制类型转换为int*即指向数组a的下一个元素的地址//int* ptr2 (int*)((int)a 1);//(int)a是数组a的首元素的地址(int)a 1是数组a的首元素的地址加上一个字节即指向数组a的首元素的下一个字节强制类型转换为int*即指向数组a的首元素的下一个字节的地址//printf(%x, %x, ptr1[-1], *ptr2);//int n 9;//float* pFloat (float*)n;////printf(n的值为%d\n, n);// printf(*pFloat的值为%f\n, *pFloat);// * pFloat 9.0;//printf(n的值为%d\n, n);// printf(*pFloat的值为%f\n, *pFloat);//IEEE754//结构体内存对齐structS1{charc1;inti;charc2;};printf(%zu\n,sizeof(structS1));//%zu是打印size_t类型的格式化字符串sizeof(struct S1)是计算结构体S1的大小结果是12因为结构体S1中有一个char类型的成员c1占1个字节一个int类型的成员i占4个字节一个char类型的成员c2占1个字节但是由于结构体内存对齐的原因结构体S1的大小是12个字节structS2{charc1;charc2;inti;};printf(%zu\n,sizeof(structS2));//%zu是打印size_t类型的格式化字符串sizeof(struct S2)是计算结构体S2的大小结果是8因为结构体S2中有两个char类型的成员c1和c2占2个字节一个int类型的成员i占4个字节但是由于结构体内存对齐的原因结构体S2的大小是8个字节structS3{doubled;charc;inti;};printf(%zu\n,sizeof(structS3));//%zu是打印size_t类型的格式化字符串sizeof(struct S3)是计算结构体S3的大小结果是16因为结构体S3中有一个double类型的成员d占8个字节一个char类型的成员c占1个字节一个int类型的成员i占4个字节但是由于结构体内存对齐的原因结构体S3的大小是16个字节structS4{charc1;structS3s3;doubled;};printf(%zu\n,sizeof(structS4));//%zu是打印size_t类型的格式化字符串sizeof(struct S4)是计算结构体S4的大小结果是32因为结构体S4中有一个char类型的成员c1占1个字节一个结构体类型的成员s3占16个字节一个double类型的成员d占8个字节但是由于结构体内存对齐的原因结构体S4的大小是32个字节//#pragma修改默认对齐数//#pragma修改默认对齐数// 难点总结 // 1. 浮点数存储过程 (IEEE 754标准) 难点// - 理解三个组成部分符号位(S)、指数位(E)、尾数位(M)// - 指数E采用移码表示实际指数 E - 127(单精度)/1023(双精度)// - 尾数M采用隐含1表示实际尾数 1.M (规格化数) 或 0.M (非规格化数)// - 特殊值处理E全0(非规格化数)、E全1(无穷大/NaN)// - 精度损失二进制无法精确表示某些十进制小数(如0.1)// - 比较陷阱浮点数直接比较可能因精度问题出错应使用误差范围比较// 2. 结构体内存对齐 难点// - 对齐规则// 1) 第一个成员在偏移量0处// 2) 其他成员对齐到对齐数的整数倍地址// (对齐数 min(编译器默认对齐数, 成员自身大小))// 3) 结构体总大小必须是最大对齐数的整数倍// 4) 嵌套结构体先按自身规则对齐再按最大对齐数对齐//// - 内存浪费为满足对齐要求编译器会插入填充字节// - 平台差异不同编译器/平台可能有不同的默认对齐数// - 性能影响对齐访问可提高CPU读取效率但可能增加内存占用// - 位域对齐位域成员有特殊的对齐规则// - #pragma pack可修改默认对齐数但可能影响跨平台兼容性// 关键理解内存对齐是用空间换时间的典型优化策略return0;}浮点数存储过程 (IEEE 754标准) 难点总结1. 核心组成部分符号位(S)1位0表示正数1表示负数指数位(E)8位(单精度)或11位(双精度)采用移码表示尾数位(M)23位(单精度)或52位(双精度)采用隐含1表示2. 关键难点解析指数E的移码表示实际指数 E - 偏置值单精度偏置值 127双精度偏置值 1023例如单精度下E127表示实际指数0E128表示实际指数1尾数M的隐含1规则规格化数(E不全为0且不全为1)实际尾数 1.M非规格化数(E全为0)实际尾数 0.M用于表示接近0的极小值这种设计节省了1位精度但增加了理解难度特殊值处理E全0且M全0表示±0取决于符号位E全0且M不全0非规格化数用于表示接近0的极小值E全1且M全0表示无穷大±∞E全1且M不全0表示NaN非数字精度损失问题二进制无法精确表示某些十进制小数如0.1浮点数运算可能产生累积误差比较陷阱0.1 0.2 0.3可能返回false3. 实际应用注意事项避免直接比较浮点数相等应使用误差范围比较注意单精度和双精度的精度差异了解平台相关的浮点运算优化结构体内存对齐难点总结1. 对齐规则详解基本对齐规则第一个成员在结构体偏移量为0的地址处其他成员对齐到对齐数的整数倍地址对齐数 min(编译器默认对齐数, 成员自身大小)常见编译器默认对齐数4或8字节结构体总大小必须是最大对齐数的整数倍嵌套结构体先按自身规则对齐再按最大对齐数对齐示例分析structS1{charc1;// 偏移0大小1inti;// 偏移4对齐到4的倍数大小4charc2;// 偏移8大小1};// 总大小12最大对齐数4的倍数2. 关键难点内存浪费问题编译器自动插入填充字节以满足对齐要求成员顺序影响结构体大小优化技巧按大小降序排列平台差异不同编译器可能有不同的默认对齐数不同CPU架构可能有不同的对齐要求跨平台开发时需要特别注意性能影响空间换时间对齐访问可提高CPU读取效率现代CPU通常支持非对齐访问但性能会下降缓存行对齐可进一步提升性能位域对齐位域成员有特殊的对齐规则位域不能跨存储单元边界不同编译器对位域的实现可能有差异3. 对齐控制#pragma pack指令#pragmapack(1)// 设置对齐数为1字节#pragmapack()// 恢复默认对齐可减少内存占用但可能降低性能影响跨平台兼容性慎用于需要与其他系统交互的数据结构属性声明GCC/Clangstruct__attribute__((packed))MyStruct{// 紧凑排列无填充字节};4. 实际应用建议性能优先保持自然对齐按成员大小降序排列空间优先使用#pragma pack(1)或packed属性网络传输序列化时考虑字节序和对齐问题文件存储使用固定大小的结构体避免依赖编译器对齐总结对比特性浮点数存储结构体内存对齐核心标准IEEE 754编译器实现相关主要目的科学计算精度内存访问效率难点类型数学表示内存布局规则平台影响标准统一差异较大优化策略精度控制成员顺序调整核心理解浮点数存储是用精度换范围的数学优化而内存对齐是用空间换时间的性能优化。两者都是计算机系统中重要的底层优化技术理解它们有助于编写高效、可靠的代码。
内存管理结构体
发布时间:2026/6/25 12:23:30
#includestdio.h#includestring.h/*int check_sys() { int i 1; return (*(char*)i); }*///char*占1个字节int*占4个字节*(char*)i表示取i的地址然后强制类型转换为char*再取值返回的是i的低位字节的值如果是小端模式则返回1如果是大端模式则返回0//用char*类型指针去访问int类型的变量访问的是int类型变量的低位字节如果是小端模式则低位字节的值为1如果是大端模式则低位字节的值为0/*unsigned char i 0;*///unsigned char占1个字节范围是0~255i的初始值为0intmain(){//memcpy void * memcpy ( void * destination, const void * source, size_t num );//从source处拷贝num个字节的数据到destination/*int arr1[] { 1,2,3,4,5,6,7,8,9,10 }; int arr2[10] { 0 }; memcpy(arr2, arr1, 20); int i 0; for (i 0; i 10; i) { printf(%d , arr2[i]); }*///memmove void * memmove ( void * destination, const void * source, size_t num );可重叠/*int arr1[] { 1,2,3,4,5,6,7,8,9,10 }; memmove(arr1 2, arr1, 20); int i 0; for (i 0; i 10; i) { printf(%d , arr1[i]); }*///memset void * memset ( void * ptr, int value, size_t num );把指针内的num个字节的字符变为value//memcmp int memcmp ( const void * ptr1, const void * ptr2, size_t num );对比prt1和prt2num个字节的变量/*char buffer1[] DWgaOtP12df0; char buffer2[] DWGAOTP12DF0; int n; n memcmp(buffer1, buffer2, sizeof(buffer1)); if (n 0) printf(%s 大于%s.\n, buffer1, buffer2); else if (n 0) printf(%s 小于%s.\n, buffer1, buffer2); else printf(%s 和%s⼀样.\n, buffer1, buffer2);*///数据在内存中的储存/*int a 0x11223344; printf(0x%p\n, a);*///对于32位的整型变量a内存中储存的顺序是44 33 22 11大小端储存模式//大端储存模式 是指数据的低位字节内容保存在内存的⾼地址处⽽数据的⾼位字节内容保存在内存的低地址处。//小端储存模式 是指数据的低位字节内容保存在内存的低地址处⽽数据的⾼位字节内容保存在内存的⾼地址处。/*int ret check_sys(); if (ret 1) { printf(大端\n); } else { printf(大端\n); }*//*char a -1; signed char b -1; unsigned char c -1; printf(a %d, b %d, c %d, a, b, c);*///a -1, b -1, c 255//char类型的变量a和b都是有符号的所以它们的值都是-1而c是无符号的所以它的值是255/*char a -128; printf(%u\n, a);*///4294967296-128128的补码/*char a 128; printf(%u\n, a);*///超出范围是1 0000 0000 是-128的补码打印出来是4294967168/*char a[1000]; int i; for (i 0; i 1000; i) { a[i] -1 - i; } printf(%d, strlen(a));*///strlen()函数计算字符串的长度遇到\0结束-1是0xff-2是0xfe-3是0xfd...-128是0x80-129是0x7f...-255是0x00所以strlen(a) 255//int count 0;// for (i 0; i 255; i)// {// printf(hello world\n);//打印256次hello world但是由于i是unsigned char类型范围是0~255当i255时再加1就会溢出变为0所以循环会继续执行打印hello world直到i再次达到255死循环。// count;// }// printf(%d\n, count);/*unsigned int i; for (i 9; i 0; i--) { printf(%u\n, i); }*///i是unsigned int类型范围是0~4294967295当i0时再减1就会溢出变为4294967295所以循环会继续执行打印4294967295直到i再次达到0死循环。//int a[4] { 1, 2, 3, 4 };//int* ptr1 (int*)(a 1);//a是数组a的地址a 1是数组a的地址加上一个数组的大小即指向数组a的下一个地址强制类型转换为int*即指向数组a的下一个元素的地址//int* ptr2 (int*)((int)a 1);//(int)a是数组a的首元素的地址(int)a 1是数组a的首元素的地址加上一个字节即指向数组a的首元素的下一个字节强制类型转换为int*即指向数组a的首元素的下一个字节的地址//printf(%x, %x, ptr1[-1], *ptr2);//int n 9;//float* pFloat (float*)n;////printf(n的值为%d\n, n);// printf(*pFloat的值为%f\n, *pFloat);// * pFloat 9.0;//printf(n的值为%d\n, n);// printf(*pFloat的值为%f\n, *pFloat);//IEEE754//结构体内存对齐structS1{charc1;inti;charc2;};printf(%zu\n,sizeof(structS1));//%zu是打印size_t类型的格式化字符串sizeof(struct S1)是计算结构体S1的大小结果是12因为结构体S1中有一个char类型的成员c1占1个字节一个int类型的成员i占4个字节一个char类型的成员c2占1个字节但是由于结构体内存对齐的原因结构体S1的大小是12个字节structS2{charc1;charc2;inti;};printf(%zu\n,sizeof(structS2));//%zu是打印size_t类型的格式化字符串sizeof(struct S2)是计算结构体S2的大小结果是8因为结构体S2中有两个char类型的成员c1和c2占2个字节一个int类型的成员i占4个字节但是由于结构体内存对齐的原因结构体S2的大小是8个字节structS3{doubled;charc;inti;};printf(%zu\n,sizeof(structS3));//%zu是打印size_t类型的格式化字符串sizeof(struct S3)是计算结构体S3的大小结果是16因为结构体S3中有一个double类型的成员d占8个字节一个char类型的成员c占1个字节一个int类型的成员i占4个字节但是由于结构体内存对齐的原因结构体S3的大小是16个字节structS4{charc1;structS3s3;doubled;};printf(%zu\n,sizeof(structS4));//%zu是打印size_t类型的格式化字符串sizeof(struct S4)是计算结构体S4的大小结果是32因为结构体S4中有一个char类型的成员c1占1个字节一个结构体类型的成员s3占16个字节一个double类型的成员d占8个字节但是由于结构体内存对齐的原因结构体S4的大小是32个字节//#pragma修改默认对齐数//#pragma修改默认对齐数// 难点总结 // 1. 浮点数存储过程 (IEEE 754标准) 难点// - 理解三个组成部分符号位(S)、指数位(E)、尾数位(M)// - 指数E采用移码表示实际指数 E - 127(单精度)/1023(双精度)// - 尾数M采用隐含1表示实际尾数 1.M (规格化数) 或 0.M (非规格化数)// - 特殊值处理E全0(非规格化数)、E全1(无穷大/NaN)// - 精度损失二进制无法精确表示某些十进制小数(如0.1)// - 比较陷阱浮点数直接比较可能因精度问题出错应使用误差范围比较// 2. 结构体内存对齐 难点// - 对齐规则// 1) 第一个成员在偏移量0处// 2) 其他成员对齐到对齐数的整数倍地址// (对齐数 min(编译器默认对齐数, 成员自身大小))// 3) 结构体总大小必须是最大对齐数的整数倍// 4) 嵌套结构体先按自身规则对齐再按最大对齐数对齐//// - 内存浪费为满足对齐要求编译器会插入填充字节// - 平台差异不同编译器/平台可能有不同的默认对齐数// - 性能影响对齐访问可提高CPU读取效率但可能增加内存占用// - 位域对齐位域成员有特殊的对齐规则// - #pragma pack可修改默认对齐数但可能影响跨平台兼容性// 关键理解内存对齐是用空间换时间的典型优化策略return0;}浮点数存储过程 (IEEE 754标准) 难点总结1. 核心组成部分符号位(S)1位0表示正数1表示负数指数位(E)8位(单精度)或11位(双精度)采用移码表示尾数位(M)23位(单精度)或52位(双精度)采用隐含1表示2. 关键难点解析指数E的移码表示实际指数 E - 偏置值单精度偏置值 127双精度偏置值 1023例如单精度下E127表示实际指数0E128表示实际指数1尾数M的隐含1规则规格化数(E不全为0且不全为1)实际尾数 1.M非规格化数(E全为0)实际尾数 0.M用于表示接近0的极小值这种设计节省了1位精度但增加了理解难度特殊值处理E全0且M全0表示±0取决于符号位E全0且M不全0非规格化数用于表示接近0的极小值E全1且M全0表示无穷大±∞E全1且M不全0表示NaN非数字精度损失问题二进制无法精确表示某些十进制小数如0.1浮点数运算可能产生累积误差比较陷阱0.1 0.2 0.3可能返回false3. 实际应用注意事项避免直接比较浮点数相等应使用误差范围比较注意单精度和双精度的精度差异了解平台相关的浮点运算优化结构体内存对齐难点总结1. 对齐规则详解基本对齐规则第一个成员在结构体偏移量为0的地址处其他成员对齐到对齐数的整数倍地址对齐数 min(编译器默认对齐数, 成员自身大小)常见编译器默认对齐数4或8字节结构体总大小必须是最大对齐数的整数倍嵌套结构体先按自身规则对齐再按最大对齐数对齐示例分析structS1{charc1;// 偏移0大小1inti;// 偏移4对齐到4的倍数大小4charc2;// 偏移8大小1};// 总大小12最大对齐数4的倍数2. 关键难点内存浪费问题编译器自动插入填充字节以满足对齐要求成员顺序影响结构体大小优化技巧按大小降序排列平台差异不同编译器可能有不同的默认对齐数不同CPU架构可能有不同的对齐要求跨平台开发时需要特别注意性能影响空间换时间对齐访问可提高CPU读取效率现代CPU通常支持非对齐访问但性能会下降缓存行对齐可进一步提升性能位域对齐位域成员有特殊的对齐规则位域不能跨存储单元边界不同编译器对位域的实现可能有差异3. 对齐控制#pragma pack指令#pragmapack(1)// 设置对齐数为1字节#pragmapack()// 恢复默认对齐可减少内存占用但可能降低性能影响跨平台兼容性慎用于需要与其他系统交互的数据结构属性声明GCC/Clangstruct__attribute__((packed))MyStruct{// 紧凑排列无填充字节};4. 实际应用建议性能优先保持自然对齐按成员大小降序排列空间优先使用#pragma pack(1)或packed属性网络传输序列化时考虑字节序和对齐问题文件存储使用固定大小的结构体避免依赖编译器对齐总结对比特性浮点数存储结构体内存对齐核心标准IEEE 754编译器实现相关主要目的科学计算精度内存访问效率难点类型数学表示内存布局规则平台影响标准统一差异较大优化策略精度控制成员顺序调整核心理解浮点数存储是用精度换范围的数学优化而内存对齐是用空间换时间的性能优化。两者都是计算机系统中重要的底层优化技术理解它们有助于编写高效、可靠的代码。