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
    • 低位字节:0x560x78(权重最低)

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)的生长方向

  • :用于动态内存分配(如mallocnew),由程序员手动管理,生长方向与栈相反:
    • 若栈向下增长,堆通常向上增长(从低地址向高地址扩展),两者之间有空闲内存区域,避免冲突。

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等主流架构均为“栈向下增长,堆向上增长”。

总结

  • 大小端:决定多字节数据的存储顺序(小端更常见,大端用于网络协议)。
  • 内存增长方向:主要指栈的生长方向(多数为向下增长),与堆的向上增长形成互补,避免内存冲突。