2 解引用
#include <stdio.h>
int main() {
static int a[5] = {1, 2, 3, 4, 5};
int b = 6;
int *p = (int *)((int*)&a + 1);
printf("*(a + 1) = %d\n", *(a + 1));
printf("*p-- = %d\n", *p--);
printf("*p = %d\n", *p);
printf("*p-- = %d\n", *p--);
printf("*p = %d\n", *p);
return 0;
}
解答:
*(a + 1) = 2
*p-- = 2
*p = 1
p-- = 1
p = 0 或者 随机数
关键点总结:
指针类型转换的影响:(int)&a 将数组指针转换为普通 int,导致指针算术以 int 为单位,而非整个数组。
内存布局:p 实际指向 a[1],而非 b。
递减操作 p--:先返回当前值,再移动指针,导致输出顺序为 2 → 1 → 1 → 随机数
3大小端
Running the following program on 32-bit (little-endian) CPU, the output is (B)
#include <stdio.h>
#include <stdint.h>
int main() {
int a = 0xddccbbaa;
char *b = (char *)&a;
printf("0x%x\n", *(b + 2));
return 0;
}
0xbb
0xcc
0xdd
0xaa
解答:
小端:高字节在高地址,低字节在低地址
地址(值):b(0xaa),b+1(0xbb),b+2(0xcc),b+3(0xdd)
一、大小端存储(Endianness)
大小端是计算机系统中多字节数据在内存中的存储顺序,核心区别在于“高位字节”和“低位字节”的存放位置。
1. 基本概念
- 字节(Byte):计算机存储的基本单位(8位),多字节数据(如int、long等)由多个字节组成。
- 高位字节与低位字节:以16进制数
0x12345678为例(假设为32位int):- 高位字节:
0x12(权重最高)、0x34 - 低位字节:
0x56、0x78(权重最低)
- 高位字节:
2. 大端存储(Big-Endian)
-
定义:高位字节存放在低地址,低位字节存放在高地址(类似人类读写数字的顺序:从高位到低位)。
-
示例:32位数据
0x12345678的存储(假设起始地址为0x0000):内存地址 存储内容 0x0000 0x12 0x0001 0x34 0x0002 0x56 0x0003 0x78 -
典型设备:网络协议(TCP/IP默认大端)、部分嵌入式处理器(如PowerPC)。
3. 小端存储(Little-Endian)
-
定义:低位字节存放在低地址,高位字节存放在高地址(与人类读写顺序相反)。
-
示例:同样
0x12345678的存储(起始地址0x0000):内存地址 存储内容 0x0000 0x78 0x0001 0x56 0x0002 0x34 0x0003 0x12 -
典型设备:x86架构CPU(如Intel、AMD)、STM32等多数单片机。
4. 大小端的影响与应用
- 跨平台数据交互:若两台设备大小端不同,直接传输多字节数据会导致解析错误(如
0x1234在小端设备中会被大端设备误读为0x3412),需通过“网络字节序”(大端)统一格式。 - C语言中判断大小端:可通过联合体(Union)判断,例如:
#include <stdio.h> union EndianTest { int a; char b; }; int main() { union EndianTest test; test.a = 1; // 0x00000001(32位) if (test.b == 1) printf("小端存储\n"); // 低地址存0x01 else printf("大端存储\n"); return 0; }
二、内存的增长方向(栈的生长方向)
内存的“向上增长”或“向下增长”通常指栈(Stack)的内存分配方向,与堆(Heap)的分配方向相反。
1. 栈(Stack)的生长方向
- 栈:用于存储函数局部变量、函数参数、返回地址等,由编译器自动管理,遵循“先进后出”(FILO)原则。
- 向下增长:多数系统(如x86、ARM)中,栈从高地址向低地址生长(即新分配的栈内存地址比之前更小)。
- 示例:调用函数时,先压入参数(高地址),再压入返回地址(低地址),栈顶指针(SP)逐渐减小。
- 向上增长:少数架构中栈从低地址向高地址生长(如新分配的栈内存地址更大),但非常少见。
2. 堆(Heap)的生长方向
- 堆:用于动态内存分配(如
malloc、new),由程序员手动管理,生长方向与栈相反:- 若栈向下增长,堆通常向上增长(从低地址向高地址扩展),两者之间有空闲内存区域,避免冲突。
3. 示例:栈向下增长的验证
#include <stdio.h>
void test(int a) {
int b;
printf("参数a的地址:%p\n", &a); // 高地址
printf("局部变量b的地址:%p\n", &b); // 低地址(栈向下增长)
}
int main() {
int c;
test(10);
printf("main中变量c的地址:%p\n", &c); // 比test中的变量地址更高
return 0;
}
输出结果(x86架构):
参数a的地址:0x7ffd9a8b9abc
局部变量b的地址:0x7ffd9a8b9ab8 // b的地址 < a的地址,栈向下增长
main中变量c的地址:0x7ffd9a8b9ad4 // c的地址 > test中的变量地址
4. 注意事项
- 内存的“增长方向”仅针对栈,堆的分配方向通常固定为向上增长,与栈方向相反。
- 不同架构可能有差异,但x86、ARM等主流架构均为“栈向下增长,堆向上增长”。
总结
- 大小端:决定多字节数据的存储顺序(小端更常见,大端用于网络协议)。
- 内存增长方向:主要指栈的生长方向(多数为向下增长),与堆的向上增长形成互补,避免内存冲突。
浙公网安备 33010602011771号