最近再次复习C++语言,用的教材是《C++ Primer》这本教材, 看到第二章的时候,里面有个问题困扰了我。

于是想上网查查怎么回事, 结果看了很久都没有得到一个满意的答案。

书上有这么一段话:当将一个超出数据类型取值范围的值赋值给这个类型的一个变量时,变量的值的结果由变量的

类型决定。

后面还有这么一段解释:

1、当接受值的变量类型为无符号类型时,  变量的值 =  超出变量范围的值 % 类型可以表示的数值的个数

Exp:

unsigned char nTest;
nTest = ;

那么这样nTest的值就可以用下面的方式进行计算:

  nTest = 343 % 256 = 343 - 256 = 87    //中间的343-256 是计算方便,实际上没有这种转换方式

我在RHEL中测试的结果如下:

[root@localhost cpp_src]# cat test.cpp
#include <iostream> int main()
{
unsigned char nTest;
unsigned int n;
nTest = ;
n=nTest; std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"n="<<n<<std::endl;
std::cout<<"343%256="<<%<<std::endl; return ;
}
[root@localhost cpp_src]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:: 警告:大整数隐式截断为无符号类型
[root@localhost cpp_src]# ./a.out
nTest=W
n=
%=

  可以看到测试的时候,对截断数据进行了提示,而且最终得到的结果也是这么计算的。

  让我迷惑的不是上面的过程,上面的过程非常的自然。令我迷惑的是下面的情况:

    当接受值的变量类型为无符号型,而赋给它的值是一个负数,该如何处理。

Exp:

unsigned char nTest;
nTest = -;

  按照上面的说法:   nTest = -1 % 256 ;

问题就出在这个地方,对于负数的求余数,我们该怎么求呢?以前从来没注意这个问题,

这次就想一定要弄明白。

[root@localhost cpp_src]# cat test.cpp
#include <iostream> int main()
{
unsigned char nTest;
unsigned int n;
nTest = -;
n=nTest; std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"n="<<n<<std::endl;
std::cout<<"-1%256="<<-%<<std::endl; return ;
}

执行结果为:

[root@localhost cpp_src]# g++ test.cpp
[root@localhost cpp_src]# ./a.out
nTest=�
n=
-%=-

  经过这个地方的计算,我们并不能得到什么规律,但是我们发现一个问题:  n= 255 = 256 + (-1)

256 是unsigned char 可以表示的数值的个数(8bit可以表示256个数), 而 -1 是 -1 % 256 后得到的余数。

于是我就继续做了几个实验:

[root@localhost cpp_src]# cat test.cpp
#include <iostream> int main()
{
unsigned char nTest;
unsigned int n;
nTest = -;
n=nTest; std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"n="<<n<<std::endl;
std::cout<<"-367%256="<<-%<<std::endl; return ;
}

执行结果为:

[root@localhost cpp_src]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:: 警告:大整数隐式截断为无符号类型
[root@localhost cpp_src]# ./a.out
nTest=�
n=
-%=-

分析:  n=145 = 256 + (-367%256)= 256 + (-111) = 145;

还有下面的实验:

#include <iostream>

int main()
{
unsigned char nTest;
unsigned int n;
nTest = -;
n=nTest; std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"n="<<n<<std::endl;
std::cout<<"-1000%256="<<-%<<std::endl; return ;
}

执行结果如下所示:

[root@localhost cpp_src]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:: 警告:大整数隐式截断为无符号类型
[root@localhost cpp_src]# ./a.out
nTest=
n=
-%=-

分析也是:  n = 24 = 256 + (-1000 % 256) = 256 + (-232) = 24 ;

于是我得到一个结论:

  无符号变量的值 = 类型可以取值的个数 + (负数 % 类型可以取值的个数)

例如8bit的计算公式为:

无符号变量的值 = 256 + (负数 % 256 )

下面通过代码进行测试:

#include <iostream>

int main()
{
unsigned char nTest;
unsigned int n;
int i;
unsigned int j;
for(i=;i<=;i++)
{
nTest = -+i;
j=nTest;
n= +( (-+i) % ); std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"j="<<j<<std::endl;
std::cout<<"n="<<n<<std::endl<<std::endl;
} return ;
}

