C语⾔中size_t类型详细说明【转载】
来看看⽹上的⼀些说法:
C语⾔ size_t到底是个什么东东?
⼤神求解
1. 简单理解为 unsigned int就可以了
2. 这是在不同的机器⾥⾯的的头⽂件定义的相应宏定义,实际上是unsigned int。
3. ⽆符号整型。
4. ⼤部分情况下是unsigned int
5. 有⼀说法,数组下标应为size_t类型,具体size_t占⼏个字节与具体环境有关
在c语⾔的很多库函数中,函数原型中,参数类型都是size_t。但是在我们编写程序时size_t类型却很少有所使⽤。那么这个类型到底有什么作⽤呢
使⽤size_t可能会提⾼代码的可移植性、有效性或者可读性,或许同时提⾼这三者。
在标准C库中的许多函数使⽤的参数或者返回值都是表⽰的⽤字节表⽰的对象⼤⼩,⽐如说malloc(n) 函数的参数n指明了需要申请的空间⼤⼩,还有memcpy(s1, s2, n)的最后⼀个参数,表明需要复制的内存⼤⼩,strlen(s)函数的返回值表明了以’\0’结尾的字符串的长度(不包括’\0’),其返回值并不是该字符串的实际长度,因为要去掉’\0’。
或许你会认为这些参数或者返回值应该被申明为int类型(或者long或者unsigned),但是事实上并不是。C标准中将他们定义为size_t。标准中记载malloc的申明应该出现在,定义为:
void *malloc(size_t n);
memcpy和strlen的申明应该出现在中:
void *memcpy(void *s1, void const *s2, size_t n);
size_t strlen(char const *s);
size_t还经常出现在C++标准库中,此外,C++库中经常会使⽤⼀个相似的类型size_type,⽤的可能⽐size_t还要多。
据我所知,⼤部分的C和C++程序员害怕这些库使⽤size_t,因为他们不知道size_t代表什么或者为什么这些库需要使⽤它,归根结底,原因在于他们什么时候什么地⽅需要⽤到它。
可移植性问题
早期的C语⾔(由Brian Kernighan 和 Dennis Ritchie 在The C Programming Language书中所写,Prentice-Hall, 1978)并没有提供size_t类型,C标准委员会为了解决移植性问题将size_t引⼊,举例如下:
c++string类型 让我们来写⼀个可移植的标准memcpy函数,我们将会看到⼀些不同的申明和它们在不同平台不同⼤⼩的地址空间上编译下的情况。
回忆memcpy(s1, s2, n)函数,它将s2指向地址开始的n个字节拷贝到s2指向的地址,返回s1,这个函数可以拷贝任何数据类型,所以参数和返回值的类型应该为可以指向任何类型的void*,同时,源地址不应该被改变,所以第⼆个参数s2类型应该为const void*,这些都不是问题。
真正的问题在于我们如何申明第三个参数,它代表了源对象的⼤⼩,我相信⼤部分程序员都会选择int:
void *memcpy(void *s1, void const *s2, int n);
使⽤int类型在⼤部分情况下都是可以的,但是并不是所有情况下都可以。int是有符号的,它可以表⽰负数,但是,⼤⼩不可能是复数。所以我们可以使⽤unsigned int代替它让第三个参数表⽰的范围更⼤。
在⼤部分机器上,unsigned int的最⼤值要⽐int的最⼤值⼤两倍,⽐如说再也给16位的机器上,unsigned int的最⼤值为65535,int的最⼤值为32767。
尽管int类型的⼤⼩依赖于C编译器的实现,但是在给定的平台上int对象的⼤⼩和unsigned int对象的⼤⼩是⼀样的。因此,使⽤unsigned int修饰第三个参数的代价与int是相同的:
void *memcpy(void *s1, void const *s2, unsigned int n);
这样似乎没有问题了,unsigned int可以表⽰最⼤类型的对象⼤⼩了,这种情况只有在整形和指针类型具有相同⼤⼩的情况下,⽐如说在
IP16中,整形和指针都占2个字节(16位),⽽在IP32上⾯,整形和指针都占4个字节(32位)。(参见下⾯C数据模型表⽰法)
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不能处理这么⼤的数据。
什么?你说很容易就可以改正?只需要把memcpy的第三个参数的类型修改⼀下:
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,⽐如说unsigned int或者unsigned long,甚⾄unsigned long long。每⼀个标准C实现应该选择⾜够⼤的⽆符号整形来代表该平台上最⼤可能出现的对象⼤⼩。
使⽤size_t
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命名空间的成员。 根据定义,size_t是sizeof关键字(注:sizeof是关键字,并⾮运算符)运算结果的类型。所以,应当通过适
当的⽅式声明n来完成赋值:
n = sizeof(thing);
考虑到可移植性和程序效率,n应该被申明为size_t类型。类似的,下⾯的foo函数的参数也应当被申明为sizeof:
foo(sizeof(thing));
参数中带有size_t的函数通常会含有局部变量⽤来对数组的⼤⼩或者索引进⾏计算,在这种情况下,size_t是个不错的选择。
适当地使⽤size_t还会使你的代码变得如同⾃带⽂档。当你看到⼀个对象声明为size_t类型,你马上就知道它代表字节⼤⼩或数组索引,⽽不是错误代码或者是⼀个普通的算术值。
本⽂来⾃
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论