最近再次复习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++语言中的取余数(求模)的计算_有符号和无符号数的相互转换问题的更多相关文章

  1. IOS-2-C语言和Objective-C语言衔接学习资料

    前言:在IOS学习中.通常会先学习一周的C语言,两周的Objective-C语言,这是今后开发的最基础最重要的部分,以下给大家分享一下培训课上的精简资料: C语言和Objective-C语言衔接学习资 ...

  2. 混合使用C++语言和Objective-C语言

    如果你的源文件扩展名是.m的,你还需要改成.mm,这样编译器才知道你将会在该文件中混合使用C++语言和Objective-C语言.

  3. C语言和Python语言在存储变量方面的不同

    C语言和Python语言在存储变量方面的不同 众所周知,Python是脚本语言,边解释边执行,而C语言是编译型语言 存储变量: C语言定义变量,变量本身代表的就是大小,任何一个字母或者数字 符号均可以 ...

  4. 开发电商平台用PHP语言和JAVA语言有什么区别?哪种语言更好?

    现在很多行业都通过电子商务拓展业务,所以商城系统开发成为很多企业的刚性需求.一般有一点技术基础的客户应该知道目前商城系统开发主流语言有两个,PHP和Java.那么很多客户朋友会纠结是选择哪个语言开发好 ...

  5. 【C语言学习趣事】_32_平胸的尴尬,嫁不出去的姑娘

    为什么写这篇文章呢? 为什么要弄这么个题目呢? 首先解释为什么用这个题目.这一切都要从那天在QQ群中的讨论说起,那天在群中,一个哥们问了一个关于(void)0 的问题.然后大家说到了 (void)0和 ...

  6. Java语言和C++语言的差异

    Java采用了C及C++的语法格式,对于学习过C及C++的程序设计者来说,学习Java将有可能很轻松.但是,如果仔细检查Java语言的许多细节,就会发现Java取消了不少C及C++的特性,并且加入了一 ...

  7. C语言和go语言之间的交互

    一.go代码中使用C代码 go代码中使用C代码,在go语言的函数块中,以注释的方式写入C代码,然后紧跟import "C" 即可在go代码中使用C函数 代码示例: go代码:tes ...

  8. Go 语言和 Scala 语言对比

    我在Google写过Go(自己的业余时间),也在LinkedIn写过Scala.两者都是具有一流的并发特性的现代语言. 下面的回答是基于我编写大规模的软件的经验得出. Go是一种开发模式严格固定,并且 ...

  9. C语言学习及应用笔记之二:C语言static关键字及其使用

    C语言有很多关键字,大多关键字使用起来是很明确的,但有一些关键字却要相对复杂一些.我们这里要说明的static关键字就是如此,它的功能很强大,相应的使用也就更复杂. 一般来说static关键字的常见用 ...

随机推荐

  1. 搭建 SVN 服务器

    安装.启动 SVN 服务器 在 Windows 下,可以使用以下命令将 SVN 服务注册到 windows 服务中去: sc create svnserver binPath= "drive ...

  2. MySQL 主主复制

    200 ? "200px" : this.width)!important;} --> 介绍 环境 OS:CentOS 6.7,MySQL 5.6 Master:192.16 ...

  3. sublime text2 安装less2css插件

    之前一直用PhpStorm,功能十分强大,各种插件也有,不过比较占内存,有时候,左边的项目列表都刷不出来,今天又出现了这个问题,于是果断换sublime了. 由于项目中要用less编译,所以得装个le ...

  4. WCF basicHttpBinding之Transport Security Mode, clientCredentialType="None"

    原创地址:http://www.cnblogs.com/jfzhu/p/4071342.html 转载请注明出处 前面文章介绍了<WCF basicHttpBinding之Message Sec ...

  5. C语言 · 查找整数

    问题描述 给出一个包含n个整数的数列,问整数a在数列中的第一次出现是第几个. 输入格式 第一行包含一个整数n. 第二行包含n个非负整数,为给定的数列,数列中的每个数都不大于10000. 第三行包含一个 ...

  6. [CORS:跨域资源共享] W3C的CORS Specification

    随着Web开放的程度越来越高,通过浏览器跨域获取资源的需求已经变得非常普遍.在我看来,如果Web API不能针对浏览器提供跨域资源共享的能力,它甚至就不应该被称为Web API.从另一方面来看,浏览器 ...

  7. Security5:Execute AS 和 impersonate 权限

    principal 是统称,包括login,user,role等,可以向作为安全主体的用户授予权限,principal分为server level和database level. 登录名是Server ...

  8. CSS系列:CSS选择器

    选择器(selector)是CSS中很重要的概念,所有HTML语言中的标记样式都是通过不同的CSS选择器来控制的.用户只需要通过选择对不同的HTML标签进行选择,并赋予各种样式声明,即可实现各种效果. ...

  9. 数据结构与算法JavaScript (二) 队列

    队列是只允许在一端进行插入操作,另一个进行删除操作的线性表,队列是一种先进先出(First-In-First-Out,FIFO)的数据结构 队列在程序程序设计中用的非常的频繁,因为javascript ...

  10. 安装CentOS、Linux系统时,GPT分区不能引导的解决方法

    安装系统:CentOS 5.9_64bit时,分区后, 提示如下错误. 解决方法: 1.按ctrl+alt+F2 进入命令行 2.先查看分区 sh #fdisk -l    以下假设分区是/dev/s ...