数据存储详解

数据存储详解:整型、大小端、浮点数、整形提升与截断

整型存储规则:原码、反码、补码

在计算机中,整数使用补码形式存储。理解原码、反码、补码便于理解整型存储、计算、整型提升、截断。

原码

  • 定义:最高位为符号位(0为正,1为负),其余位表示二进制
  • 范围:n位原码可表示 -(2^(n-1) - 1) 到 +(2^(n-1)-1)
  • 注意:有两个零(+0和-0),导致运算复杂
// 8位原码示例
+5:  0000 0101
-5:  1000 0101
+0:  0000 0000
-0:  1000 0000  // 两个零的问题

反码

  • 定义:正数的反码等于原码;负数的反码是原码除符号位外各位取反
  • 用途:计算补码的中间步骤
// 8位反码示例
+5的反码:  0000 0101 (与原码相同)
-5的反码:  1111 1010 (原码1000 0101除符号位外取反)

补码

  • 定义:正数的补码等于原码;负数的补码是反码+1
  • 优点:只有一个零,运算简单,硬件实现容易
  • 范围:n位补码可表示 -2^(n-1) 到 +(2^(n-1)-1)
// 8位补码示例
+5的补码:  0000 0101
-5的补码:  1111 1011 (反码1111 1010 + 1)
+0的补码:  0000 0000
-0的补码:  0000 0000 (只有一个零)

// 特殊值:最小负数
-128的补码: 1000 0000 (8位补码的最小值)

大小端存储

字节序是指多字节数据在内存中的存储顺序。那么,对于char[]就不用考虑字节序问题。

大端序

  • 定义:高位字节存储在低地址,低位字节存储在高地址
  • 特点:符合人类阅读习惯,网络传输标准
  • 使用:网络协议

小端序

  • 定义:低位字节存储在低地址,高位字节存储在高地址
  • 特点:便于处理器运算
  • 使用:x86、x86-64、ARM(通常)

存储示例

以32位整数 0x12345678 为例:

内存地址:    0x1000  0x1001  0x1002  0x1003

大端存储:      0x12    0x34    0x56    0x78
            (高位)                  (低位)

小端存储:      0x78    0x56    0x34    0x12
            (低位)                  (高位)

检测和转换代码

#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>  // 网络字节序转换函数

// 检测系统字节序
int is_little_endian() {
    uint32_t test = 0x12345678;
    uint8_t *byte_ptr = (uint8_t*)&test;
    return (*byte_ptr == 0x78);  // 如果第一个字节是0x78,则为小端
}

// 手动字节序转换
uint32_t swap_bytes_32(uint32_t value) {
    return ((value & 0xFF000000) >> 24) |
           ((value & 0x00FF0000) >> 8)  |
           ((value & 0x0000FF00) << 8)  |
           ((value & 0x000000FF) << 24);
}

uint16_t swap_bytes_16(uint16_t value) {
    return ((value & 0xFF00) >> 8) |
           ((value & 0x00FF) << 8);
}

void print_bytes(void *ptr, size_t size) {
    uint8_t *byte_ptr = (uint8_t*)ptr;
    printf("内存布局: ");
    for (size_t i = 0; i < size; i++) {
        printf("0x%02X ", byte_ptr[i]);
    }
    printf("\n");
}

int main() {
    printf("系统字节序检测:\n");
    if (is_little_endian()) {
        printf("当前系统是小端序\n");
    } else {
        printf("当前系统是大端序\n");
    }
    
    uint32_t value = 0x12345678;
    printf("\n原始值: 0x%08X\n", value);
    print_bytes(&value, sizeof(value));
    
    // 使用系统函数转换
    uint32_t network_order = htonl(value);  // host to network long
    printf("网络字节序: 0x%08X\n", network_order);
    print_bytes(&network_order, sizeof(network_order));
    
    uint32_t host_order = ntohl(network_order);  // network to host long
    printf("转换回主机序: 0x%08X\n", host_order);
    
    // 手动转换
    uint32_t swapped = swap_bytes_32(value);
    printf("手动字节序转换: 0x%08X\n", swapped);
    print_bytes(&swapped, sizeof(swapped));
    
    return 0;
}

网络编程应用场景

#include <sys/socket.h>
#include <netinet/in.h>

struct packet_header {
    uint16_t version;      // 版本号
    uint16_t length;       // 数据长度
    uint32_t sequence;     // 序列号
    uint32_t timestamp;    // 时间戳
};

// 发送前转换为网络字节序
void prepare_packet(struct packet_header *header) {
    header->version = htons(header->version);
    header->length = htons(header->length);
    header->sequence = htonl(header->sequence);
    header->timestamp = htonl(header->timestamp);
}

// 接收后转换为主机字节序
void parse_packet(struct packet_header *header) {
    header->version = ntohs(header->version);
    header->length = ntohs(header->length);
    header->sequence = ntohl(header->sequence);
    header->timestamp = ntohl(header->timestamp);
}

浮点数存储(IEEE 754标准)

  • 单精度浮点数(32位)
符号位(S) | 指数位(E) | 尾数位(M)
    1位   |    8位    |   23位
  • 双精度浮点数(64位)
符号位(S) | 指数位(E) | 尾数位(M)
    1位   |   11位    |   52位

存储规则

浮点数的值计算公式:

value = (-1)^S × (1 + M) × 2^(E - bias)

其中:
- S: 符号位 (0为正,1为负)
- E: 指数位 (偏移后的指数)
- M: 尾数位 (小数部分)
- bias: 偏移量 (单精度127,双精度1023)

要注意的是:

  • 当指数位不为0,尾数位表示1.XXXX中的XXXX,能够提升一位精度
  • 当指数位为0,尾数位不再表示1.XXXX中的XXXX,而是表示0.XXXX的XXXX
  • 浮点数再运算时会精度问题,要尽可能避免使用浮点数计算

整型提升与截断

整型提升:将小于int的整型自动转换为int或unsigned int

截断:将大类型的值赋给小类型时,丢弃高位部分

  • 整型提升规则

    • 小于int的类型在运算时自动提升为int

    • 提升发生在算术运算、位运算、比较运算中

    • 可变参数函数调用时也会发生提升

  • 截断机制:大类型赋值给小类型时自动截断高位

整型提升代码实例

#include <stdio.h>

int main() {
    // 例子1:char运算时的提升
    char a = 100;
    char b = 50;
    char result = a + b;  // a和b先提升为int,相加后截断回char
    
    printf("char运算: %d + %d = %d\n", a, b, result);
    printf("a + b的实际类型大小: %zu (int大小: %zu)\n", 
           sizeof(a + b), sizeof(int));
    
    // 例子2:位运算中的提升
    unsigned char c = 0xFF;  // 255
    printf("~0xFF = 0x%X\n", ~c);  // c提升为int后取反
    
    return 0;
}

截断代码实例

#include <stdio.h>

int main() {
    // 例子1:int截断为char
    int big = 300;         // 二进制: 0001 0010 1100
    char small = big;      // 截断为: 0010 1100 = 44
    
    printf("int %d 截断为 char %d\n", big, small); // 把char打印成%d,也发生了整形提升
    
    // 例子2:负数截断
    int neg = -1;          // 二进制: 1111...1111
    char neg_small = neg;  // 截断为: 1111 1111 = -1
    
    printf("int %d 截断为 char %d\n", neg, neg_small);
    
    // 例子3:符号位变化
    int pos = 200;         // 二进制: 1100 1000
    signed char result = pos;  // 截断为: 1100 1000 = -56 (最高位是1)
    
    printf("正数 %d 截断后变成 %d (符号改变)\n", pos, result);
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值