当前位置: 首页 > news >正文

面试必问!!!:整数在计算机中是怎么保存的?

面试必问:补码、大小端、类型转换,一篇打通

1. 测试环境说明

以下数据基于Visual Studio x86(32位)环境实测。不同平台下部分类型大小可能不同,后文会详细说明。

项目配置
开发环境Visual Studio
目标平台x86(32位)
字节序小端存储

2. 本文目标

初学C语言时只知道int是 4 字节、char是 1 字节,但不清楚为什么这样设计、负数怎么存、类型之间怎么转换。

本文目标:画完整数家族的全貌,搞懂补码、大小端、类型转换这三个面试高频考点。


3. 有符号整数家族

有符号整数可以表示正数、负数和零。存储结构是1位符号位 + N位数值位

类型格式控制符大小(Byte)存储结构范围
char%hhd11符号+7数值-128 ~ 127
short%hd21符号+15数值-32768 ~ 32767
int%d41符号+31数值-2^31 ~ 2^31-1
long%ld41符号+31数值-2^31 ~ 2^31-1
long long%lld81符号+63数值-2^63 ~ 2^63-1

注意long在 32 位 Windows 上是 4 字节,在 64 位 Linux 上是 8 字节。本文环境是 32 位 VS,所以long=int= 4 字节。


4. 无符号整数家族

无符号整数所有位都表示数值,没有符号位。

类型格式控制符大小(Byte)范围
unsigned char%hhu10 ~ 255
unsigned short%hu20 ~ 65535
unsigned int%u40 ~ 2^32-1
unsigned long%lu40 ~ 2^32-1
unsigned long long%llu80 ~ 2^64-1

适用场景:身高、体重、数组下标、size_t等没有负数的数据。


5. 补码:负数在内存中的存储方式

计算机中所有整数都以补码形式存储。这是面试最高频的考点之一。

5.1 正数:原码 = 反码 = 补码

正数三码合一,直接存其二进制形式。

inta=10;// 原码:(0) 00000000 00000000 00000000 00001010// 补码:(0) 00000000 00000000 00000000 00001010// 内存(小端):0A 00 00 00

5.2 负数:原码 → 取反 → +1

负数需要三步才能存入内存。以 -10 为例:

// 第一步:写原码(符号位=1,数值位=绝对值)// 原码:(1) 00000000 00000000 00000000 00001010// ↑符号位为1表示负数,后面是10的二进制// 第二步:求反码(符号位不变,其余位取反)// 反码:(1) 11111111 11111111 11111111 11110101// ↑符号位保持1,后面是原码数值位的按位取反// 第三步:得补码(反码最低位加1)// 补码:(1) 11111111 11111111 11111111 11110110// ↑这就是最终存储在内存中的二进制形式// 内存查看(小端存储):// 地址: 0x00 0x01 0x02 0x03// 数据: 0xF6 0xFF 0xFF 0xFF// 解释: 低地址存低字节,所以看到的是 F6 FF FF FF

计算过程总结

  1. 原码:符号位1 + 绝对值的二进制
  2. 反码:符号位不变,数值位按位取反
  3. 补码:反码加1(得到最终存储形式)

5.3 从内存读出负数:减 1 → 取反

如果看到内存中最高位是 1,就知道它是负数,需要反向操作:

补码:(1) 11111111 11111111 11111111 11110110 减一:(1) 11111111 11111111 11111111 11110101 取反:(1) 00000000 00000000 00000000 00001010 → 原码 = -10

5.4 为什么用补码?

统一加减法,省去减法电路。 A - B 就是 A + (-B),CPU 只需要加法器就能完成所有整数运算。

补码运算示例

inta=10;// 补码:00001010intb=-6;// 补码:11111010intc=a+b;// 00001010 + 11111010 = 00000100 = 4// 10 + (-6) = 4,验证正确

6. 大小端:字节的存储顺序

同一个整数0x12345678在不同 CPU 上的存储顺序不同。这是面试中考察“是否真的写过底层代码”的标志性问题。

模式低地址 → 高地址常见平台
小端78 56 34 12x86/x64(VS 32位也是小端)
大端12 34 56 78部分嵌入式、网络字节序

验证方法

  • 用 VS 调试器查看内存窗口
  • 或 GDB 的x/4xb &a,第一个字节是0x78就是小端

内存布局对比

小端存储(x86/x64): 地址:0x00 0x01 0x02 0x03 数据:0x78 0x56 0x34 0x12 大端存储(网络字节序): 地址:0x00 0x01 0x02 0x03 数据:0x12 0x34 0x56 0x78

7. 类型转换的底层规则

7.1 大字节 → 小字节:截取低字节

inta=0x12345678;charc=a;// c = 0x78,高 3 字节被丢弃// 内存视角:// a: 78 56 34 12 (小端存储)// c: 78 (只保留最低字节)

7.2 小字节 → 大字节:符号扩展

// 有符号类型:高位全部用符号位填充,保证数值不变charc=-1;// 内存:FFinti=c;// 内存:FF FF FF FF(符号位 1 填充高位)// 解释:c 的符号位是 1,扩展时高位全部填充 1// 无符号类型:高位全部用 0 填充unsignedcharuc=255;// 内存:FFunsignedintui=uc;// 内存:00 00 00 FF(高位填充 0)

