【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关键字的常见用 ...
随机推荐
- awk 留底
序 因为经常做awk编码,而且跨过一段时间就容易忘记,故在此做个留底.便于翻阅.——后期会在这个页面不断补充! 常用常量 属性 描述 NR 已读入的总记录数 ARGIND 当前被 ...
- Qt And MFC Mouse Over Tips
Qt鼠标提示分析说明 关于鼠标停留在控件上面,显示提示内容的方法. 对于Qt来说, Qt的某一个控件类, 如果属于GUI的, 那么这个控件类会有一个setToolTip(QString text)的方 ...
- padding标准盒模型和怪异盒子模型
我们都知道padding是为块级元素设置内边距 但是在使用过程中,我们却会遇到一些问题.padding的标准盒模型和怪异盒模型 padding盒子模型 我们通过demo来讲这个问题,用文字干讲第一没意 ...
- iOS开发--应用崩溃日志揭秘(二)
场景 4: 吃棒棒糖时闪退! 用户邮件说, “当rage master吃棒棒糖时应用就闪退…” 另一用户说, “我让rage master 吃棒棒糖,没几次应用就闪退了!”崩溃日志如下: Incide ...
- Security1:Create Login
Login 用于登陆SQL Server,语法是 -- Syntax for SQL Server CREATE LOGIN login_name { WITH <option_list1> ...
- Javascript算法系列之快速排序(Quicksort)
原文出自: http://www.nczonline.net/blog/2012/11/27/computer-science-in-javascript-quicksort/ https://gis ...
- Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用
转载博客:http://blog.csdn.net/xiaanming/article/details/26810303 大家好!差不多两个来月没有写文章了,前段时间也是在忙换工作的事,准备笔试面试什 ...
- 【原创】开源Math.NET基础数学类库使用(14)C#生成安全的随机数
本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新 开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 前言 ...
- IOS数据存储之NSUserDefaults
前言: 作为从事Android开发人来说一定听说过SharedPreferences,然后要成为一名ios开发工程师来说咋能不知道NSUserDefaults!接下来让我们认识一下. NSUserDe ...
- Java 8新特性-1 函数式接口
Java 8 引入的一个核心概念是函数式接口(Functional Interfaces). 通过在接口里面添加一个抽象方法,这些方法可以直接从接口中运行. 如果一个接口定义个唯一一个抽象方法,那么这 ...