#include<stdio.h>#include<string.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~255,i的初始值为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和prt2,num个字节的变量/*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-128,128的补码/*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,当i=255时,再加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,当i=0时,再减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
- 例如:单精度下,E=127表示实际指数0,E=128表示实际指数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可能返回false
3. 实际应用注意事项
- 避免直接比较浮点数相等,应使用误差范围比较
- 注意单精度和双精度的精度差异
- 了解平台相关的浮点运算优化
结构体内存对齐难点总结
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/Clang)
struct__attribute__((packed))MyStruct{// 紧凑排列,无填充字节};4. 实际应用建议
- 性能优先:保持自然对齐,按成员大小降序排列
- 空间优先:使用
#pragma pack(1)或packed属性 - 网络传输:序列化时考虑字节序和对齐问题
- 文件存储:使用固定大小的结构体,避免依赖编译器对齐
总结对比
| 特性 | 浮点数存储 | 结构体内存对齐 |
|---|---|---|
| 核心标准 | IEEE 754 | 编译器实现相关 |
| 主要目的 | 科学计算精度 | 内存访问效率 |
| 难点类型 | 数学表示 | 内存布局规则 |
| 平台影响 | 标准统一 | 差异较大 |
| 优化策略 | 精度控制 | 成员顺序调整 |
核心理解:浮点数存储是"用精度换范围"的数学优化,而内存对齐是"用空间换时间"的性能优化。两者都是计算机系统中重要的底层优化技术,理解它们有助于编写高效、可靠的代码。