文章目录
数据存储详解:整型、大小端、浮点数、整形提升与截断
整型存储规则:原码、反码、补码
在计算机中,整数使用补码形式存储。理解原码、反码、补码便于理解整型存储、计算、整型提升、截断。
原码
- 定义:最高位为符号位(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;
}

被折叠的 条评论
为什么被折叠?



