对二值信号进行存储和执行计算的电子电路非常简单和可靠。
重要的三中数字表示:
- 无符号 -- 基于传统的二进制表示法,表示大于或者等于零的数字。
- 补码 -- 表示有符号整数的最常见方式,有符号整数就是可以为正或者为负得数字。
- 浮点数 -- 表示实数的科学记数法的以二为基数的版本。
计算机的表示法是用有限数量的位来对一个数字编码。由于表示的精度有限,浮点运算是不可结合的。
整数运算和浮点运算会有不同的数学属性是因为他们处理数字表示有限性的方式不同--整数的表示虽然只能编码一个相对较小的数值范围,但是这种表示是精确的;而浮点数虽然可以编码一个较大的数值范围,但是这种表示只是近似的。
整数运算--精确的。
浮点运算--非精确的。
2.1 信息存储
大多数计算机使用8位的块,或者字节,作为最小的可寻址的存储器单位,而不是在存储器中访问单独的位。
C语言中一个指针的值(无论是指向一个整数、一个结构或某个其他程序对象)都是以某个存储块的第一个字节的虚拟地址。
在C语言中,以0x或者0X开头得数字常量被认为是十六进制的值。字符A~F既可以是大写,也可以是小写,甚至是大小写混合。
十六进制转换为二进制:首先将每4位分为一组,再把它转换为十六进制。不过需要注意,如果位得总数不是4的倍数,最左边得一组可以少于4位,前面用0补足,然后将每个4位组转换为相应的十六进制数字。
当值x是2的非负整数n次幂时,也就是x=2^n,我们可以很容易地将x携程十六进制形式,只要记住:
x的二级制表示就是1后面跟n个0.
十六进制数字0代表4个二级制0。
所以,当n表示成i+4j的形式,其中0≤i≤3时,我们可以把写成开头得十六进制数字为 1(i=0)、2(i=1)、4(i=2)、8(i=3),后面跟随着j个十六进制的0.
比如: x=2048=2^11
于是有: n = 11 = 3+4*2 从而得到十六进制表示 0x800
2.1.2 字
每台计算机都有一个字长(word size),指明整数和指针数据的标称大小(nominal size)。因为虚拟地址是通过一个字来编码的。所以字长决定的最重要的系统参数就是虚拟地址空间得最大大小。也就是说,对于一个字长为w位得机器而言,虚拟地址的范围是0~2(w-1),程序最多访问2w个字节。
2.1.3 数据大小
C的数据类型大小:
char -- 1个字节。
short int -- 2个字节
int -- 4个字节
long int-- 使用机器的全字长,如果是32位则4字节,如果64为则8字节
long long int-- 允许64位整数,使用8个字节。对于32位机器而言,编译器必须把这种数据类型的操作编译成一系列32位操作的代码。
float -- 4个字节
double-- 8个字节
char* -- 机器的全字长,32位是4个字节,64位是8个字节。
需要注意:许多程序员假设一个声明为int类型得程序对象能被用来存储一个指针。这在大多数32位得机器上能正常工作,但是在一台64位得机器上却会导致问题。(因为64位下int是4字节,而指针是8字节。)
2.1.4 寻址和字节顺序
在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为嗦使用字节中最小的地址。
例如:一个类型为int得变量x的地址为0x100,也就是说,地址表达式&x的值为0x100。那么,x的4个字节被存储在存储器的0x100,0x101,ox102,ox103位置。
在存储器存储中,有大端法和小端法的区分:
大端法 -- 最高有效字节在最前面。IBM和SUN采用此种规则。
小端法 -- 最低有效字节在最前面。Intel兼容机采用此种规则。
许多比较新的微处理器使用双端法,也就是说可以把他们配置为大端或者小端的机器运行。
对于int类型的x,位于地址0x100处,它得十六进制值为0x1234567 ,地址范围为0x100~0x103字节,其排列顺序依赖于机器类型。
大端法 -- 01(ox100) 23(0x101) 45(0x102) 67(0x103)
小端法 -- 67(ox100) 45(0x101) 23(0x102) 01(0x103)
一般情况下,机器所使用的字节顺序对我们来说是透明的,不可见的。无论使用哪种类型的机器编译程序都会得到同样的结果。
除非在编写网络程序的时候,才需要注意这个问题。
为了避免这类问题,网络应用程序的代码编写必须遵守已建立的关于字节顺序的规则,以确保发送机器将它得内部表示转换成网络标准,而接收方机器则将网络标准转换为它得内部表示。
对于任一位向量a,有a^a =0 。
2.1.9 C语言中的位运算和逻辑运算
C语言中的位运算:
| 或
& 与
~ 取反
^ 异或 当p=1且q=0,或者p=0且q=1时, p^q = 1
C语言中的逻辑运算:
|| 或 or
&& 与 and
! 非 not
逻辑运算认为所有非零的参数都表示true,而参数0表示false。
逻辑运算&&和||与他们对应的位级运算&和|之间的第二个重要的区别是,如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。
2.1.0 C语言中的移位运算
左移、右移
右移支持两种:逻辑右移和算术右移。
逻辑右移在左端补0。
算术右移是在左端补最高有效位的值。
x = 01100001 10010101
x》4 (逻辑右移) 00000110 00001001
x》4 (算术右移) 00000110 11111001
C语言中并没有明确定义应该使用哪种类型的右移。对于无符号数据(也即是以 unsigned声明开通的整型对象),逻辑右移是必须得。
几乎所有的编译器、机器组合都对有符号数据使用算术右移,且许多程序员也都假设机器会使用算术右移。
另外,Java对于如何进行右移有明确的定义。
x》k 会将x算术右移k个位置。
x>>>k 会对x做逻辑右移。
在许多机器上,当移位位数k超过了机器的字长w时,移位移的是w mod k 位。
比如在32位机器上移位40位,则只是移8位。
int lval = oxFEDCBA98 <<32 = oxFEDCBA98
int aval = oxFEDCBA98 >>36 = 0xFFEDCBA9
unsigned uval = oxFEDCBA98u >>40 = ox00FEDCBA
不过C语言没有对这种行为进行保障,所以移位数量应该保持小于字长。
另一方面,Java特别要求位移数量应该按照之前讲的求模的方法来计算。
加法减法的优先级比移位运算要高。
所以: 1<<2+3<<4 = 1<<(2+3)<<4 = (1<<(2+3))<<4 = 512
在拿不准时,请用括号表明运算顺序。
网友评论