【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关键字的常见用 ...
随机推荐
- 女生的最爱,装饰品。WPF也有,Adorner。(上海晒衣服理念)
说到装饰,不由要说到女性. 去年过年回家给我妈买了周大福项链,很明显就感觉待遇就不一样了,即使这样,还是被一个阿姨说应该买更重点的.看来钱这种东西果然是多一点才好.虽然自己无所谓,但让家里人更开心也是 ...
- PC使用网线上网的条件下,通过PC的Wifi共享提供手机上网教程
场景和目标 你有一个笔记本(或装有无线网卡的PC),可以通过网线上网,但是没有无线路由器.现在想要通过笔记本的无线网,让手机也能共享wifi上网. 环境 Win7 操作系统.带有无线网卡的PC或笔记本 ...
- Git Shell 基本命令(官网脱水版)
用户信息 当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址. 这样做很重要,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改: $ git conf ...
- SQL 2014 AlwaysOn 搭建
AlwaysOn底层依然采用Windows 故障转移群集的机制进行监测和转移,因此也需要先建立Windows Cluster,只不过可用性组中的数据库不一定非要再存放在共享存储上了.可以是存储在本地磁 ...
- Security6:授予权限的思路和一般步骤
思路是:Grants permissions on a securable to a principal. The general concept is to GRANT <some permi ...
- SqlServer索引的原理与应用
索引的概念 索引的用途:我们对数据查询及处理速度已成为衡量应用系统成败的标准,而采用索引来加快数据处理速度通常是最普遍采用的优化方法. 索引是什么:数据库中的索引类似于一本书的目录,在一本书中使用目录 ...
- windows下实现Git在局域网使用
1.首先在主机A上创建一个文件夹用于存放你要公开的版本库.然后进入这个文件夹,右键->Git create repository here,弹出的窗口中勾选Make it Bare!之后将这个文 ...
- PHP类的原理
一.类的实现 类的内部存储结构: struct _zend_class_entry { char type; // 类型:ZEND_INTERNAL_CLASS / ZEND_USER_CLASS c ...
- ubuntu 配置git公钥
Git是分布式的代码管理工具,远程的代码管理是基于SSH的,所以要使用远程的Git则需要SSH的配置. github的SSH配置如下: 一 . 设置Git的user name和email: $ git ...
- Cesium应用篇:2影像服务(下)
文章中相关范例下载路径:https://yunpan.cn/cByQqkANWN7Pu 访问密码 823d 上篇主要介绍了Cesium自带的影像Provider ,在本篇中,我们主要涉及到如何扩展这些 ...