news 2026/8/24 15:26:07

C语言:底层程序设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言:底层程序设计

文章目录

  • 前言
    • 本文旨在系统性地介绍C语言中底层程序设计。
  • 一、二进制和进制转换
    • 1.1、二进制转十进制(权重法)
    • 1.2、十进制转二进制(短除法)
    • 1.3、 二进制转八进制
    • 1.4、二进制转十六进制
  • 二、原码、反码、补码
    • 2.1、有符号整数
    • 2.2、无符号整数
  • 三、浮点数的存储
    • 3.1、浮点数存的过程
    • 3.2、浮点数取的过程
    • 3.3、比较浮点数相等
    • 3.4、浮点数与整数存储对比
  • 四、大小端字节序
    • 4.1、小端字节序(存储)模式
    • 4.2、大端字节序(存储)模式
    • 4.3、网络字节序
    • 4.4、大小端的判断
  • 五、移位操作符
    • 5.1、左移操作符
    • 5.2、右移操作符
  • 六、位操作符:&、|、^、~

前言

本文旨在系统性地介绍C语言中底层程序设计。

一、二进制和进制转换

二进制、八进制、十进制、十六进制只是同一个数值的不同书写方式。
C语言中:八进制以 0 开头(如 017),十六进制以 0x 开头(如 0xF),二进制的 0b 前缀是 C23 新增特性。

1.1、二进制转十进制(权重法)

数字的每一位都有一个“权重”,它决定这一位在数值中所占的分量,该位的值 = 该位数字 × 该位权重。 权重 = 进制 ^ 位数(从右向左从 0 开始)。

以二进制 1101 为例:

二进制位1101
权重23222120
权重值8421
求值1 * 81 * 40 * 21 * 1

1 * 8 + 1 * 4 + 0 * 2 + 1 * 1 = 13

1.2、十进制转二进制(短除法)

方法:不断除以 2,记录余数,从下往上读取。

以十进制 125 为例:

被除数除数余数
125÷ 2621
62÷ 2310
31÷ 2151
15÷ 271
7÷ 231
3÷ 211
1÷ 201

结果:从下往上读取余数:1111101

1.3、 二进制转八进制

8进制的数字每⼀位是 0~7。
核心规则:从右向左,每 3 位二进制数对应 1 位八进制数(因为 23=8), 剩余不够3个2进制位的直接换算。

以二进制 01101011为例:

二进制序列0 11 0 10 1 1
对应八进制153

1.4、二进制转十六进制

16进制的数字每⼀位是0~ 9, A~F。
核心规则:从右向左,每 4 位二进制数对应 1 位十六进制数(因为 24=16),剩余不够4个⼆进制位的直接换算。

以二进制 01101011为例:

二进制序列0 1 1 01 0 1 1
对应十六进制6B

二、原码、反码、补码

整数的2进制表示方法有三种,原码、反码、补码。

整数在内存中统一使用补码表示和存储。
原因说明
①符号位统一处理 :符号位和数值位一起运算,无需单独判断正负
②加减法统一 :CPU只有加法器,减法可通过补码转为加法
③硬件简化 :原码↔补码的转换规则相同,无需额外电路

2.1、有符号整数

有符号整数的三种表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号位,剩余的都是数值位。符号位都是用0表示“正”,用1表示“负”。

正整数的原、反、补码都相同。

负整数的三种表示方法各不相同,需要进行计算(方法如下)
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。

补码算出原码,有2种方法:①原路返回:补码-1,再取反,得到原码;②补码取反,+1,也能得到原码。所以原码->补码,补码->原码 的计算方法是可以统⼀的。

2.2、无符号整数

无符号整数的三种 2 进制表示相同,没有符号位,每⼀位都是数值位。

三、浮点数的存储

浮点数类型包括 float、double、long double,精度和范围依次增大。浮点数在 <float.h> 中定义了取值范围,运算时注意精度误差。
根据国际标准IEEE(电气和电子工程协会) 754,任意⼀个二进制浮点数V的表示形式:
V = (−1)S∗ M ∗ 2E

部分含义
S符号位:0 为正,1 为负
M有效数字(1 ≤ M < 2)
E指数位