测试结果为:

[root@localhost cpp_src]# g++ test.cpp
[root@localhost cpp_src]# ./a.out
nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=!
j=
n= nTest="
j=
n=

  通过上面的测试,可以证明我的猜想是正确的。

  接下来的问题如何求取这个余数,因为以前都没考虑过这个问题。我们这里再次来进行猜想:

我们知道 -1000  % 256 = - 232,而  1000/256*256 + (-1000) = -232

-1       % 256  = -1        而 1     /256*256 + (-1) = -1

因此我们这样进行猜想求模公式为:

负数 % 变量类型可以表示数的个数  = 负数 * (-1) / 变量类型可以表示数的个数 * 变量类型可以表示数的个数 + 负数

下面我们用代码进行测试,验证上面的公式是否正确。

#include <iostream>

int main()
{
int n,k;
for(int i=;i<=;i++)
{
n=(-+i)%;
k = (- + i)*(-)/* + (-+i); std::cout<<"n="<<n<<std::endl;
std::cout<<"k="<<k<<std::endl<<std::endl;
} return ;
}

执行结果如下

[root@localhost cpp_src]# g++ mod.cpp
[root@localhost cpp_src]# ./a.out
n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=-

  可以发现我的猜想与实际的结果一样,因此可以确定这种方法是对的。

小结:

  1、 当用正数赋值给无符号数时,如果正数在无符号数表示的值范围内时直接赋值。

           当用正数赋值给无符号数时,如果正数不在无符号数表示的值范围, 即正数 》 表示的值的范围 , 

          得到的结果为:  无符号变量 = 正数的值 % 无符号数类型可以表示的值个数

      2、当用负数给无符号数赋值时, 

    无符号变量的值 = 类型可以取值的个数 + (负数 % 类型可以取值的个数)

  3、负数 % 正数 的求模公式

             负数 % 类型可以取值的个数  = 负数 * (-1) / 类型可以取值的个数* 类型可以取值的个数 + 负数

    结合上面的第二条和第三条就可以得到有符号数给无符号数赋值时的最终求值公式。

   无符号变量的值 =  类型可以取值的个数 + 负数 * (-1) / 类型可以取值的个数 * 类型可以取值的个数 + 负数

  可以利用上面的公式进行测试:

unsigned char n;
unsigned int k; n = -;
k=n;

程序的执行结果如下:

[root@localhost cpp_src]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:: 警告:大整数隐式截断为无符号类型
[root@localhost cpp_src]# ./a.out
n=U
k=

用手代入公式计算:

  无符号变量的值 =  类型可以取值的个数 + 负数 * (-1) / 类型可以取值的个数 * 类型可以取值的个数 + 负数

n = 256 + (-171)* -1 / 256 * 256 + (-171)  = 85

结果正确。

    至此,本次问题的讨论到此结束,如果您有更好的计算方法,请您不吝赐教。

如果您觉得我说的不正确,也请您不吝赐教。

【C语言学习趣事】_33_关于C语言和C++语言中的取余数(求模)的计算_有符号和无符号数的相互转换问题的更多相关文章

  1. IOS-2-C语言和Objective-C语言衔接学习资料

    前言:在IOS学习中.通常会先学习一周的C语言,两周的Objective-C语言,这是今后开发的最基础最重要的部分,以下给大家分享一下培训课上的精简资料: C语言和Objective-C语言衔接学习资 ...

  2. 混合使用C++语言和Objective-C语言

    如果你的源文件扩展名是.m的,你还需要改成.mm,这样编译器才知道你将会在该文件中混合使用C++语言和Objective-C语言.

  3. C语言和Python语言在存储变量方面的不同

    C语言和Python语言在存储变量方面的不同 众所周知,Python是脚本语言,边解释边执行,而C语言是编译型语言 存储变量: C语言定义变量,变量本身代表的就是大小,任何一个字母或者数字 符号均可以 ...

  4. 开发电商平台用PHP语言和JAVA语言有什么区别?哪种语言更好?

    现在很多行业都通过电子商务拓展业务,所以商城系统开发成为很多企业的刚性需求.一般有一点技术基础的客户应该知道目前商城系统开发主流语言有两个,PHP和Java.那么很多客户朋友会纠结是选择哪个语言开发好 ...

  5. 【C语言学习趣事】_32_平胸的尴尬,嫁不出去的姑娘

    为什么写这篇文章呢? 为什么要弄这么个题目呢? 首先解释为什么用这个题目.这一切都要从那天在QQ群中的讨论说起,那天在群中,一个哥们问了一个关于(void)0 的问题.然后大家说到了 (void)0和 ...

  6. Java语言和C++语言的差异

    Java采用了C及C++的语法格式,对于学习过C及C++的程序设计者来说,学习Java将有可能很轻松.但是,如果仔细检查Java语言的许多细节,就会发现Java取消了不少C及C++的特性,并且加入了一 ...

  7. C语言和go语言之间的交互

    一.go代码中使用C代码 go代码中使用C代码,在go语言的函数块中,以注释的方式写入C代码,然后紧跟import "C" 即可在go代码中使用C函数 代码示例: go代码:tes ...

  8. Go 语言和 Scala 语言对比

    我在Google写过Go(自己的业余时间),也在LinkedIn写过Scala.两者都是具有一流的并发特性的现代语言. 下面的回答是基于我编写大规模的软件的经验得出. Go是一种开发模式严格固定,并且 ...

  9. C语言学习及应用笔记之二:C语言static关键字及其使用

    C语言有很多关键字,大多关键字使用起来是很明确的,但有一些关键字却要相对复杂一些.我们这里要说明的static关键字就是如此,它的功能很强大,相应的使用也就更复杂. 一般来说static关键字的常见用 ...

随机推荐

  1. 东哥读书小记 之 《MacTalk人生元编程》

         一直以来的自我感觉:自己是个记性偏弱的人.反正从小读书就喜欢做笔记(可自己的字写得巨丑无比,尼玛不科学呀),抄书这事儿真的就常发生俺的身上. 因为那时经常要背诵课文之类,反正为了怕自己忘记, ...

  2. OData的初步认识

    What – OData是什么? OData - Open Data Protocal,是一个设计和使用RESTful API的标准.REST本身只是一个构建web服务的思想和理念,其没有规定一个统一 ...

  3. Underscore.js使用

    Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象. 他解决了这个问题:"如果我面对一个空白的 ...

  4. webpack + vuejs 基本配置(一)

    开始之前 本文包含以下技术,文中尽量给与详细的描述,并且附上参考链接,读者可以深入学习: 1.webpack2.Vue.js3.npm4.nodejs —- 这个就不给连接了,因为上面的连接都是在你实 ...

  5. sizzle分析记录:关于querySelectorAll兼容问题

    querySelector和querySelectorAll是W3C提供的新的查询接口 目前几乎主流浏览器均支持了他们.包括 IE8(含) 以上版本. Firefox. Chrome.Safari.O ...

  6. Mesh Data Structure in OpenCascade

    Mesh Data Structure in OpenCascade eryar@163.com 摘要Abstract:本文对网格数据结构作简要介绍,并结合使用OpenCascade中的数据结构,将网 ...

  7. 纯CSS照片墙

    css中transform参考CSS3属性transform详解之(旋转:rotate,缩放:scale,倾斜:skew,移动:translate 效果图:

  8. 深入学习jQuery选择器系列第五篇——过滤选择器之内容选择器

    × 目录 [1]contains [2]empty [3]parent[4]has[5]not[6]header[7]lang[8]root 前面的话 本文介绍过滤选择器中的内容选择器.内容选择器的过 ...

  9. Neutron Vlan Network 原理- 每天5分钟玩转 OpenStack(92)

    前面我们陆续学习了 Neutron local network,flat network 和 DHCP 服务,从本节将开始讨论 vlan network. vlan network 是带 tag 的网 ...

  10. 在.NET开发面向Oracle数据库的应用程序

    其实这个不是一个什么新的话题.但是之前在多次项目中,总是遇到大家针对Oracle数据库的访问时,会有各种各样的问题,最基本的就是要在客户端安装各种client,版本不一样的话还有各种问题. 静下心来看 ...