C语言数据在内存中如何存储?一文搞懂大小端与对齐规则

 更新时间:2026年10月08日 08:44:20   作者:yingjuxia.com  
想彻底搞懂C语言数据在内存中的存储方式吗?本文从内存基本概念讲起,涵盖基本类型、大小端判断、结构体内存对齐、共用体和位域等核心知识,并附带验证代码,帮你轻松掌握底层存储原理,不再为指针和内存问题头疼

C语言——数据在内存中的存储

在 C 语言中,理解“数据到底是怎么存在内存里的”是非常核心的一环。这直接影响到你对指针、内存对齐、字节序、大小端、结构体布局、联合体、位域等很多重要概念的理解。

下面从最基础开始,一步步把 C 语言中各种数据类型在内存中的真实存储方式讲清楚。

1. 内存的基本概念(先搞清楚这几点)

  • 内存是以**字节(byte)**为单位编址的
  • 每个字节有唯一的地址(通常用十六进制表示)
  • 现代计算机基本都是8 bit = 1 byte
  • 内存地址通常按从小到大增长(低地址 → 高地址)
  • 大多数系统采用小端序(little-endian),少数采用大端序(big-endian)

2. 基本数据类型在内存中的存储(以 64 位系统为例)

类型常见大小(字节)对齐要求(典型)取值范围(有符号)内存表示特点
char / signed char11-128 ~ 127直接存 ASCII 或数值
unsigned char110 ~ 255—
short22-32768 ~ 32767按小端/大端存 2 字节
unsigned short220 ~ 65535—
int44-2^31 ~ 2^31-14 字节整数
unsigned int440 ~ 2^32-1—
long8(64位系统)8-2^63 ~ 2^63-1视平台(32位系统可能是 4 字节)
long long88-2^63 ~ 2^63-1固定 8 字节
float44≈ ±1.18e-38 ~ ±3.4e38IEEE 754 单精度浮点
double88≈ ±2.23e-308 ~ ±1.79e308IEEE 754 双精度浮点
long double12/16(视平台)8/16更高精度视编译器与平台(x86 常用 80 位扩展精度)
指针(*)4(32位)/ 8(64位)4/8—存储内存地址

3. 小端序 vs 大端序(最容易混淆的部分)

小端序(Little-Endian):低位字节存低地址(目前绝大多数 x86、x86_64、ARM 都是小端)

大端序(Big-Endian):高位字节存低地址(一些网络协议、PowerPC、SPARC 常用)

例子:int x = 0x12345678;

在内存中的存储(假设地址从 0x1000 开始):

地址小端序(主流)大端序
0x10007812
0x10015634
0x10023456
0x10031278

怎么判断当前系统是小端还是大端?

#include <stdio.h>

int is_little_endian() {
    int num = 1;
    return *(char*)&num == 1;  // 如果低地址存的是 01,则小端
}

int main() {
    printf("是小端序吗? %s\n", is_little_endian() ? "是" : "否");
    return 0;
}

4. 结构体在内存中的存储(内存对齐)

C 语言为了提高 CPU 访问效率,会对结构体成员进行内存对齐(padding)。

规则(常见编译器默认):

  1. 结构体整体大小 = 最后一个成员偏移 + 最后一个成员大小 + 填充字节
  2. 每个成员的起始地址 = 该成员大小的倍数(或结构体最大成员大小的倍数)
  3. 结构体总大小必须是结构体最大成员大小的倍数(padding 到对齐边界)

示例:

struct A {
    char a;      // 1 字节
    int b;       // 4 字节
    short c;     // 2 字节
};

struct B {
    char a;      // 1
    short c;     // 2
    int b;       // 4
};

在 64 位系统 + 4 字节对齐下的布局:

  • struct A:1 + 3(填充) + 4 + 2 + 2(填充) = 12 字节
  • struct B:1 + 1(填充) + 2 + 4 = 8 字节

如何关闭/控制对齐?

#pragma pack(1)      // 1 字节对齐(无填充)
#pragma pack()       // 恢复默认

__attribute__((packed))  // gcc/clang 方式

5. 共用体(union)在内存中的存储

共用体所有成员共享同一块内存,大小等于最大成员的大小。

union Data {
    int i;       // 4 字节
    float f;     // 4 字节
    char c[8];   // 8 字节
};

union Data d;
d.i = 0x12345678;
printf("%x\n", d.c[0]);   // 小端下可能是 78

6. 位域(Bit-field)的内存布局

