最近再次复习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. Android属性动画源代码解析(超详细)

    本文假定你已经对属性动画有了一定的了解,至少使用过属性动画.下面我们就从属性动画最简单的使用开始. ObjectAnimator .ofInt(target,propName,values[]) .s ...

  2. C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字

    C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字 上一篇实现了把文字绘制到OpenGL窗口,但实质上只是把含有文字的贴图贴到矩形模型上.本篇我们介绍用Poi ...

  3. [ASP.NET MVC 小牛之路]01 - 理解MVC模式

    本人博客已转移至:http://www.exblr.com/liam  PS:MVC出来很久了,工作上一直没机会用.出于兴趣,工作之余我将展开对MVC的深入学习,通过博文来记录所学所得,并希望能得到各 ...

  4. Azure PowerShell (6) 设置单个Virtual Machine Endpoint

    <Windows Azure Platform 系列文章目录> 请注意: - Azure不支持增加Endpoint Range - 最多可以增加Endpoint数量为150 http:// ...

  5. C#中,使用正式表达式匹配获取所需数据

    .NET中,使用正式表达式匹配获取所需数据 需求:获取一串字符串中,正则匹配出需要的数据. 例如以下字符串: string temp ="ErrorCode:-1,Message:{&quo ...

  6. 在SqlServer2008R2中,在一张表上加上insert、update、delete触发器(带游标)

    在日常工作中,在SqlServer2008R2中,需要向一张表上加上触发器,监控插入.更新.删除. --一个触发器内三种INSERT,UPDATE,DELETE状态 IF exists(select ...

  7. web安全测试资料

    最近因为工作需要,整理了安全测试工具AppScan的一个教程.目录如下: 网上对于appscan的资料挺多,但是也很乱很杂.不利于系统的学习,这也是我为什么整理这样一份指导手册. 在这份手册里,主要包 ...

  8. Unity 文件读取

    存储: 在程序发布后文件的存放有两种,第一种是打包到Uniyt的资源包中(*.unity3D),第二种就是把文件存放在一个特殊的目录如:StreamingAssets,这个目录的东西Unity不会打包 ...

  9. 连接 insance 到 vlan101 - 每天5分钟玩转 OpenStack(97)

    前面我们创建了 vlan101,今天继续部署 instance 到该 vlan network, 并讨论 instance 之间的连通性. launch 新的 instance "cirro ...

  10. 准备 KVM 实验环境 - 每天5分钟玩转 OpenStack(3)

    KVM 是 OpenStack 使用最广泛的 Hypervisor,本节介绍如何搭建 KVM 实验环境 安装 KVM 上一节说了,KVM 是 2 型虚拟化,是运行在操作系统之上的,所以我们先要装一个 ...