且听风吟

Don't panic! I'm a programmer.

CSAPP读书笔记-信息的表示和处理

| Comments

寻址和字节顺序

  • 字长决定整数和指针的大小,因为虚拟地址是以这样的一个字来编码的,所以字长决定的最重要的系统参数就是虚拟地址的空间的最大大小。

  • 在存储器中存储数据的顺序分为小端存储和大端存储:
    小端存储:低字节存储在低地址,高字节存储在高地址。Intel兼容机大多采用这种规则。
    大端存储:高字节存储在低地址,高字节存储在低地址。IBM和Sun的大型机多采用这种规则。
    大多数时候,字节顺序对程序员是透明的。但是字节存储顺序在以下场合会产生问题:
    在不同类型的机器间通过网络传送二进制数据,发送方应该把数据转换成网络标准再发送,接收方应该先把数据从网络标准格式转化成内部格式。
    阅读表示整数数据的字节序列时

表示字符串

  • 使用ASCII码作为字符码在任何系统上的表示方法都相同,与字节顺序和字大小规则无关,因此文本数据比二进制数据有更强的平台独立性。由于不同的机器使用不同且不兼容的指令和编码方式,因此编译后的二进制代码在不同的机器上时不兼容的,如果要在不同的机器上运行程序,必须重新编译生成新的二进制码才能运行。

  • char类型是有符号的还是无符号的?

C语言的位运算

  • 表达式~0生成一个全1的掩码,不管机器的字大小是多少,这是一个可移植的写法。

  • C语言中的右移位运算包括逻辑右移和算术右移。逻辑右移在左端补0,算术右移在左端补最高有效位的值。C语言标准并没有规定应该使用哪种类型的右移方式。实际上,对于无符号数据,右移必须是逻辑的。而对于有符号数据,几乎所有的机器都使用算术右移。在Java中对右移有明确定义:运算符>>表示算术右移,运算符>>>表示逻辑右移。

  • 一种交换指针所指向的存储位置的方法:

    void swap(int *x, int *y) {
        *y = *x ^ *y;
        *x = *x ^ *y;
        *y = *x ^ *y;
    }
    

数据类型及转换

  • 整数数据类型的取值范围

  • 确定大小的整数类型

    由于每种数据类型在不同的机器上可能有不同的取值范围,而c语言标准只是规定了每种数据类型的最小范围,而不是确定的范围,ISO C99在stdint.h中定义了形如intN_t和uintN_t,指定了N位有符号和无符号整数。N一般取值8,16,32,64,如int32_t,可以在所有机器上无歧义的定义一个32位的整型变量。 Java中没有long long数据类型,单字节数据类型位byte,而不是char。

  • 类型转换以及有符号数和无符号数之间的转换

    当执行一个运算时,如果一个运算数是有符号的而另一个是无符号的,那么C语言会隐式的将有符号的运算数强制转换为无符号数,并假设这两个数都是非负的。
    比如在一个32位机器上计算-1<0U,-1的二进制位模式表示为FFFFFFFF,那么有符号数-1会被隐式转换为无符号数——以无符号数来解释位模式FFFFFFFF,那么就变成了计算4294967295U < 0U,而得出来的答案显然是错的。
    无符号数和有符号数之间的这种隐式转换很容易导致隐藏的bug,使用无符号数一定要谨慎。

    类型转换的时机:

    1. 显示的强制转换
    2. 当一种类型的表达式被赋值给另外一种类型的变量时,发生隐式转换。

    C语言允许不同数据类型之间的强制转换。这种转换的一般规则是:转换的数底层的存储位模式不会改变,只是改变了解释这些位的方式。
    当转换既涉及到大小变化(如int—>short)又涉及到符号改变时,会先进行大小的转换,再以有符号或者无符号的方式去解释转换后的位模式。如:

      short  sx = 0xcfc7;
      unsigned uy = sx;
      printf("uy = %u\n", uy);
    

    输出:uy = 4294954951

    转换的过程是:
    先转换大小:short—>int,进行位扩展,0xcfc7—>0xffffcfc7
    再完成有符号到无符号的转换:以无符号的方式解释0xffffcfc7,得到4294954951。

Reference

关于补码