本节书摘来自华章计算机《编写高质量代码:改善c程序代码的125个建议》一书中的第1章,建议2-3,作者:马 伟 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
c语言标准规定size_t是一种无符号整数类型,编译器可以根据操作系统的不同而用typedef来定义不同的size_t类型,即在不同的操作系统上所定义的size_t可能不一样。例如在32位操作系统上可以将size_t定义为unsigned int类型,而在64位操作系统上则可以定义为unsigned long int类型,甚至还可以将size_t定义为unsigned long long int类型等,如下面的示例所示。
在gcc的stddef.h文件中将size_t定义为:
从上面的定义可以看出,size_t类型的引入增强了程序在不同平台上的可移植性,而它也正是为了方便系统之间的移植而定义的。size_t类型的变量大小足以保证存储内存中对象的大小,任何表示对象长度的变量,包括作为大小、索引、循环计数和长度的整数值,都可以声明为size_t类型。比如我们常用的sizeof操作符的结果返回的就是size_t类型,该类型保证能容纳实现所建立的最大对象的字节大小。size_t类型的限制是由size_max宏指定的。
接下来看看size_t类型的使用示例,如代码清单1-3所示。
不难发现,代码清单1-3中存在着一个严重的问题:当p所引用的动态分配的缓冲区在n > int_max时将会发生溢出。我们知道,int 类型的限制是由int_max宏指定的,而size_t类型代表的是一个无符号整数类型,它可能包含一个大于int_max的值。因此,当n的值为0 < n <= int_max时,执行循环n次,代码如预期一样正常运行;但当n的值为int_max < n <= size_max,且整型变量i的增值超过int_max时,i的值将是从int_min开始的负值。这时,p[i]所引用的内存位置是在p所引用的内存之前,这就会导致写入发生在数组边界之外。
因此,为了避免发生这种潜在性的错误,应该将变量i也声明成size_t类型,如代码清单1-4所示。
除了size_t类型之外,iso/iec tr 24731-1:2007中引入了一种新类型rsize_t,虽然它被定义为size_t类型,但它明确地表示是用于保存单个对象的长度的。
在vc++ 2010的crtdefs.h文件中将rsize_t定义为:
在支持rsize_t类型的代码中,你可以检查对象的长度,验证它不大于rsize_max(一个正常单个对象的最大长度),库函数也可以使用rsize_t进行输入校验。
在vc++ 2010的limits.h文件中将rsize_max定义为:
这样就消除了示例整数溢出的可能性,现在我们可以将代码清单1-3中的变量i声明成rsize _t类型,同时也可将参数n修改成rsize _t类型,并与rsize_max进行比较以验证数据的合法范围,如代码清单1-5所示。