C 语 言 --- 整 形 提 升
- signed(有符号数)和unsigned(无符号数)
-
- [有 符 号 数(signed)](#有 符 号 数(signed))
- [无 符 号 (unsigned)](#无 符 号 (unsigned))
- [整 形 提 升](#整 形 提 升)
- [算 数 转 换](#算 数 转 换)
- 复杂表达式的求值]
- 总结
💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 C 语 言
💡个 人 主 页:@笑口常开xpr 的 个 人 主 页
📚系 列 专 栏:C 启新程
✨代 码 趣 语:不 要 重 复!在 一 个 系 统 中,每 条 知 识 必 须 唯 一、清 晰、权 威 的 表 达。
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee

在 编 程 的 世 界 里,每 一 行 代 码 都 可 能 隐 藏 着 无 限 的 可 能 性 。你 是 否 想 过,一 个 小 小 的 程 序 究 竟 能 改 变 什 么?它 可 以 是 解 决 复 杂 问 题 的 工 具 ,也 可 以 是 实 现 梦 想 的 桥 梁。今 天,就 让 我 们 一 起 走 进 C 语 言 整 形 提 升 的 世 界,探 索 它 的 无 限 潜 力。
signed(有符号数)和unsigned(无符号数)
在 开 始 讲 解 之 前,首 先 回 顾 下 面 的 代 码。
下 面 展 示代 码 示 例
。
javascript
#include<stdio.h>
#include<stdbool.h>
int main()
{
printf("%zd\n", sizeof(char)); //1
printf("%zd\n", sizeof(bool)); //1
printf("%zd\n", sizeof(short)); //2
printf("%zd\n", sizeof(int)); //4
printf("%zd\n", sizeof(long)); //4
printf("%zd\n", sizeof(long long)); //8
printf("%zd\n", sizeof(float)); //4
printf("%zd\n", sizeof(double)); //8
printf("%zd\n", sizeof(long double)); //8
return 0;
}

在 C 语 言 中,signed(有 符 号)和 unsigned(无 符 号)属 于 用 来 修 饰 整 数 类 型 的 关 键 字,它 们 会 对 整 数 类 型 可 表 示 的 值 的 范 围 以 及 存 储 方 式 产 生 影 响。在 上 面 的 代 码 中,代 码 里 所 涉 及 的 各 种 数 据 类 型 均 为 有 符 号 类 型。如 果 数 据 类 型 前 面 没 有 加 signed 和 unsigned 则 默 认 这 个 数 据 类 型 是 有 符 号 的;反 之,则 是 无 符 号 的。
有 符 号 数(signed)
表 示 范 围
有 符 号 类 型 的 整 数 使 用 最 高 位( 即 最 左 边 的 位)作 为 符 号 位,用 来 表 示 数 值 的 正 负。符 号 位 为 0 时,表 示 正 数;符 号 位 为 1 时,表 示 负 数。其 余 的 位 用 于 表 示 数 值 的 大 小。
以 有 符 号 signed char 为 例 计 算 取 值 范 围
有 符 号 char 类 型 通 常 用 8 位 二 进 制 来 表 示,其 中 最 高 位 为 符 号 位,剩 余 7 位 用 于 表 示 数 值。
正 数 范 围
当 符 号 位 为 0 时,代 表 正 数。此 时 剩 余 7 位 能 表 示 的 最 大 正 数 为 0111 1111,转 换 为 十 进 制 就 是2 ^ 6 + 2 ^ 5 + 2 ^ 4 + 2 ^ 3 + 2 ^ 2 + 2 ^ 1 + 2 ^ 0 = 127)。所以,有符号 char 类型的最大正数是 127。以 此 类 推,最 小 正 数 是 0000 0000,转 化 为 十 进 制 是 0。
负 数 范 围
当 符 号 位 为 1 时,代 表 负 数。在 计 算 机 中,负 数 以 补 码 形 式 存 储。最 小 负 数 的 补 码 为 1000 0000,规 定 对 应 的 十 进 制 数 是 -128。以 此 类 推,最 大 负 数 是 1111 1111,转 化 为 十 进 制 是 -1。

存 储 方 式 :负 数 通 常 采 用 补 码 形 式 存 储。
无 符 号 (unsigned)
无 符 号 类 型 的 整 数 没 有 符 号 位,所 有 的 位 都 用 于 表 示 数 值 的 大 小。因 此,无 符 号 类 型 只 能 表 示 非 负 整 数。
以 无 符 号 unsigned char 为 例 计 算 取 值 范 围
最 小 值
最 小 的 无 符 号 char 数 值 是 8 位 全 为 0,即 0000 0000,对应的十进制数是 0。
最 大 值
最 大 的 无 符 号 char 数 值 是 8 位 全 为 1,即 1111 1111,转 换 为 十 进 制 就 是 (2 ^ 7 + 2 ^ 6 + 2 ^ 5 + 2 ^ 4 + 2 ^ 3 + 2 ^ 2 + 2 ^ 1 + 2 ^ 0 = 255)。所 以,无 符 号 char 的 取 值 范 围 是 0 到 255。

存 储 方 式:无 符 号 整 数 直 接 以 二 进 制 的 形 式 存 储。
下 面 使 用 代 码验 证 结 果
。
javascript
#include <stdio.h>
#include <limits.h>
int main()
{
//打印有符号char的取值范围
printf("有符号 char 的最小值: %d\n", SCHAR_MIN);
printf("有符号 char 的最大值: %d\n", SCHAR_MAX);
//打印无符号char的取值范围
printf("无符号 char 的最小值: %u\n", 0);
printf("无符号 char 的最大值: %u\n", UCHAR_MAX);
return 0;
}

整 形 提 升
定 义
整 形 提 升 是 指 在 表 达 式 求 值 时,把 小 于 int 类 型 的 整 数 类 型(如 char、short 等)转 换 为 int 类 型( 如 果 int 能 完 整 表 示 原 类 型 的 值)或 者 unsigned int 类 型( 如 果 int 不 能 完 整 表 示 原 类 型 的 值)的 过 程。
规 则
有 符 号 类 型:如 果 原 始 类 型 是 有 符 号 的,且 其 值 可 以 用 int 类 型 表 示,那 么 提 升 后 的 类 型 为 int;如 果 原 始 类 型 的 值 超 出 了 int 的 表 示 范 围,提 升 后 的 类 型 为 unsigned int。通 常,像 signed char、signed short 等 有 符 号 类 型,只 要 值 在 int 的 表 示 范 围 内,都 会 提 升 为 int。
无 符 号 类 型:如 果 原 始 类 型 是 无 符 号 的,当 int 能 够 表 示 该 无 符 号 类 型 的 所 有 可 能 值 时,提 升 为 int;否 则,提 升 为 unsigned int。例 如,unsigned char 和 unsigned short 类 型,如 果 其 最 大 值 能 被 int 表 示,就 会 提 升 为 int,否 则 提 升 为 unsigned int。
原 因
CPU 内 整 型 运 算 器 ( ALU ) 的 操 作 数 的 字 节 长 度 一 般 就 是 int 的 字 节 长 度,同 时 也 是 CPU 的 通 用 寄 存 器 的 长 度。即 使 两 个 char 类 型 的 相 加 ,在 CPU 执 行 时 实 际 上 也 要 先 转 换 为 CPU 内 整 型 操 作 数 的 标 准 长 度。通 用 CPU 是 难 以 直 接 实 现 两 个 8 比 特 字 节 直 接 相 加 运 算,虽 然 机 器 指 令 中 可 能 有 这 种 字 节 相 加 指 令。表 达 式 中 各 种 长 度 可 能 小 于 int 长 度 的 整 型 值,都 必 须 先 转 换 为 int 或 unsigned int, 然 后 才 能 送 入 CPU 去 执 行 运 算。
保 证 计 算 精 度:int 类 型 通 常 是 计 算 机 中 最 适 合 进 行 整 数 运 算 的 类 型,将 整 数 类 型 提 升 为 int 可 以 利 用 计 算 机 的 整 数 运 算 单 元,提 高 计 算 效 率 和 精 度。
统 一 操 作 数 类 型:在 表 达 式 中,不 同 类 型 的 操 作 数 进 行 运 算 时,需 要 统 一 类 型,整 形 提 升 可 以 确 保 操 作 数 类 型 一 致,避 免 因 类 型 不 匹 配 导 致 的 错 误。
下 面 展 示代 码 示 例
。
javascript
#include<stdio.h>
int main()
{
char a = 3;
//截断
//00000000000000000000000000000011 --- 补码
//00000011 --- a
char b = 123;
//00000000000000000000000001111011 --- 补码
//01111011 --- b
char c = a + b;
//char c = (int)a + (int)b;
//00000011 --- a
//01111011 --- b
//整形提升
//00000000000000000000000001111011 --- a的整形提升 --- 前面补充最高位
//00000000000000000000000000000011 --- b的整形提升 --- 前面补充最高位
//00000000000000000000000001111110 --- a+b
//01111110 --- 存入c中发生截断
//%d -- 打印十进制的整数
//00000000000000000000000001111110 --- 补码 --- 整形提升 --- 前面补充最高位
printf("%d\n", c);
return 0;
}
总 结
截断 整形提升前面补充最高位 截断 整形提升前面补充最高位 对应二进制位相加 整形提升前面补充最高位 取反+1 a的补码 保留后8位 32位 b的补码 保留后8位 存入c中发生截断 补码 原码
如 果 学 会 了 可 以 尝 试 算 一 下 这 道 题。
下 面 展 示代 码 示 例
。
javascript
#include<stdio.h>
int main()
{
char a = 3;
char b = 127;
char c = a + b;
printf("%d\n", c);
return 0;
}
温 馨 提 示:读 者 们 ,先 自 己 计 算 答 案,这 是 提 升 编 程 能 力 的 好 机 会。若 未 达 要 求 ,别 气 馁 ,参 考 下 文 解 释 会 有 新 收 获。

下 面 展 示
代 码 示 例
。
javascript
#include<stdio.h>
int main()
{
char a = 3;
//00000000000000000000000000000011
//00000011 --- a
char b = 127;
//00000000000000000000000001111111
//01111111 --- b
char c = a + b;
//00000011 --- a
//01111111 --- b
//整形提升 --- 补符号位
//00000000000000000000000000000011
//00000000000000000000000001111111
//00000000000000000000000010000010
//10000010 --- c
//%d --- 整形提升
//11111111111111111111111110000010 --- 补最高位
//10000000000000000000000001111101
//10000000000000000000000001111110 --- 原码
printf("%d\n", c);
return 0;
}
下 面 展 示
代 码 示 例
。
javascript
#include <stdio.h>
int main()
{
unsigned char i = 0;
for (i = 0;i <= 255;i++)
{
printf("hello world\n");
}
return 0;
}
结 果 解 释
这 段 代 码 会 死 循 环 打 印 hello world ,对 于 一 个 8 位 的 无 符 号 整 数( unsigned char ),其 取 值 范 围 是 0 到 255。当 i 的 值 为 255 时,它 的 二 进 制 表 示 是 1111 1111。执 行 i++ 操 作,相 当 于 在 二 进 制 层 面 进 行 加 1 运 算:

由 于 类 型 为 unsigned char,发 生 整 形 提 升,保 留 后 8 位 ,i 的 值 是 0000 0000,化 为 十 进 制 是 0,然 后 不 断 打 印,最 终 成 为 死 循 环。
算 数 转 换
定 义
算 术 转 换 是 一 种 自 动 类 型 转 换 机 制,它 主 要 发 生 在 不 同 数 据 类 型 参 与 算 术 运 算 时,目 的 是 确 保 操 作 数 具 有 兼 容 的 类 型,以 顺 利 进 行 运 算。
规 则
整 数 提 升:
首 先 会 对 所 有 比 int 类 型 短 的 整 数 类 型(如 char、short 等)进 行 整 形 提 升,将 它 们 转 换 为 int 类 型。
- 类 型 转 换:
如 果 两 个 操 作 数 类 型 不 同,会 将 较 低 等 级 的 类 型 转 换 为 较 高 等 级 的 类 型,直 到 两 个 操 作 数 类 型 相 同。
类 型 等 级 从 低 到 高 大 致 顺 序 为:char < short < int < long < long long < float < double < long double
不 同 整 数 类 型 的 运 算
下 面 展 示代 码 示 例
。
javascript
#include <stdio.h>
int main()
{
short a = -10;
//整形提升
//1000000000000000000000000001010 --- 原码
//1111111111111111111111111110101 --- 反码
//1111111111111111111111111110110 --- 补码
//11110110 --- 截断
int b = 20;
//00000000000000000000000000010100 --- b
int result = a + b;
//0000000000001010 --- a
//11111111111111111111111111110110 --- 补符号位
//00000000000000000000000000010100 --- b
//00000000000000000000000000001010 --- 2+8 --- 10
printf("Result: %d\n", result);
return 0;
}
注意
从 右 向 左 数 第 5 位 按 照 二 进 制 加 法 " 逢 二 进 一" 规 则, 本 位 留 0 ,向 高 位 进 1。以 此 类 推,最 后 结 果 超 出 int 类 型 的 32 位 发 生 截 断,所以结果为10。
复杂表达式的求值]
三 个 影 响 的 因 素:
- 操 作 符 的 优 先 级
- 操 作 符 的 结 合 性
- 是 否 控 制 求 值 顺 序
下 面 详 细 介 绍 C 语 言 操 作 符 的 三 个 常 见 属 性。
优 先 级
操 作 符 的 优 先 级 确 定 了 在 表 达 式 中 操 作 符 执 行 的 先 后 顺 序。优 先 级 高 的 操 作 符 会 先 被 计 算。
结 合 性
当 一 个 表 达 式 中 存 在 多 个 优 先 级 相 同 的 操 作 符 时,结 合 性 决 定 了 操 作 符 的 计 算 顺 序。结 合 性 分 为 左 结 合 和 右 结 合。左 结 合 表 示 从 左 到 右 依 次 计 算,右 结 合 则 表 示 从 右 到 左 依 次 计 算。
求 值 顺 序
在 大 多 数 情 况 下,C 语 言 并 不 保 证 表 达 式 中 操 作 数 的 求 值 顺 序。例 如,在 表 达 式 a + b * c 中,虽 然 * 的 优 先 级 高 于 +,但 C 语 言 并 不 保 证 b 和 c 的 求 值 顺 序。这 可 能 会 导 致 一 些 意 外 的 结 果。

总结
至 此,关 于 C 语 言 整 形 提 升 的 探 索 暂 告 一 段 落,但 你 的 编 程 征 程 才 刚 刚 启 航。写 代 码 是 与 机 器 深 度 对 话,过 程 中 虽 会 在 语 法、算 法 困 境 里 挣 扎,但 这 些 磨 砺 加 深 了 对 代 码 的 理 解。愿 你 合 上 电 脑 后,灵 感 不 断,在 C 语 言 的 世 界 里 持 续 深 耕,书 写 属 于 自 己 的 编 程 传 奇,下 一 次 开 启 ,定 有 全 新 的 精 彩 等 待。小 编 期 待 重 逢,盼 下 次 阅 读 见 你 们 更 大 进 步,共 赴 代 码 之 约!