size_t的取值与机器相关无符号类型,它被设计得足够大以便能够内存中任意对象的大小。
在cstddef头文件中定义了size_t类型,
这个文件是C标准库stddef.h头文件的C++版本。
size_t是一些C/C++标准在cstddef/stddef.h中定义的
size_t的真实类型与操作系统有关,在32位架构中被普遍定义为:
typedef unsigned int size_t;
而在64位架构中被定义为:
typedef unsigned long size_t;
size_t在32位架构上是4字节,在64位架构上是8字节,在不同架构上进行编译时需要注意这个问题。
而int在不同架构下都是4字节,与size_t不同;且int为带符号数,size_t为无符号数。
某些情况下正确的使用size_t会提高代码的可移植性、有效性或者可读性,或许同时提高这三者。
对在不同平台不同大小的地址空间上编译下的影响
理解
是类型 且为无符号数
#include <stdio.h>
#include <string.h>
int main()
{
int i = -1;
if(i > strlen("Demon"))
printf("Hello World");
else
printf("Hello Demon");
return 0;
}
输出的竟然是Hello World!-1 > 5?!
strlen返回值是size_t类型,即无符号的整型,而i的类型是int,即有符号的整型。当有符号整型和无符号整型进行运算时,有符号整型会先自动转化成无符号。-1转化成无符号数为4294967295,远远大于5。
今后遇到有符号数和无符号数之间的运算时要千万小心。
以memcpy为例
以memcpy(s1, s2, n)函数为例,它将s2指向地址开始的n个字节拷贝到s2指向的地址,返回s1,这个函数可以拷贝任何数据类型,所以参数和返回值的类型应该为可以指向任何类型的void*,同时,源地址不应该被改变,所以第二个参数s2类型应该为const void*
真正的问题在于我们如何申明第三个参数,它代表了源对象的大小
使用unsigned int
unsigned int可以表示最大类型的对象大小了,这种情况只有在整形和指针类型具有相同大小的情况下,比如说在IP16中,整形和指针都占2个字节(16位),而在IP32上面,整形和指针都占4个字节(32位)。
void *memcpy(void *s1, void const *s2, unsigned int n);
ps
C数据模型表示法
可使用简明的标记来表述不同目标平台下c语言数据的实现。这些标记的一边形式形如:
I nI L nL LL nLL P nP。
其中每个大写字母(或成对出现)代表一个C的数据类型,每一个对应的n是这个类型包含的位数。
I代表int,L代表long,LL代表long long,以及P代表指针(指向数据,而不是函数)。每个字母和数字都是可选的。
例如,I16P32架构支持16位int和32位指针类型,没有指明是否支持long或者long long。如果两个连续的类型具有相同的大小,通常省略第一个数字。
例如,你可以将I16L32P32写为I16LP32,这是一个支持16位int,32位long,和32位指针的架构。
标记通常把字母分类在一起,所以可以按照其对应的数字升序排列。例如,IL32LL64P32表示支持32位int,32位long,64位long long和32位指针的架构;然而,通常写作ILP32LL64。
这种memcpy的申明在I16LP32架构上(整形是16-bit 长整形和指针类型时32-bits)显得不够用了,比如说摩托罗拉第一代处理器68000,在这种情况下,处理器可能拷贝的数据大于65535个字节,但是这个函数第三个参数n不能处理这么大的数据。
使用unsigned long
void *memcpy(void *s1, void const *s2, unsigned long n);
可以在I16LP32目标架构上使用这个函数了,它可以处理更大的数据。而且在IP16和IP32平台上效果也还行,说明它确实给出了memcpy的一种移植性较好的申明。但是,在IP16平台上相比于使用unsigned int,你使用unsigned long可能会使你的代码运行效率大打折扣(代码量变大而且运行变慢)。
在标准C中规定,长整形(无论无符号或者有符号)至少占用32位,因此在IP16平台上支持标准C的话,那么它一定是IP16L32 平台。这些平台通常使用一对16位的字来实现32位的长整形。在这种情况下,移动一个长整形需要两条机器指令,每条移动一个16位的块。事实上,这个平台上的大部分的32位操作都需要至上两条指令。
因此,以可移植性为名将memcpy的第三个参数申明为unsigned long而降低某些平台的性能是我们所不希望看到的。使用size_t可以有效避免这种情况。
使用size_t
size_t类型是一个类型定义,通常将一些无符号的整形定义为size_t,比如说unsigned int或者unsigned long,甚至unsigned long long。每一个标准C实现应该选择足够大的无符号整形来代表该平台上最大可能出现的对象大小。
size_t的定义在<stddef.h>, <stdio.h>, <stdlib.h>, <string.h>, <time.h>和<wchar.h>这些标准C头文件中,也出现在相应的C++头文件, 等等中,你应该在你的头文件中至少包含一个这样的头文件在使用size_t之前。 包含以上任何C头文件(由C或C++编译的程序)表明将size_t作为全局关键字。包含以上任何C++头文件(当你只能在C++中做某种操作时)表明将size_t作为std命名空间的成员。
void *memcpy(void *s1, void const *s2, size_t n);
根据定义,size_t是关键字sizeof运算结果的类型。
size_t n = sizeof(int);
(注:sizeof是关键字,并非运算符)
适当地使用size_t还会使你的代码变得如同自带文档。当你看到一个对象声明为size_t类型,你马上就知道它代表字节大小或数组索引,而不是错误代码或者是一个普通的算术值。
size_t(-1)/sizeof(_Tp)到底是什么意义?
以32位机来说,它代表了实现/平台允许你放入容器的最大元素数目,对于std::vector最大容量为2^32 / sizeof(T) 个元素。换句话来说, 也就是我能完全使用4GiB虚拟内存地址空间(实际不可能)而填充的最大元素数目。
为什么size_t(-1)为2^32?
size_t通常在32位机上定义为32位,而在64位机上定义为64位的无符号整型。通过强制类型转换,size_t(-1)恰能代表其能表示的最大值,即2^32 (2^64).