IEEE 754 浮点数存储结构

类型总位数符号位 S指数位 E有效数字 M
float(32位)321823
double(64位)6411152

3.1、浮点数存的过程

①有效数字 M 的存储规则:由于 1≤M<2,M 的整数部分始终为 1,因此只存储小数部分,读取时自动补回 1。节省 1 位有效数字,float 实际精度提升到 24 位有效数字。

②指数E 的存储规则:E 是无符号整数,但科学计数法中的指数可以为负数。因此 IEEE 754 规定:存储时 E 的真实值必须加上一个中间数(偏移量)。float类型,E位数8,取值范围0 ~ 255,偏移量127;double类型,E位数11,取值范围0 ~ 2047,偏移量1023。

浮点数可能无法精确保存,如 1.2 在二进制中无限循环,无法精确保存,因此存在误差。

3.2、浮点数取的过程

①E 不全为 0 且不全为 1(规格化数)
规则:
指数真实值 = E 存储值 - 偏移量(float: 127,double: 1023)
有效数字 M = 1.xxxxxx(整数部分的 1 自动补回)

示例:

0.5 在 float 中的存储
0.5 → 二进制:0.1 → 规格化:1.0 × 2⁻¹
S = 0(正数)
E 存储值 = -1 + 127 = 126 = 01111110
M = 0(小数部分,补全 23 位)
存储:0 01111110 00000000000000000000000

②E 全为 0(非规格化数)
规则:
指数真实值 = 1 - 127(float)或 1 - 1023(double)
有效数字 M = 0.xxxxxx(不再补回整数部分的 1)
用途:表示 ±0 和非常接近 0 的极小数字。

示例:

存储:0 00000000 00100000000000000000000
S = 0(正数)
E = 0(全为 0)
M = 00100000000000000000000
表示:0.001 × 2⁻¹²⁶(一个极小的正数)

③ E 全为 1(特殊值)
规则:
如果 M 全为 0 → 表示 ±∞(正负由 S 决定)
如果 M 不为 0 → 表示 NaN(Not a Number,非法数值)

示例:

1 11111111 00000000000000000000000 → -∞
0 11111111 00010000000000000000000 → NaN(M 不全为 0)

float 取值范围图

-∞

-3.403×10³⁸

-1.175×10⁻³⁸

-0

+0

+1.175×10⁻³⁸

+1.175×10³⁸

+∞

3.3、比较浮点数相等

浮点数不能直接用 == 比较,因为存储有精度误差。正确做法是使用 fabs() 判断差值是否小于一个允许的误差范围(如 1e-9)。

代码示例:

#include<stdio.h>#include<math.h>// fabs() 函数intmain(){doublea=0.1;doubleb=0.2;doublec=0.3;doubleeps=1e-9;// 误差范围(容差)if(fabs((a+b)-c)<eps){printf("==\n");// 输出 ==}else{printf("!=\n");}return0;}

3.4、浮点数与整数存储对比

代码示例:

#include<stdio.h>intmain(){// 整数 9 的存储// 内存中(32位):00000000 00000000 00000000 00001001// 按 int 解读 = 9intn=9;// 用 float 指针指向 n 的内存// 将同一块内存按 float 方式解读float*pFloat=(float*)&n;// 按 int 方式打印 → 9printf("n 的值为:%d\n",n);// 9// 按 float 方式解读整数 9 的内存// 9 的二进制:00000000 00000000 00000000 00001001// 按 float 解读:S=0,E=00000000(全0),M=...1001// E全0 → 非规格化数 → 极小值 → 0.000000printf("*pFloat 的值为:%f\n",*pFloat);// 0.000000// 将浮点数 9.0 写入 n 的内存// 9.0 = 1.001 × 2³// S=0,E=3+127=130=10000010,M=00100000000000000000000// 内存:0 10000010 00100000000000000000000// 按 int 解读:1091567616*pFloat=9.0;// 按 int 方式打印同一块内存 → 1091567616printf("n 的值为:%d\n",n);// 1091567616// 按 float 方式打印 → 9.000000printf("*pFloat 的值为:%f\n",*pFloat);// 9.000000return0;}

四、大小端字节序

