目录
1.3 debug过程、c/c++如何查看变量类型、string.length()返回值类型
2.无符号数、有符号数负数及其补码、有符号数正数和0及其原码表示存储机制
3.问题解决:c/c++运算过程有符号数的隐式转换与其二进制存储位模式的不变性-,细看-1 < 25u强制转换和比较过程。
3.1上段引用强制转换情况的发生是隐式发生的,发生的条件即运算符包含最少一个无符号数
3.2 “这种方法对于标准的算数运算来说并无多大差异”这句话我现在还没弄明白,因为
1. 问题与结论
1.1 问题:
为什么 -1 大于 25u 而 -1 小于 25。
1.2 结论为:
当无符号数和有符号数进行运算(不管关系运算像>和<;还是算数运算+-*/),C/c++都会隐式的将有符号类型转换为无符号类型,但底层存储形式(即位模式不改变),通俗的讲就是二进制的01顺序及其数量不改变,改变的是解释这些二进制的方式,用解释无符号数的规则去解释有符号数。
1.3 debug过程、c/c++如何查看变量类型、string.length()返回值类型
遇到这个问题是因为学习kmp算法中有一步判断‘i<s.length()&&j<t.length()’很是奇怪当j==-1时,即-1<t.length()导致无法循环,后来我尝试对条件进行debug
结果显而易见,-1<t.length() is false (注意我这里仅仅用t.length()而不用直接的整数,其实如果你要输入整数只要在数后面加u,例如-1<25u,25u为无符号常量,具体可以去必应搜索)。可以运行以下查看结果,这就是这篇博客要记录解决的问题:为什么 -1 大于 25u 而 -1 小于 25
#include<iostream>
using namespace std;
int main()
{
if(!(-1<25u))
cout<<"-1 大于 25u"<<endl;
if(-1<25)
cout<<"-1 小于 25"<<endl;
}
输出:
-1 大于 25u
-1 小于 25
另补充以下string.length()返回类型为无符号类型
还有如何用函数求变量数据类型
#include<iostream>
#include <typeinfo>
#include <cxxabi.h>
using namespace std;
int main()
{
string str="123457878";
//直接用 typeid(s.length()).name()只会输出数据类型缩写
//而下方语句输出全称
cout<<abi::__cxa_demangle(typeid(str.length()).name(),0,0,0 )<<endl;
}
输出:
unsigned long long
2.无符号数、有符号数负数及其补码、有符号数正数和0及其原码表示存储机制
(1)无论什么信息存储到计算机内就是二进制的01序列(当然包括数)
(2)接下来我们重点关注无符号(unsigned)整数即没有正负号的数和有符号整数即[-∞,+∞]内的整数。
(3)绝大部分机器对于c语言存储有符号负数均采用补码(two's s-complement)表示。从补码二进制转十进制角度理解(CSAPP中45页)补码:
设有a在内存中以2字节也就是8位二进制存储 short a;
(3-1)位模式表示
此时a二进制位模式为(左边为高位,右边为地位) [x7,x6,x5,x4,x3,x2,x1,x0]
(3-2)二进制补码转十进制
此时最高位x7权值为(-1*2^7)可以理解为符号位(当然这是另一种理解角度,别跟这里的最高位负权值理解矛盾);,其余权值都为(2^6,2^5......2^0)
即:{ -2^7,2^6,2^5,2^4,2^3,2^2,2^1,2^0 }
举个例子:当a =-1;
则其补码二进制表示为[ 1,1,1,1,1,1,1,1 ],我们根据权值计算其十进制: ∑(二进制位*权值)
1*(-1*2^7)+1*(2^6)+1*(2^5)+1*(2^4)+1*(2^3)+1*(2^2)+1*(2^1)+1*(2^0)= -1
(4)对于c语言存储有符号正数和0均采用原码存储表示,从原码二进制转十进制角度理解:
原码的最高位 (第八位,也就是x7) 必须是0,其它x0~x6位(还是假设8位)是否01都可以,转换成十进制的权值也即 { 2^6,2^5......2^0 }
举个例子:当 short a =17;
则其原码二进制表示为[ 0,0,0,1,0,0,0,1],我们根据权值计算其十进制: ∑(二进制位*权值)
0*(1*2^7)+0*(2^6)+0*(2^5)+1*(2^4)+0*(2^3)+0*(2^2)+0*(2^1)+1*(2^0)= 16+1=17
举个例子:当 short a =0;
则其原码二进制表示为[ 0,0,0,0,0,0,0,0 ]
(5)对于无符号数,没有正负号,也就是不需要最高位的符号位,也就是最高位权值没有负号且01任意,那么每个二进制都能发挥它每一位的价值(没错,说的就是最高位)无符号二进制转十进制:
我们接着用8位的 short a;
(4-1)位模式表示
此时a二进制位模式为(左边为高位,右边为地位) [x7,x6,x5,x4,x3,x2,x1,x0]
(3-2)二进制补码转十进制
此时每一位权值为{ 2^7,2^6,2^5,2^4,2^3,2^2,2^1,2^0 }
举个例子:当a = 44 ;
44=32+8+4=1*2^5+1*2^3+1*2^4=0*2^7+0*2^6+1*2^5+1*2^4+1*2^3+0*2^2+0*2^1+0*2^0
则其无符号数二进制表示为[ 0,0,1,1,1,0,0,0 ],
反过来,我们根据权值计算其十进制: ∑(二进制位*权值)
0*2^7+0*2^6+1*2^5+1*2^4+1*2^3+0*2^2+0*2^1+0*2^0=44
3.问题解决:c/c++运算过程有符号数的隐式转换与其二进制存储位模式的不变性-,细看-1 < 25u强制转换和比较过程。
“由于C语言对同时包含有符号和无符号数表达式的这种处理方式,出现了一些奇特的行为,当执行一个运算时,如果它的一个运算数是有符号的而另一个是无符号的,那么C语言会隐式地将有符号数强制转换为无符号数,并假设这两个数是非负的,来执行这个运算......这种方法对于标准的算数运算来说并无多大差异,但对于<和> 这样的关系运算符来说,会导致不可思议的结果。” ————《CSAPP》53页
如下解释:
3.1上段引用强制转换情况的发生是隐式发生的,发生的条件即运算符包含最少一个无符号数
例如:1+2u; -1+2u, -1<123u, 1u>23
3.2 “这种方法对于标准的算数运算来说并无多大差异”这句话我现在还没弄明白,因为
#include<iostream>
#include <typeinfo>
#include <cxxabi.h>
using namespace std;
int main()
{
string str="1";
cout<<abi::__cxa_demangle(typeid(-8+str.length()).name(),0,0,0 )<<endl;
cout<<-8+str.length()<<endl;
cout<<"---------------------------------"<<endl;
cout<<abi::__cxa_demangle(typeid(-8+1u).name(),0,0,0 )<<endl;
cout<<-8+1u<<endl;
cout<<"---------------------------------"<<endl;
cout<<abi::__cxa_demangle(typeid(-8+1).name(),0,0,0 )<<endl;
cout<<-8+1<<endl;
}
输出:
unsigned long long
18446744073709551609
---------------------------------
unsigned int
4294967289
---------------------------------
int
-7
Amazing!这怎不是影响?...
3.3回归正题,-1 < 25u,细看其强制转换和比较过程
有符号整数 假设还是 8位的 short a = -1;;
无符号常量 25u也假设存储在8位二进制中。
-1的二进制补码表示(还记得计算机存储负数是以补码形式):
1111 1111
25u二进制表示(没有正负,每一位都对该值的正向增大有正向贡献):
25=16+8+1 二进制为 0001 1001
Step1: 计算-1 < 25u,此时C看到了无符号数25,那么准备将有符号的-1强制转换为无符号数
Step2:计算机内存中1111 1111与0001 1001改变吗?不改变。此时1111 1111被解释为有符号数。
Step3: 1111 1111二进制转无符号的十进制 :
1*2^7+1*2^6+1*2^5+1*2^4+1*2^3+1*2^2+1*2^1+1*2^0=255
那么此时-1经过强制转换,变成了255
25u已经是无符号数,所以0001 1001就是25,25就是0001 1001。
Step4:计算 255<25 显而易见 255 < 25 is false,即 -1 < 25u is false,
Over!