【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关键字的常见用 ...
随机推荐
- .NET陷阱之六:从枚举值持久化带来大量空间消耗谈起
好长时间没有写博文了,今天继续. 这次跟大家分享的内容起因于对一个枚举值列表的序列化,下面简化后的代码即能重现.为了明确起见,我显式指定了枚举的基础类型. // 定义一个枚举类型. public en ...
- 《Entity Framework 6 Recipes》中文翻译系列 (37) ------ 第六章 继承与建模高级应用之独立关联与外键关联
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-13 在基类中应用条件 问题 你想从一个已存在的模型中的实体派生一个新的实体, ...
- Atitit 深入理解软件的本质 attilax总结 软件三原则"三次原则"是DRY原则和YAGNI原则的折
Atitit 深入理解软件的本质 attilax总结 软件三原则"三次原则"是DRY原则和YAGNI原则的折 1.1.1. 软件的本质:抽象 1 1.2. 软件开发的过程就是不断 ...
- iOS-----dSYM 文件分析工具
来到新公司后,前段时间就一直在忙,前不久 项目 终于成功发布上线了,最近就在给项目做优化,并排除一些线上软件的 bug,因为项目中使用了友盟统计,所以在友盟给出的错误信息统计中能比较方便的找出客户端异 ...
- JS函数 -- 功能,语法,返回值,匿名函数,自调用匿名函数,全局变量与局部变量,arguments的使用
“JavaScript设计得最出色的就是它的函数的实现.” -- <JavaScript语言精粹> 函数包含一组语句,它们是JS的基础模块单元,用于指定对象的行为.一般来说,所谓编程,就是 ...
- PHP扩展-扩展的生成和编译
首先说明一下,PHP扩展有两种编译方式:方式一:在编译PHP时直接将扩展编译进去方式二:扩展被编译成.so文件,在php.ini里配置加载路径: 以下开始说明创建PHP扩展并编译的步骤:下载PHP源码 ...
- css绝对定位如何在不同分辨率下的电脑正常显示定位位置?
有时候我们在写页面中,会发现绝对定位的父级元素已经相对定位了,但是在不同分辨率的电脑下,绝对定位还是会错乱,似乎父级的相对定位并没有起了作用. 首先要明白如下几个原理: 1.笔记本电脑的分辨率一般为1 ...
- php解决中文截取乱码问题
针对截取字符串出现中文乱码问题,网上有很多介绍,也有很多函数,但笔者看着网上的函数,总感觉有点别扭, 所以自己动手写了一个防止截取字符串时出现中文乱码的函数. 实现的原理还是比较简单,主要是利用ASC ...
- SSISDB1:使用SSISDB管理SSIS Projects
使用Project Deployment Model,将SSIS Project部署到Integration Services Catalog之后,SSISDB负责管理SSIS Project.在SS ...
- java.util.NoSuchElementException: Timeout waiting for idle object
出现这个问题第一个想法就是连接池的参数设置问题,把最大连接数量设置大一些就行了,但是我就一个客服端访问服务器,连接池连接数量不可能会不够用.我的项目架构是spring mvc+hibernate,用s ...