C 语言内存探秘:数据存储的字节密码

打印 上一主题 下一主题

主题 1396|帖子 1396|积分 4203

一、数据在内存中的存储

1、基本数据范例存储



  • 整型:如int范例,通常在32位体系中占4个字节,在内存中以二进制补码的情势存储。比方,整数10的二进制表示为00000000 00000000 00000000 00001010,以小端字节序存储时,在内存中的次序是0A 00 00 00;以大端字节序存储时,次序是00 00 00 0A。
  • 浮点型:单精度float范例一样平常占4个字节,双精度double范例占8个字节。浮点数在内存中的存储遵照IEEE 754标准,以符号位、指数位和尾数位的情势存储。比方,单精度浮点数3.14在内存中的存储情势与整数完全不同。
  • 字符型:char范例通常占1个字节,用来存储单个字符的ASCII码值。比方,字符'A'的ASCII码值是65,在内存中存储为41(十六进制)。
  1.   int main()
  2. {
  3.    printf("%zd\n", sizeof(int));
  4.    printf("%zd\n", sizeof(char));
  5.    printf("%zd\n", sizeof(float));
  6.    return 0;
  7. }
复制代码

2、数组存储



  • 数组中的元素在内存中是连续存储的。比方,int arr[5] = {1, 2, 3, 4, 5};,数组arr的5个元素在内存中依次排列,假设数组首地点为0x1000,那么arr[0]存储在0x1000处,arr[1]存储在0x1004处,以此类推,每个元素之间的偏移量为sizeof(int)。

3、布局体存储

1、基本存储规则



  • 布局体的成员在内存中是按照定义的次序依次存储的。每个成员的存储地点相对于布局体首地点有肯定的偏移量。比方,对于布局体struct Example { int a; char b; };,起首存储int型成员a,然后存储char型成员b。
  • 编译器大概会在布局体成员之间插入填充字节(Padding),这是为了满足成员的对齐要求。对齐要求通常是为了提高内存访问的服从,因为大多数盘算机硬件在访问内存时,对于按照肯定字节对齐的数据访问速率更快。比方,在32位体系中,int范例通常要求4字节对齐,double范例要求8字节对齐等。
2、举例阐明



  • 例1:简单布局体

    • 考虑布局体struct Simple { char c; int i; };。假设char范例占1个字节,int范例占4个字节。
    • 起首存储c,其地点假设为布局体首地点0x0000,占1个字节,存储范围是0x0000。
    • 由于int范例要求4字节对齐,在c和i之间会插入3个填充字节。i的存储起始地点为0x0004,占4个字节,存储范围是0x0004 - 0x0007。所以整个布局体Simple占8个字节。

  • 例2:包含数组的布局体

    • 定义布局体struct ArrayStruct { int a; char arr[3]; int b; };。
    • 起首存储a,假设其地点为0x0000,占4个字节,存储范围是0x0000 - 0x0003。
    • 接着存储arr数组,其起始地点为0x0004,因为char数组本身没有对齐要求,且前面a已经保证了4字节对齐。arr占3个字节,存储范围是0x0004 - 0x0006。
    • 对于b,由于int范例要求4字节对齐,所以在arr和b之间会插入1个填充字节。b的存储起始地点为0x0008,占4个字节,存储范围是0x0008 - 0x000B。整个布局体ArrayStruct占12个字节。

  • 例3:嵌套布局体

    • 定义布局体struct Inner { char c; };和struct Outer { int a; struct Inner in; char b; };。
    • 起首存储Outer布局体中的a,假设其地点为0x0000,占4个字节,存储范围是0x0000 - 0x0003。
    • 接着存储Inner布局体中的c,由于Inner布局体是嵌套在Outer布局体中的,c的存储起始地点为0x0004,占1个字节,存储范围是0x0004。
    • 对于Outer布局体中的b,因为char范例前面已经满足了4字节对齐(由于a的存储),所以b的存储起始地点为0x0005,占1个字节,存储范围是0x0005。整个Outer布局体占8个字节。

3、检察布局体巨细和成员偏移量的方法



  • 在C语言中,可以使用sizeof运算符来检察布局体的巨细。比方,对于上述struct Simple布局体,可以通过printf("%d", sizeof(struct Simple));来输出布局体的巨细。
  • 有些编译器提供了扩展来检察布局体成员的偏移量,如在GCC中,可以使用__attribute__((packed))来取消布局体的对齐填充,使得布局体按照紧密排列的方式存储,这样可以更清楚地看到成员的原始偏移量。不外这种方式大概会影响内存访问服从,一样平常用于特殊的需求,如数据存储格式有严格要求的网络协议数据包的构建等。
二、巨细端字节序



  • 概念

    • 大端字节序(Big-Endian):也叫大端序或大字节序,数据的高位字节存于低地点,低位字节存于高地点。比方,对于整数0x12345678,高位字节0x12存于内存低地点,接着依次是0x34、0x56、0x78存于更高地点,就像按从左到右(高位在前)的次序存储。
    • 小端字节序(Little-Endian):又称小端序或小字节序,与大端字节序相反,数据的低位字节存于低地点,高位字节存于高地点。对于0x12345678,在小端字节序下,0x78存于内存低地点,接着是0x56、0x34、0x12存于更高地点,如同从右到左(低位在前)存储。

  • 影响:不同的字节序在多字节数据的存储和传输中会产生影响。在网络编程中,通常规定使用大端字节序举行数据传输,以保证不同主机之间数据的一致性。假如两台主机的字节序不同,在举行数据通讯时就必要举行字节序的转换。
三、字节序的判断



  • 利用指针范例转换判断:通过将一个多字节数据的地点转换为char *范例指针,然后访问该指针指向的字节,根据第一个字节的值来判断字节序。比方:
  1. #include <stdio.h>
  2. int main() {
  3.     int num = 1;
  4.     char *ptr = (char *)&num;
  5.     if (*ptr == 1) {
  6.         printf("小端字节序\n");
  7.     } else {
  8.         printf("大端字节序\n");
  9.     }
  10.     return 0;
  11. }
复制代码


  • 利用联合体判断:联合体的所有成员共用同一块内存空间,可以利用这一特性来判断字节序。比方:
  1. #include <stdio.h>
  2. union EndianTest {
  3.     int num;
  4.     char bytes[4];
  5. };
  6. int main() {
  7.     union EndianTest test;
  8.     test.num = 1;
  9.     if (test.bytes[0] == 1) {
  10.         printf("小端字节序\n");
  11.     } else {
  12.         printf("大端字节序\n");
  13.     }
  14.     return 0;
  15. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大连全瓷种植牙齿制作中心

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表