深入理解C指针1.2  指针的长度和类型_深入理解C指针1.2  指针的长度和类型试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > 深入理解C指针 > 1.2  指针的长度和类型

深入理解C指针——1.2  指针的长度和类型

如果考虑应用程序的兼容性和可移植性,指针长度就是一个问题。在大部分现代平台上,数据指针的长度通常是一样的,与指针类型无关,char 指针和结构体指针长度相同。尽管C 标准没有规定所有数据类型的指针长度相同,但是通常实际情况就是这样。不过,函数指针长度可能与数据指针长度不同。 指针长度取决于使用的机器和编译器。比如,在现代Windows 上,指针是32 位或64 位长。对于DOS 和Windows 3.1 来说,指针则是16 位或32 位长。 1.2.1  内存模型 64 位机器的出现导致为不同数据类型分配的内存在长度上的差异变得明显。不同的机器和编译器在给C 的基本数据类型分配空间上有不同的做法。用来描述不同数据模型的一种通用表示法总结如下: I In L Ln LL LLn P Pn 每个大写字母对应整数、长整数或指针,小写字母表示为该数据类型分配的位数。表1-3 总结了这些模型,其中数字表示位数。 表1-3:机器内存模型 模型取决于操作系统和编译器,一种操作系统可能支持多种模型,这通常是用编译器选项来控制的。 1.2.2  指针相关的预定义类型 使用指针时经常用到以下四种预定义类型。 • size_t 用于安全地表示长度。 • ptrdiff_t 用于处理指针算术运算。 • intptr_t和uintptr_t 用于存储指针地址。 下面将展示每种类型的用法,ptrdiff_t 除外,我们会在1.3.1 节的“两个指针相减”中讨论它。 1. 理解size_t size_t 类型表示C 中任何对象所能达到的最大长度。它是无符号整数,因为负数在这里没有意义。它的目的是提供一种可移植的方法来声明与系统中可寻址的内存区域一致的长度。size_t 用做sizeof 操作符的返回值类型,同时也是很多函数的参数类型,包括malloc 和strlen。 在声明诸如字符数或者数组索引这样的长度变量时用size_t 是好的做法。它经常用于循环计数器、数组索引,有时候还用在指针算术运算上。 size_t 的声明是实现相关的。它出现在一个或多个标准头文件中,比如stdio.h 和stblib.h,典型的定义如下: #ifndef __SIZE_T #define __SIZE_T typedef unsigned int size_t; #endif define 指令确保它只被定义一次。实际的长度取决于实现。通常在32 位系统上它的长度是32 位,而在64 位系统上则是64 位。一般来说,size_t 可能的最大值是SIZE_MAX。 通常size_t 可以用来存放指针,但是假定size_t 和指针一样长不是个好主意。稍后的“使用sizeof 操作符和指针”会讲到,intptr_t 是更好的选择。 打印size_t 类型的值时要小心。这是无符号值,如果选错格式说明符,可能会得到不可靠的结果。推荐的格式说明符是%zu。不过,某些情况下不能用这个说明符, 作为替代,可以考虑%u 或%lu。 下面这个例子将一个变量定义为size_t,然后用两种不同的格式说明符来打印: size_t sizet = -5; printf("%dn",sizet); printf("%zun",sizet); 因为size_t 本来是用于表示正整数的,如果用来表示负数就会出问题。如果为其赋一个负数,然后用%d 和%zu 格式说明符打印,就得到如下结果: -5 4294967291 %d 把size_t 当做有符号整数,它打印出-5 因为变量中存放的就是-5。%zu 把size_t 当做无符号整数。当-5 被解析为有符号数时,高位置为1,表示这个数是负数。当它被解析为无符号数时,高位的1 被当做2 的乘幂。所以在用%zu 格式说明符时才会看到那个大整数。 正数会正常显示,如下所示: sizet = 5; printf("%dn",sizet); // 显示5 printf("%zun",sizet); // 显示5 因为size_t 是无符号的,一定要给这种类型的变量赋正数。 2. 对指针使用sizeof操作符 sizeof 操作符可以用来判断指针长度。下面的代码显示char 指针的长度: printf("Size of *char: %dn",sizeof(char*)); 输出如下: Size of *char: 4 当需要用指针长度时,一定要用sizeof 操作符。 函数指针的长度是可变的。通常,对于给定的操作系统和编译器组合,它是固定的。很多编译器支持创建32 位和64 位应用程序,所以对于同一个程序来说,不同的编译选项可能会导致其使用不同的指针长度。 在Harvard 架构上,代码和数据存储在不同的物理内存中。比如Intel 的MCS-51 (8051)微处理器就是Harvard 架构。尽管Intel 不再生产这种芯片,但现在还是有很多二进制兼容的衍生品在使用。Small Device C Complier(SDCC)就支持这类处理器(参见http://sdcc.sourceforge.net/doc/sdccman.pdf)。这种机器的指针长度可能介于1 到4 字节之间,因此指针长度应该在需要时再确定,原因是在这种环境中它并不固定。 3. 使用intptr_t和uintptr_t intptr_t 和uintptr_t 类型用来存放指针地址。它们提供了一种可移植且安全的方法声明指针,而且和系统中使用的指针长度相同,对于把指针转化成整数形式来说很有用。 uintptr_t 是intptr_t 的无符号版本。对于大部分操作,用intptr_t 比较好。uintptr_t 不像intptr_t 那样灵活。下面的例子说明如何使用intptr_t: int num; intptr_t *pi = # 如果像下面那样试图把整数地址赋给uintptr_t 类型的指针,我们会得到一个语法错误: uintptr_t *pu = # 错误看起来是这样的: error: invalid conversion from 'int*' to 'uintptr_t* {aka unsigned int*}' [-fpermissive] 不过,用强制类型转换来赋值是可以的: intptr_t *pi = # uintptr_t *pu = (uintptr_t*)# 如果不转换类型,不能将uintptr_t 用于其他类型: char c; uintptr_t *pc = (uintptr_t*)&c; 当可移植性和安全性变得重要时,就应该使用这些类型。不过,为简单起见,我们的例子中不会使用。 避免把指针转换成整数。如果指针是64 位,整数只有4 字节时就会丢失信息。 早期的Intel 处理器采用16 位的分段架构,近指针和远指针也是相对的。今天的虚拟内存架构上就不是这样了。远指针和近指针是C 标准的扩展, 用来支持早期的Intel 处理器的分段架构。近指针一次只能寻址64 KB 的内存。远指针最多可以寻址1 MB 内存,但是比近指针慢。巨指针是规范化过的远指针,使用尽可能高的段。

展开全文

推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《深入理解C指针》其他试读目录

• 1.1  指针和内存
• 1.2  指针的长度和类型 [当前]
• 1.3  指针操作符
• 1.4  指针的常见用法
• 1.5  小结