栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

如何打印一个十进制数的二进制补码(二补数,two‘s complement)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何打印一个十进制数的二进制补码(二补数,two‘s complement)

作者是C语言初学者,遇到这个问题后思考了很久,终于成功地解决了问题。我想把我的探索之路分享出来,希望在帮到各位的同时,给各位一点关于指针,堆栈,递归的启示。


在网上查阅相关资料后,我确定了一种将十进制数转化为二进制补码的方法:强制转换为unsigned类型,因此,我写了如下函数:

但是因为补码的特性, 当输入负数的时候,就会因为该二进制的十进制表达的数值过大而溢出(如,二进制数11111111111111111111111111111111转换为十进制表达11111111111111111111111111111111的时候就超出了界限,而且这种溢出几乎是不可通过增大类型容量来去除的)

因此,这种方法不行。

我当时就想,能否不将它转换为十进制表达一次性打印,而是将它的每一位上的数一个个地分批次地打印?

我冥思苦想,终于想到一个具有极其特殊的性质的方法:递归

众所周知,递归会在调用函数结束之后逆向地执行剩下的步骤,那么,我们能不能利用这个特性,先打印补码的第一位(也就是最后一个计算得到的数),然后依次打印呢?理论上应该是可行的(但因此会创建非常多的堆栈,对速度有很大影响)

于是,我尝试通过递归的特性利用“退潮”式打印打印补码

理论上,只要操作得当,将每次的余数存储在一个数组中,在递归最后一次调用函数后,就会开始进行调用函数之后的打印工作,从而形成一种奇特的从后向前打印的景观

但是实际操作的时候,发现所有的计算都是正确的,但是到了最后的打印的时候,却出现了未曾预料的打印错误

源代码如下:

 

 

似乎是数组参数传递时出错了?

曾经想过用指针解决这个问题,但是指针本身的复杂性令问题更加晦涩,再加上指针传递也得将一系列的连续的地址传过去......等等,连续的?!这岂不是意味着我可以通过在函数中对地址进行递增操作来控制内存??

这样,或许数组传递的问题就解决了

还有一个问题,真正的传统的不依赖指针的数组传递是什么样的?我决定再熬一会夜解决这个问题。

问题2:数组作为参数传递

奇怪!数组的传递和我的方法一般无二!我决定再调试一次找出真正的问题所在。


发现1

数组传递时,转递的类型是int*,也就是将后面的变量当成了指针,因此传递的是该指针所指向的值

 

发现2

当rest[count]表达式出现时,居然凭空诞生了一个区别于数组变量rest的变量rest[count]!也就是说,rest[count]或许根本不在rest的列表之内!也就是说,传递的数组和真正的数组绝对完全不一样!那我们传递的到底是个什么东西??为什么网络上的数组传递方法都是这样的?难道是已经过时的文档?还是在VS中以cpp文件编写C语言的时候与传统C语法要求有所不同?还是传递方法是对的,但是另有隐情?

发现3

在rest[count]值变化的时候,rest列表中唯一的值居然也跟着变化了!

 

 

这个rest[count]与rest列表绝对不是完全无关的!

等等...为什么rest[count]和rest列表中的唯一值是一样的...难道他们本来就是一个变量?

发现4

递归中函数调用完毕后剩下的步骤的执行次数是由堆栈的数目决定的

 

所以输入3时才会打印3次1!因为创建了3个堆栈!

我能否让最后一个堆栈不打印呢?

或许将打印操作放到循环中可以不打印?


尝试1

经过咨询,指针代表的是实际地址,那么我们可以根据数组地址是连续的特性来解决参数传递的问题

利用如下代码进行测试:

 

输出结果:

这就意味着,数组中的数是连续排列的,通过指针加一能够获得下一个数的地址

调试结果:

 

这说明了在int数组中每个元素地址相隔4,但指针只相差1

如果换成char数组:

 

会发现,char数组中每个元素地址相差1,指针相差1

下面进行参数传递测试:

 

进行调试后发现,因为p和p_1作为左值可以赋予不同的值,所以直接对p和p_1进行赋值操作完全没法影响到原变量值

查阅后更改为:

 

这里,*i指的是i背后的那个值,这个赋值操作的意思是:将1赋给指针i指向的那个值,也就是rest[0]

果不其然,成功了:

 

那么,我们就可以更改我们的原函数了。

尝试2

尝试通过指针更改原堆栈中的值的特性,和迭代退潮式打印的特性来打印二进制补码

代码如下:

void to_binary(unsigned long long int *ori ,
               int *addr) {
    
    while (*ori != 0) {
        
        *addr = *ori % 2;
        addr++;
        *ori = *ori / 2;
        to_binary(ori,addr);
        printf("%d", *(--addr));
    }
    
}
​
​
int main(void) {
    int rest[100];
    
    unsigned long long int ori = 3;
    to_binary(&ori, &rest[0]);
}

打印成功了!

接下来要解决负数的问题。

尝试3

通过强制转换将负数纳入体系

成功:

void to_binary(unsigned long long int *ori ,int *addr) {
    
    while (*ori != 0) {
        
        *addr = *ori % 2;
        addr++;
        *ori = *ori / 2;
        to_binary(ori,addr);
        printf("%d", *(--addr));
    }
    
}
​
​
int main(void) {
    int rest[100];
    int ori = -3;
    unsigned long long int ori_ = (unsigned long long)ori;
    to_binary(&ori_, &rest[0]);
}
尝试4

做出最终产品

成功:

void to_binary(unsigned long long int *ori ,int *addr) {
    
    while (*ori != 0) {
        
        *addr = *ori % 2;
        addr++;
        *ori = *ori / 2;
        to_binary(ori,addr);
        printf("%d", *(--addr));
    }
    
}
​
​
int main(void) {
    int rest[300];
    while (1) {
        int ori = 0;
        printf("n请输入要打印补码的数:");
        scanf_s("%d", &ori);
        unsigned long long int ori_ = (unsigned long long)ori;
        to_binary(&ori_, &rest[0]);
    }
}

再次声明,作者水平不高,写这篇文章只是纯粹想要给同样有这些困惑的人以一些启示,文中代码可能会有不规范之处,用语也可能不够严谨,存在很多比喻式术语,如果您无法忍受,可以去看别的文章;如果能帮到您,希望您能将这篇文章分享给有同样困惑的人,谢谢合作

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/296207.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号