struct Flags {
    unsigned int f1 : 1;
    unsigned int f2 : 3;
    unsigned int f3 : 4;
    int          f4 : 8;
};

位域会尽量打包到同一个整数中,但:

  • 可能产生填充
  • 跨越不同整数时容易出错
  • 地址不可取(& 操作符对位域无效)

7. 快速记忆总结

  • char → 1 字节,原样存
  • short/int/float → 2/4 字节,按小端存(主流)
  • double/long long → 8 字节
  • 结构体 → 有对齐填充,成员按声明顺序排列
  • 共用体 → 所有成员重叠,大小取最大
  • 指针 → 存地址(32位4字节,64位8字节)
  • 内存对齐 → 为了 CPU 访问效率,宁可浪费空间

8. 动手验证建议

写下面这段代码,打印每个成员的地址和内容,看看内存布局:

#include <stdio.h>

struct Test {
    char a;
    int b;
    short c;
    double d;
    char e;
};

int main() {
    struct Test t = { 'A', 0x12345678, 0xABCD, 3.1415926535, 'Z' };
    
    printf("地址\t\t成员\t值\t\t十六进制\n");
    printf("%p\t a\t%c\t\t%x\n", &t.a, t.a, t.a);
    printf("%p\t b\t%d\t\t%x\n", &t.b, t.b, t.b);
    printf("%p\t c\t%d\t\t%x\n", &t.c, t.c, t.c);
    printf("%p\t d\t%.3f\t\t%llx\n", &t.d, t.d, *(unsigned long long*)&t.d);
    printf("%p\t e\t%c\t\t%x\n", &t.e, t.e, t.e);
    
    printf("\n结构体总大小:%zu 字节\n", sizeof(t));
    return 0;
}

运行后观察地址差值,就能直观看到填充字节和对齐。

如果你想深入某个部分(比如 IEEE 754 浮点精确表示、位域跨字节、结构体在不同编译器下的布局差异、内存对齐对性能的影响等)。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • C语言的空类型指针,空指针,野指针详解

    C语言的空类型指针,空指针,野指针详解

    这篇文章主要介绍了C语言的空类型指针,空指针,野指针的详解及用法示例,介绍了其相关概念,然后分享了几种用法,具有一定参考价值。需要的朋友可以了解下。
    2021-09-09
  • C++实现银行排队系统

    C++实现银行排队系统

    这篇文章主要为大家详细介绍了C++实现银行排队系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • 浅谈C++中什么时候需要手动清理内存

    浅谈C++中什么时候需要手动清理内存

    尽管现代 C++ 提倡使用智能指针和容器自动管理内存,但在某些特定场景下仍需手动进行内存管理,下面就来介绍一下C++中什么时候需要手动清理内存,感兴趣的可以了解一下
    2025-07-07
  • 一篇文章带你了解C++语法基础--字符串

    一篇文章带你了解C++语法基础--字符串

    这篇文章主要介绍了C++常用字符串分割方法实例汇总,包括了strtok函数、STL、Boost等常用的各类字符串分割方法,非常具有实用价值,需要的朋友可以参考下
    2021-08-08
  • C++连接并使用MySQL数据库

    C++连接并使用MySQL数据库

    这篇文章主要为大家详细介绍了C++连接并使用MySQL数据库,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07
  • 基于Qt实现将BMP图像数据存入SQLite数据库

    基于Qt实现将BMP图像数据存入SQLite数据库

    这篇文章主要为大家详细介绍了如何使用 Qt 框架实现了将 BMP 图像文件以二进制形式存入 SQLite 数据库,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-06-06
  • C语言数据的存储和取出详细讲解

    C语言数据的存储和取出详细讲解

    这篇文章主要介绍了C语言数据的存储和取出详细讲解,作者使用图文代码实例讲解,有感兴趣的同学可以学习研究下
    2021-02-02
  • 老生常谈C++getline使用方法

    老生常谈C++getline使用方法

    下面小编就为大家带来一篇老生常谈C++getline使用方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • C++中关键字Struct和Class的区别

    C++中关键字Struct和Class的区别

    这篇文章主要介绍了C++中关键字Struct和Class的区别,本文用大量实例讲解了Struct和Class的区别,需要的朋友可以参考下
    2014-09-09
  • Linux页面置换算法的C语言实现

    Linux页面置换算法的C语言实现

    这篇文章主要为大家详细介绍了Linux页面置换算法的C语言实现,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12

最新评论