4.1、小端字节序(存储)模式

数据的低位字节保存在内存的低地址处,高位字节保存在高地址处。

小端序把最低位字节放在最低地址,让 CPU 做加法时可以直接从低地址开始计算,无需跳转,简化硬件设计,提升计算效率。

4.2、大端字节序(存储)模式

数据的高位字节保存在内存的低地址处,低位字节保存在高地址处。

示例:0x11223344 在内存中的存储

地址小端存储大端存储
低地址44(低位)11(高位)
高地址11(高位)44(低位)

4.3、网络字节序

为了避免不同计算机之间数据传输时发生混乱,TCP/IP协议族规定大端序为“网络字节序”。当数据在网络上传输时,发送方计算机会将数据从其主机字节序转换成网络字节序(大端),接收⽅再将其从网络字节序转换回自己的主机字节序。

4.4、大小端的判断

关键:&i 获取的是低地址,(char*)&i 读取低地址处的 1 个字节。
如果低地址处是 01 → 小端;如果是 00 → 大端。
代码示例:

#include<stdio.h>intcheck_sys(){inti=1;// 取 i 的低地址,强转为 char*(读取1个字节)return*(char*)&i;}intmain(){intret=check_sys();if(ret==1)printf("小端\n");elseprintf("大端\n");return0;}

五、移位操作符

移位操作符只能对整数进行操作,对二进制位(补码)进行左移或右移。

操作符名称语法说明
<<左移a << b将 a 的二进制位(补码)向左移动 b 位
>>右移a >> b将 a 的二进制位(补码)向右移动 b 位

约束条件:①b >= 0 移动位数不能为负数;②b < 类型宽度 移动位数不能大于等于类型宽度(如 int 为 32 位)。
注:违反约束 未定义行为。

5.1、左移操作符

移位规则:左边丢弃,右边补 0

左操作数类型条件行为标准定义
unsigned任意低位补 0,结果 ≡ a × 2b(mod (a的类型最大值 + 1))完全定义
signed(≥ 0)不溢出结果为 a × 2b完全定义
signed(≥ 0)溢出未定义行为未定义
signed(< 0)任意未定义行为未定义

代码示例:

#include<stdio.h>intmain(){intnum=10;// 0000 1010intn=num<<1;// 0001 0100 = 20printf("n = %d\n",n);// 20printf("num = %d\n",num);// 10(原值不变)return0;}

5.2、右移操作符

移位规则:①逻辑右移:左边补 0,右边丢弃;②算术右移:左边补符号位(正数补 0,负数补 1),右边丢弃。

左操作数类型条件行为标准定义
unsigned任意逻辑右移(高位补 0),结果 = floor(a / 2ᵇ)完全定义
signed(≥ 0)任意算术右移(高位补 0),结果 = floor(a / 2ᵇ)完全定义
signed(< 0)任意实现定义(通常是算术右移,高位补符号位)实现定义

代码示例:

#include<stdio.h>intmain(){// 无符号数:逻辑右移unsignedintnum=10;// 0000 1010unsignedintn=num>>1;// 0000 0101 = 5printf("n = %u\n",n);// 5printf("num = %u\n",num);// 10return0;}

对有符号数进行移位操作时,先将其转换为无符号数,再移位,即可获得标准定义的确定行为,避免“实现定义”或“未定义行为”。
代码示例:

#include<stdio.h>intmain(){intx=-8;// 有符号数unsignedintux=(unsignedint)x;// 转换为无符号数ux>>=1;// 确定的逻辑右移(高位补0)printf("x = %d\n",x);// -8printf("ux = %u\n",ux);// 2147483644(逻辑右移结果)return0;}

六、位操作符:&、|、^、~

位操作符对整数的二进制位(补码)进行逐位运算,操作数必须是整数。

操作符名称形式规则
&按位与a & b对应位:有0则0,全1才1
|按位或a | b对应位:有1则1,全0才0
^按位异或a ^ b对应位:相同为0,相异为1
~按位取反~a所有位取反:0变1,1变0

代码示例:

