【C语言学习趣事】_33_关于C语言和C++语言中的取余数(求模)的计算_有符号和无符号数的相互转换问题
最近再次复习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++语言中的取余数(求模)的计算_有符号和无符号数的相互转换问题的更多相关文章
- IOS-2-C语言和Objective-C语言衔接学习资料
前言:在IOS学习中.通常会先学习一周的C语言,两周的Objective-C语言,这是今后开发的最基础最重要的部分,以下给大家分享一下培训课上的精简资料: C语言和Objective-C语言衔接学习资 ...
- 混合使用C++语言和Objective-C语言
如果你的源文件扩展名是.m的,你还需要改成.mm,这样编译器才知道你将会在该文件中混合使用C++语言和Objective-C语言.
- C语言和Python语言在存储变量方面的不同
C语言和Python语言在存储变量方面的不同 众所周知,Python是脚本语言,边解释边执行,而C语言是编译型语言 存储变量: C语言定义变量,变量本身代表的就是大小,任何一个字母或者数字 符号均可以 ...
- 开发电商平台用PHP语言和JAVA语言有什么区别?哪种语言更好?
现在很多行业都通过电子商务拓展业务,所以商城系统开发成为很多企业的刚性需求.一般有一点技术基础的客户应该知道目前商城系统开发主流语言有两个,PHP和Java.那么很多客户朋友会纠结是选择哪个语言开发好 ...
- 【C语言学习趣事】_32_平胸的尴尬,嫁不出去的姑娘
为什么写这篇文章呢? 为什么要弄这么个题目呢? 首先解释为什么用这个题目.这一切都要从那天在QQ群中的讨论说起,那天在群中,一个哥们问了一个关于(void)0 的问题.然后大家说到了 (void)0和 ...
- Java语言和C++语言的差异
Java采用了C及C++的语法格式,对于学习过C及C++的程序设计者来说,学习Java将有可能很轻松.但是,如果仔细检查Java语言的许多细节,就会发现Java取消了不少C及C++的特性,并且加入了一 ...
- C语言和go语言之间的交互
一.go代码中使用C代码 go代码中使用C代码,在go语言的函数块中,以注释的方式写入C代码,然后紧跟import "C" 即可在go代码中使用C函数 代码示例: go代码:tes ...
- Go 语言和 Scala 语言对比
我在Google写过Go(自己的业余时间),也在LinkedIn写过Scala.两者都是具有一流的并发特性的现代语言. 下面的回答是基于我编写大规模的软件的经验得出. Go是一种开发模式严格固定,并且 ...
- C语言学习及应用笔记之二:C语言static关键字及其使用
C语言有很多关键字,大多关键字使用起来是很明确的,但有一些关键字却要相对复杂一些.我们这里要说明的static关键字就是如此,它的功能很强大,相应的使用也就更复杂. 一般来说static关键字的常见用 ...
随机推荐
- 如何让用户只能访问特定的数据库(MSSQL)
背景 客户的SQL Server实例上有多个厂商的数据库,每个数据库由各自的进行厂进行商维护, 为了限定不同厂商的维护人员只能访问自己的数据库,现需要给各个厂商限定权限,让他们登录SQL Server ...
- Ubuntu安装Python2.7,nodejs
安装Python2.7 sudo add-apt-repository ppa:fkrull/deadsnakes-python2.7sudo apt-get update sudo apt-get ...
- MySQL GROUP BY用法
背景介绍 最近在设计数据库的时候因为开始考虑不周,所以产生了大量的重复数据.现在需要把这些重复的数据删除掉,使用到的语句就是Group By来完成.为了进一步了解这条语句的作用,我打算先从简单入手. ...
- 手写原生ajax
关于手写原生ajax重要不重要,各位道友自己揣摩吧, 本着学习才能进步,分享大家共同受益,自己也在自己博客里写一下 function createXMLHTTPRequest() { //1.创建XM ...
- thinkPHP入门
什么是框架 框架就是一定结构的代码,框架提供一个开发web程序的基础架构以及常用的功能 代码,PHP框架的web程序开发拜倒了流水线上. php框架就是一定要按别人规定好的架构编写. php开发框架有 ...
- LINQ系列:Linq to Object联接操作符
联接是指将一个数据源对象与另一个数据源对象进行关联或联合的操作.这两个数据源对象通过一个共同的值或属性进行关联. LINQ的联接操作符将包含可匹配(或相同)关键字的两个或多个数据源中的值进行匹配. L ...
- OpenCASCADE Hidden Line Removal
OpenCASCADE Hidden Line Removal eryar@163.com Abstract. To provide the precision required in industr ...
- OpenCascade B-Spline Basis Function
OpenCascade B-Spline Basis Function eryar@163.com Abstract. B-splines are quite a bit more flexible ...
- nodejs+easyui(抽奖活动后台)增删改查
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfAAAAJACAIAAAD97KNZAAAgAElEQVR4nO2daXxb5Z2o7w+dO1/ufL ...
- Unity基础知识学习笔记一
1,Unity 4.5.4这个版本,在打包ios应用程序的时候.会生成一个xcode工程,但是这个工程无法在xcode6上变异,无法生成ios8上发布.所以unity在ios8发布3天内就发了4. ...