keil 里的补码妙用(keil下的C51)

本文详细解析了单片机中不同数据类型(有符号与无符号字符型)的存储方式及其运算原理,通过具体实例展示了补码、反码及原码的概念,并解释了如何在内存中表示正负数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上程序:

#include "STC15W4K.H"      // 注意宏定义语句后面无分号
void main()
{ 
    unsigned char  a=0;  // a定义为无符号字符型(0-255),
    char  b=0;           // b定义为带符号字符型(-128~127)
    while(1)
    {
        a=-3;         // 执行后 a=1111 1101,KEIL输出显示 253
                      // 分析:[-3]=[1000 0011]原=[1111 1100]反=[1111 1101]补 
                      // 由于a定义为无符号字符型,[[1111 1101]补]补= 1111 1101=253                  
        b=-3;         // 执行后 b=1111 1101   KEIL输出显示 -3                      
        a = 127;      // 执行后 a=0111 1111   KEIL输出显示 127
        b = 127;      // 执行后 b=0111 1111   KEIL输出显示 127
        a = a + 3;    // 执行后 a=1000 0010   KEIL输出显示 130
        b = b + 3;    // 执行后 b=1000 0010   KEIL输出显示 -126
    }
}

分析:记住一点,为了负数的计算方便,计算机对数的存储均是按照补码的形式存储的。单片机也是如此, 所以内存中运算就很方便了,直接按照无符号相加就行了。
正数的补码等于反码等于原码。
负数的补码等于原码取反再加1。

第一个结果 a = -3

-3 对应的原码为 1000 0011, 对应的补码为反码再加1(除去符号位),即是: 1111 1100 + 1 = 1111 1101 这就是-3在内存中的存储值。现在把它赋值给a,。也就是说a的补码现在就是1111 1101。至于对应的原码是多少,这之前编译器要看一下a是有符号数,还是无符号数。一看a是无符号数,那么最高位就无所谓了,数据位就是8位。那么好了,补码就等于原码。原码为1111 1101 就是253咯

注意:负数原码中最高位为符号位,数据位只有7位(2^7)。正数的数据位为8位(2^8)这也侧面反映了一点,正数的取值范围是 0~255,负数的取值范围为-128~127!

第二个结果b=-3

同时此时b在内存中对应的补码就是 1111 1101了,至于它对应的原码多少。还得看它的定义类型是啥。一看是char, 有符号,好了。那么对应最高位就是一个符号位了。求对应的原码方法就是除去最高位其他位再求补码。
1 000 0010 + 1 = 1 000 0011 显然就是-3咯。

第三个结果a=127

单纯就看127,为正数。在内存中的补码就等于原码为:0111 1111 也就是现在a对应内存的补码就是0111 1111了,再看一下a是无符号且是正数,那么好了。补码就是原码,a就是127了。

第四个结果b = 127

同上,b在内存中对应的补码为 0111 1111, 再看一下b是有符号,但是符号位为0,有符号char的取值范围是 -128~127没超过,那么也即是正数,所以补码等于原码,就是127了。

第五个结果a+=3;

127+3=130没超过无符号char的取值,所以结果就是130

第六个结果 b+=3

先大概一瞅肯定会溢出,至于结果是几,且来分析一般。
b对应补码为(这两补码相加即可) 0111 1111 + 0000 0011 = 1000 0010 这就是b现在对应的补码。且b为无符号数,最高位为1,负数。好了,原码等于补码的补码。即是 1 111 1101 + 1 = 1111 1110
表示为1 111 1110 = -126


总结一下规律, 先不用考虑左边对应变量的数据类型或者溢出什么的。先把右边对应的数的补码写出来(正数补码为自身,负数补码为反码加一)。然后再看对应的事有符号数还是无符号数。


两个特殊的0和128

正0的补码等于负0的补码等于0
但是-128的补码似乎没法表示了,因为它的原码和反码没法7位表示
可以这么想,-1是最大的负整数,-1的补码1111 1111减去1得到-2的补码
1111 1110,那么-127的补码 1000 0001减去1不就得到了-128的补码吗?!就是1000 0000

补充: 负数的补码快速运算为:符号位不变。其他的从低位开始,直到遇到第一个1之前,什么都不变。遇到第一个1后,保留这个1,以后按位取反。

[-7]原 = 1000 0111
[-7]补 = 1111 1001

数值在内存中确实是按补码形式存放的,数据长度超过最高有效位后,直接去掉最高有效位外的符号和数值。