7.3 有符号 ↔ 无符号:二进制位不变,解读方式变

unsignedintu=0xFFFFFFFF;printf("%d",u);// 输出 -1(按有符号解读)printf("%u",u);// 输出 4294967295(按无符号解读)// 同样的二进制,按 %d 解读是 -1,按 %u 解读是 4294967295

8. sizeof 验证

用代码验证每种类型在自己的平台上实际占多少字节。

以下是在 Windows VS x86 环境下的测试代码和结果:

#include<stdio.h>intmain(){printf("=== Windows VS x86 环境类型大小测试 ===\n\n");// 有符号整数类型printf("有符号整数类型:\n");printf("sizeof(char) = %zu 字节\n",sizeof(char));printf("sizeof(short) = %zu 字节\n",sizeof(short));printf("sizeof(int) = %zu 字节\n",sizeof(int));printf("sizeof(long) = %zu 字节\n",sizeof(long));printf("sizeof(long long) = %zu 字节\n\n",sizeof(longlong));// 无符号整数类型printf("无符号整数类型:\n");printf("sizeof(unsigned char) = %zu 字节\n",sizeof(unsignedchar));printf("sizeof(unsigned short) = %zu 字节\n",sizeof(unsignedshort));printf("sizeof(unsigned int) = %zu 字节\n",sizeof(unsignedint));printf("sizeof(unsigned long) = %zu 字节\n",sizeof(unsignedlong));printf("sizeof(unsigned long long) = %zu 字节\n\n",sizeof(unsignedlonglong));// 指针类型printf("指针类型:\n");printf("sizeof(int*) = %zu 字节\n",sizeof(int*));printf("sizeof(char*) = %zu 字节\n",sizeof(char*));printf("sizeof(void*) = %zu 字节\n",sizeof(void*));return0;}

运行结果

=== Windows VS x86 环境类型大小测试 === 有符号整数类型: sizeof(char) = 1 字节 sizeof(short) = 2 字节 sizeof(int) = 4 字节 sizeof(long) = 4 字节 sizeof(long long) = 8 字节 无符号整数类型: sizeof(unsigned char) = 1 字节 sizeof(unsigned short) = 2 字节 sizeof(unsigned int) = 4 字节 sizeof(unsigned long) = 4 字节 sizeof(unsigned long long) = 8 字节 指针类型: sizeof(int*) = 4 字节 sizeof(char*) = 4 字节 sizeof(void*) = 4 字节

验证结论

  1. 在 Windows VS x86 环境下,intlong都是 4 字节
  2. 指针类型大小为 4 字节(32位系统)
  3. 与前面表格中的理论值完全一致


9. 平台差异提醒

不同平台下类型大小可能不同,以下是常见差异对比:

类型32位 Windows(本文环境)64位 Linux说明
char1 字节1 字节字符类型,固定1字节
short2 字节2 字节短整型,固定2字节
int4 字节4 字节整型,通常4字节
long4 字节8 字节主要差异点
long long8 字节8 字节长整型,固定8字节
指针4 字节8 字节主要差异点

经验:跨平台代码不要假设long和指针的大小,用sizeof实测或用固定大小的类型(如int32_tint64_t)。

10. 踩坑记录

坑1:格式控制符用错

chara=255;printf("%hhd\n",a);// -1(255 按有符号解读是 -1)printf("%hhu\n",a);// 255(正确)

坑2:char 的有符号/无符号不确定

// char 默认是有符号还是无符号取决于编译器// VS 中默认 char 是有符号的charc1=255;// 可能被解释为 -1unsignedcharc2=255;// 明确指定无符号,保证是 255// 需要明确指定 signed char 或 unsigned charsignedcharsc=-128;// 明确有符号unsignedcharuc=255;// 明确无符号

坑3:long 的平台差异

// 在 VS 32 位下 long = 4 字节// 在 64 位 Linux 下 long = 8 字节longvalue=100;printf("sizeof(long) = %zu\n",sizeof(long));// 不同平台结果不同// 解决方案:使用固定大小类型#include<stdint.h>int32_tfixed32=100;// 固定 4 字节int64_tfixed64=100;// 固定 8 字节

坑4:大小端导致的字节序问题

// 网络传输时需要注意字节序转换uint32_tnetwork_value=0x12345678;uint32_thost_value=ntohl(network_value);// 网络字节序转主机字节序// 文件读写时也要注意字节序FILE*fp=fopen("data.bin","wb");uint32_tdata=0x12345678;fwrite(&data,sizeof(data),1,fp);// 写入的是主机字节序

11. 学习体会:从“背锅侠”到“规则制定者”

以前学C语言,看到int是4字节、char是1字节,总觉得这是“天经地义”的设定,背就完事了。面试被问到“为什么负数要存补码”,只能支支吾吾说“计算机就是这么规定的”。

