内存和地址

本文深入探讨了计算机内存的基本单位——字节,并介绍了如何利用指针进行内存操作。包括字节与位的关系、字节的组合形成不同大小的数据类型、以及在C语言中通过指针读取内存的不同方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计算机的内存由数以亿万计的位(bit)组成,每个位可以容纳值01。由于一个位所能表示的值得范围太有限,所以单独的位的用处不大,通常许多位组成一组作为一个存储单位,这样就可以存储范围较大的值。以下展示了现实机器中的一些内存位置:

100

101

102

103

104

105

106

107

这些位置的每一个都被称为字节(byte),每个字节都包含了存储一个字符所需要的位数。在很多现代的机器上,每个字节包含8个位,可以存储无符号值0255,或者有符号值-128127。实际内存中每个位置总是包含一些值。每个字节通过地址来标识,如上图中的数字所示。

为了存储更大的值,我们把两个或更多个字节合在一起作为一个更大的内存单位。例如,很多机器以字为单位存储整数,每个字一般由24个字节组成。下图所示内存位置与上图相同,但这次它以4个字节的字来表示。

100

104

尽管一个字(INT32)包含了4个字节,它仍然只有一个地址。至于它的地址是它最左边那个字节的位置还是最右边那个字节的位置,不同的机器有不同的规定。另一个需要注意的硬件事项是边界对齐boundary alignment)。在要求边界对齐的机器上,整型值存储的起始位置只能是某些特定的字节,通常是24的倍数。但这些问题是硬件设计者的事情,它们很少影响C程序员。我们只对两件事情感兴趣:

1.内存中的每个位置由一个独一无二的地址标识。

2.内存中的每个位置都包含一个值。

在实际程序中我们经常根据需要借助强大的指针对一块内存进行操作,再按字节组合析取出所需数据,平时的程序中经常用到通用指针void*(LPVOID)的妙处就在于可以按照需要操作一块内存,以取所需值类型。

以下测试小程序向我们清晰的展示了三种字节析取情况:

#include <stdio.h> #include <windows.h> int main(void) { int i; BYTE byte[9] = {48, 49, 50, 51, 52, 53, 54, 55,0}; printf("每1个byte的16进制BYTE值:/n"); for(i = 0; i < 9; i++) { printf("byte[%d] = %x /n", i, byte[i]); } printf("----------------------------------/n"); printf("字符串byte[9]:/n"); BYTE *pBYTE = byte; printf("*pBYTE = %s /n", pBYTE); printf("----------------------------------/n"); printf("每2个byte组合而成的16进制INT16值:/n"); INT16 *pINT16 = (INT16*)pBYTE; for (i = 0; i < 4; i++) { INT16 i16 = *(pINT16 + i); // Debug printf("*(pINT16 + %d) = %x /n", i, *(pINT16 + i)); } printf("----------------------------------/n"); printf("每4个byte组合而成的16进制INT32值:/n"); INT32 *pINT32 = (INT32*)pBYTE; for (i = 0; i < 2; i++) { INT32 i32 = *(pINT32 + i); // Debug printf("*(pINT32 + %d) = %x /n", i, *(pINT32 + i)); } printf("----------------------------------/n"); return 0; }

说明*(pINT16 + 0) = 3130而不是3031,这是因为Windows操作系统使用的是小序在前的存储方式(little endian),也即在起始地址处存放整数的低序号字节(低地址低字节)。关于字节的大小端问题,留待网络编程中进行探讨。

以下展示了两种强制类型转换:

// 多字节的截取(容易造成数据的丢失!)

int i1 = 0x12345678; // 小序存放顺序4byte0x78,0x56,0x34,0x12

short s1 = (short)i1; // 析取2byte0x5678

char c1 = (char)i1; // 析取1byte0x78

// 短字节的扩展

short s2 = 0x5678; // 小序存放顺序2byte0x78,0x56

int i2 = (int)s2; // 扩展2byte,高位补00x00005678

1byte=8bit,底层都是二进制位串进行移位实现相关操作。标准C++中的<bitset>提供了二进制位串操作接口。以下为打印单字节和通用数据类型二进制位串的程序示例。

typedef unsigned char uchar; // 枚举整数x二进制串中含有多少个1 int enum_filled_bits(int x) { int countx = 0; while (x) { countx++; x = x & (x - 1); } return countx; } // 打印单字节数的二进制位串 void binary_print_byte(uchar c) { for(int i = 0; i < 8; ++i) { if((c << i) & 0x80) // 左移 cout << '1'; else cout << '0'; } cout << ' '; } // 打印通用类型的二进制位串 template <class T> void binary_print_multibytes(T val) { void *f = &val; // 取地址 size_t sz = sizeof(T); uchar *pByte = new uchar[sz]; int i; for(i = 0; i < sz; i++) pByte[i] = *((uchar*)&f + i); #ifdef _BIG_ENDIAN for(i = 0; i != sz; i++) binary_print_byte(pByte[i]); #else // for windoze(Intel X86) for(i = sz; i != 0; i--) binary_print_byte(pByte[i-1]); #endif delete[] pByte; cout << endl; }

参考:

C/C++基本据类型

Pointers in C

Pointers

Pointers and Memory

字节那些事儿

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值