【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关键字的常见用 ...
随机推荐
- VBA批量查找和复制文件
Function findAndCopy(srcFile As String, destFile As String, cmdFile As String) Dim WSH As Object, wE ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十七):个性化菜单接口说明
前不久微信上线了个性化菜单接口,Senparc.Weixin SDK也已经同步更新. 本次更新升级Senparc.Weixin.MP版本到v13.5.2,依赖Senparc.Weixin版本4.5.4 ...
- C#中,使用正式表达式匹配获取所需数据
.NET中,使用正式表达式匹配获取所需数据 需求:获取一串字符串中,正则匹配出需要的数据. 例如以下字符串: string temp ="ErrorCode:-1,Message:{&quo ...
- Linux压缩命令
Linux常见的压缩格式有.zip..gz..bz2..tar..tar.gz..tar.bz2:常用的压缩命令有zip.tar.这里列举了各压缩命令的使用示例.更多的用法请使用命令 --help查阅 ...
- CI Weekly #4 | 不同规模的团队,如何做好持续集成?
CI Weekly 围绕『 软件工程效率提升』 进行一系列技术内容分享,包括国内外持续集成.持续交付,持续部署.自动化测试. DevOps 等实践教程.工具与资源,以及一些工程师文化相关的程序员 Ti ...
- C#中通过反射方法获取控件类型和名称
这个方法是简单的也是神奇的. 有木有想过,将自己项目中的所有类型,包括自定义类型的命名空间和名称全部获取出来? 有木有想过,有一种简便的方法可以自动化管理项目中的控件和窗体? 有木有想过... 首先, ...
- DAC Usage2:通过DAC实现DB Schema的Migration和Upgrade
一,Introduce Extract DAC 是从现存的DB中创建DAC,抽取DB Object的definition 和 与之相关的实例级别的元素,比如Login,以及Login 和User之间的 ...
- 深入学习jQuery元素尺寸和位置操作
× 目录 [1]尺寸设置 [2]位置设置 前面的话 对于javascript来说,元素尺寸有scroll.offset.client三大属性,以及一个强大的getBoundingClientRect( ...
- 区分元素特性attribute和对象属性property
× 目录 [1]定义 [2]共有 [3]例外[4]特殊[5]自定义[6]混淆[7]总结 前面的话 其实attribute和property两个单词,翻译出来都是属性,但是<javascript高 ...
- T-SQL:毕业生出门需知系列(二)
第2课 检索数据 2.1 SELECT 语句 用途:从一个或多个表中检索数据信息 关键字:作为SQL组成部分的保留字.关键字不能用作表或列的名字. 为了使用SELECT检索表数据,必须至少给出两条信息 ...