现在终于明白了:这些不是死记硬背的规则,而是一套自洽的底层逻辑

  • 补码:不是故意为难你,而是为了让CPU只用加法器就能搞定所有整数运算(包括减法)。就像你学会了“借位减法”,突然发现原来可以全部用加法搞定——这感觉,爽!
  • 大小端:不是CPU设计师的恶趣味,而是历史遗留和性能优化的结果。知道这个,下次面试官问“怎么判断大小端”,你就能淡定地说:“看第一个字节是高位还是低位呗!”
  • 类型转换:本质就是“二进制位不变,解释方式变”。同一个0xFFFFFFFF%d看是 -1,%u看是 4294967295——不是内存变了,是你的“眼镜”换了。

现在再看这些规则,不再是“背锅侠”式的被动接受,而是能理解背后的设计逻辑。就像玩游戏终于看懂了规则书,从“被规则玩”变成了“玩规则”。

12. 下篇预告:浮点数的“魔法”与“陷阱”

恭喜你!整数家族的存储规则已经通关:补码让负数有了统一的表示,大小端决定了字节排列顺序,类型转换的本质是“二进制位不变,解释方式变”。

但内存的“魔法秀”还没结束——接下来,让我们走进浮点数的奇幻世界

🎩 下篇看点:

  1. IEEE 754 标准:为什么0.1 + 0.2 ≠ 0.3?不是计算机算错了,而是浮点数的“精度游戏”。
  2. S-E-M 三段式:float 和 double 那套“符号位-指数位-尾数位”到底怎么工作?比整数复杂,但也更有趣!
  3. 内存视角:同一块内存,用intfloat解读会得到完全不同的结果——这不是bug,这是特性!
  4. 实战踩坑:为什么float a = 0.1; if (a == 0.1)可能永远不成立?如何正确比较浮点数?

🚀 预告金句:

“整数是规规矩矩的上班族,浮点数是充满创意的艺术家——但艺术家也有自己的‘怪癖’。”

准备好了吗?下一篇,我们继续拆解内存底层,看看那些看似“魔法”的现象背后,都是严谨的数学规则。

(悄悄说:学完浮点数,你就能理解为什么游戏里的物理引擎有时候会“抽风”,也能写出更稳健的科学计算代码了。)

http://www.jsqmd.com/news/952923/

相关文章:

  • 论文AIGC率怎么降?2026实测SpeedAI领跑多平台横评 - 仙仙学姐测评
  • Java:Java后端开发,本地开发环境,服务器部署环境,运维支撑环境 都需要哪些类别的工具或技术 / Java后端三大环境完整清单 202606
  • 小Why的密码锁【牛客tracker 每日一题】
  • Inference与Prediction的本质区别:从机器学习工程实践看系统层与算法层的分界
  • 从Hello World到第一个项目:用VS Code + Rust-Analyzer插件打造你的高效Rust工作流
  • 别只盯着物种丰度图了!16S报告里这3个高级功能(LEfSe、FAPROTAX、随机森林)才是发文章的关键
  • JSON对比终极指南:3分钟掌握可视化差异分析神器
  • 2026年四川商用摆摊大伞/岗亭遮阳伞公司对比推荐 - 行业平台推荐
  • 115. 全机型救砖方案汇总|高通EDL/MTK刷写/苹果DFU黑砖修复实操教程
  • Claude深度集成开发工作流:工程化上下文管理实践
  • arXiv投稿避坑实录:从邮箱注册到.bbl文件,新手必看的5个细节
  • 2026实用降AI工具测评:选这几款高效不踩坑 - 老米_专讲AIGC率
  • 2026年评价高的哈尔滨收银系统/哈尔滨小程序开发/哈尔滨GEO/哈尔滨电子签品质保障公司 - 品牌宣传支持者
  • Steam挂刀行情站:数据驱动的饰品交易智能决策系统
  • 多维聚合实战:从OLAP立方体构建到实时聚合优化
  • Mythos能力编排层:大模型受控释放的工程实践
  • 2026年6月主流企业智能体全维度评测:从智能助手到企业级AI中枢
  • 2026年靠谱的郑州家装淋浴房/淋浴房/郑州成品淋浴房/郑州民宿淋浴房高口碑品牌推荐 - 品牌宣传支持者
  • 系统内置apk无法使用 手动安装却可以
  • 2026年知名的哈尔滨系统集成/哈尔滨电子签热选公司推荐 - 行业平台推荐
  • 单卡RTX 4090微调20B多语言大模型做推理训练实战
  • 从充电场站到干线物流:千方 ESG 报告里的多场景节能探索
  • 百度网盘全速下载终极指南:告别限速,轻松获取文件
  • Java 开发者,不必在 AI 时代感到焦虑
  • Moltbot:本地化自动化代理的系统级实践与可信执行设计
  • 2026年热门的太阳伞/岗亭遮阳伞长期合作厂家推荐 - 品牌宣传支持者
  • 从PHM 2012挑战赛看工业预测性维护:如何用轴承振动数据训练你的第一个RUL模型
  • Adobe Photoshop Lightroom Classic
  • Unity 滚动球游戏(二)
  • 快速验证物联网想法:用快马一键生成esp8266 wifi连接原型代码