<think>嗯,用户之前问了关于蓝桥杯单片机的问题,现在转而询问Keil4中long int的取值范围。可能需要先确认用户的使用场景。蓝桥杯比赛常用单片机,比如51系列或者STM32,不同的单片机架构在Keil4中使用的编译器可能不同,进而影响数据类型的取值范围。 首先,Keil4本身是一个IDE,但具体的C编译器会根据目标芯片不同而变化。对于51单片机,通常使用C51编译器;而对于ARM架构如STM32,可能使用ARMCC或Clang。不同的编译器对数据类型的处理可能不同。 用户提到的是long int,但需要明确是C51还是ARM编译器下的情况。在C51中,long int通常是4字节,范围是-2,147,483,648到2,147,483,647。而如果是ARM编译器,可能也是类似的,但需要确认是否使用了不同的编译选项,比如是否启用了C99标准或者不同的数据模型。 此外,用户可能是在编程时遇到了溢出或者数值不准确的问题,需要了解数据类型的范围来避免错误。需要提醒用户注意不同编译器下的差异,以及可能的隐式类型转换问题。 另外,用户之前的询问涉及硬件相关的电压值,现在转向软件开发环境的数据类型,可能是在项目中同时涉及硬件和软件调试。需要给出明确的取值范围,并建议查阅编译器文档以确认,同时举例说明如何验证,比如使用sizeof运算符或者查看limits.h头文件。 需要确保回答准确,避免混淆不同编译器的差异,同时提供实际的例子帮助用户理解。可能用户对C语言的数据类型在嵌入式系统中的具体实现不太熟悉,因此需要详细解释,并强调编译器的影响。</think>在 **Keil μVision 4**(Keil4)中,`long int` 的取值范围与**编译器类型**和**目标芯片架构**密切相关。以下是详细解析: --- ### 一、核心结论 - **Keil C51 编译器**(用于 51 单片机): `long int` 为 **4 字节**,取值范围:**-2,147,483,648 ~ 2,147,483,647** (即 $-2^{31} \sim 2^{31}-1$)。 - **Keil ARM 编译器**(如 ARMCC,用于 Cortex-M 系列): `long int` 为 **4 字节**,取值范围与 C51 相同(标准 C 语言规范)。 --- ### 二、详细说明 #### 1. **Keil C51 编译器(8051 单片机)** - **数据类型定义**: - `long int`:4 字节(32 位),有符号整数。 - 二进制补码表示,最高位为符号位。 - **范围公式**: $$ -2^{31} \leq \text{值} \leq 2^{31}-1 $$ 即 **-2,147,483,648 ~ 2,147,483,647**。 - **验证方法**: 在代码中添加以下语句,观察编译结果或运行时行为: ```c #include <limits.h> printf("LONG_MIN=%ld, LONG_MAX=%ld", LONG_MIN, LONG_MAX); ``` #### 2. **Keil ARM 编译器(Cortex-M 系列)** - **遵循 C 标准**: `long int` 严格遵循 **ISO C 标准**,定义与 C51 一致(4 字节,相同取值范围)。 - **扩展支持**: 若需更大范围,可使用 `long long int`(8 字节,范围 $-2^{63} \sim 2^{63}-1$)。 --- ### 三、常见问题 #### 1. **为什么不是 8 字节?** - `long int` 在 **嵌入式系统** 中通常定义为 4 字节,以节省内存和提升效率。 - 若需 8 字节整数,需使用 `long long int`(注意兼容性)。 #### 2. **跨平台移植风险** - 在 PC 编程(如 Visual Studio)中,`long int` 可能为 8 字节(64 位系统)。 - **建议**:使用 `<stdint.h>` 中的明确类型(如 `int32_t`、`int64_t`)避免歧义。 #### 3. **隐式类型转换问题** - 若赋值超出 `long int` 范围,会触发**溢出**(编译器可能不报错,但数值错误)。 - 示例: ```c long int a = 2147483648; // 超出最大值,实际值变为 -2,147,483,648 ``` --- ### 四、总结 - **Keil4 中 `long int` 的固定范围**: **-2,147,483,648 ~ 2,147,483,647**(4 字节)。 - **关键依赖**: 编译器类型(C51 或 ARM)不影响 `long int` 的定义,但需注意芯片资源限制。 - **推荐实践**: - 需要大范围整数时,使用 `long long int`; - 严格使用 `<stdint.h>` 类型保证代码可移植性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReCclay

如果觉得不错,不妨请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值