#include<stdio.h>intmain(){inta=5;// 二进制补码(32位):0000 0000 0000 0000 0000 0000 0000 0101intb=3;// 二进制补码(32位):0000 0000 0000 0000 0000 0000 0000 0011// 1. 按位与 &// 0000 0000 0000 0000 0000 0000 0000 0101 (a)// 0000 0000 0000 0000 0000 0000 0000 0011 (b)// -----------------------------------------// 0000 0000 0000 0000 0000 0000 0000 0001 = 1intand=a&b;printf("5 & 3 = %d\n",and);// 1// 2. 按位或 |// 0000 0000 0000 0000 0000 0000 0000 0101 (a)// 0000 0000 0000 0000 0000 0000 0000 0011 (b)// -----------------------------------------// 0000 0000 0000 0000 0000 0000 0000 0111 = 7intor=a|b;printf("5 | 3 = %d\n",or);// 7// 3. 按位异或 ^// 0000 0000 0000 0000 0000 0000 0000 0101 (a)// 0000 0000 0000 0000 0000 0000 0000 0011 (b)// -----------------------------------------// 0000 0000 0000 0000 0000 0000 0000 0110 = 6intxor=a^b;printf("5 ^ 3 = %d\n",xor);// 6// 4. 按位取反 ~// ~a = 1111 1111 1111 1111 1111 1111 1111 1010 (补码)// 符号位为1 → 负数// 补码转原码:取反 +1// 原码:1000 0000 0000 0000 0000 0000 0000 0110 = -6intnot=~a;printf("~5 = %d\n",not);// -6return0;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/24 15:24:10

Minecraft-Region-Fixer:一条命令完成区块数据修复的完整指南

Minecraft-Region-Fixer&#xff1a;一条命令完成区块数据修复的完整指南 【免费下载链接】Minecraft-Region-Fixer Python script to fix some of the problems of the Minecraft save files (region files, *.mca). 项目地址: https://gitcode.com/gh_mirrors/mi/Minecraft…

作者头像 李华
网站建设 2026/8/24 15:23:58

Ubuntu 网络不可达问题排查与解决指南

1. 问题现象 在 Ubuntu 系统中&#xff0c;你可能会遇到“网络不可达”的错误&#xff0c;具体表现为&#xff1a; 无法通过浏览器访问任何网站。终端执行 ping 或 curl 命令时&#xff0c;返回“Network is unreachable”错误。系统托盘网络图标显示断开连接或受限连接。无法…

作者头像 李华
网站建设 2026/8/24 15:23:43

yt-dlp-gui 零门槛上手:不碰命令行,把视频存到桌面

yt-dlp-gui 零门槛上手&#xff1a;不碰命令行&#xff0c;把视频存到桌面 【免费下载链接】yt-dlp-gui Windows GUI for yt-dlp 项目地址: https://gitcode.com/gh_mirrors/yt/yt-dlp-gui 刷到想留存的视频&#xff0c;链接复制到一半&#xff0c;却不知道往哪粘&#…

作者头像 李华
网站建设 2026/8/24 15:23:01

Xilinx Zynq7000系列FPGA纯verilog图像缩放,串口上位机动态调整缩放分辨率,提供4套工程源码和技术支持

目录 1、方案概述创作背景工程概述免责声明 2、相关方案推荐本博主所有FPGA工程项目-->汇总目录我这里已有的FPGA图像缩放方案本方案在Xilinx Artix7 系列FPGA上的应用本方案在Xilinx Kintex7 系列FPGA上的应用本方案在Xilinx UltraScale 系列FPGA上的应用本方案在国产FPGA紫…

作者头像 李华
网站建设 2026/8/24 15:19:55

WebP元数据缺失不能补成0

前 篇排查完“读取调用完成&#xff0c;Canvas 宽高却仍未提供”后&#xff0c;我一度想用一个很常见的办法把界面补齐&#xff1a;没有值就显示 0。表格会整齐&#xff0c;后面的尺寸判断也能继续写&#xff0c;似乎比一大片“未提供”更像一个完整功能。 幸好我没有直接提交那…

作者头像 李华
网站建设 2026/8/24 15:16:34

基于SpringBoot的教学管理自动化系统设计与实现毕业设计项目源码

联系博主 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 …

作者头像 李华