C/C++ 的 size_t

1. 背景

  • 使用 size_t 可能会提高代码的可移植性、有效性或可读性,或者同时提高这三者;
  • 在标准 C/C++ 的语法中,只有 intfloatcharbool 等基本的数据类型,至于 size_tsize_type 都是以后的编程人员为了方便记忆所定义的一些便于理解的由基本数据类型的变体类型。

2. 实现方式

size_t 是C标准库中定义的,它是一个基本的与机器相关的无符号整数的 C/C++ 类型, 它是 sizeof 操作符返回的结果类型,该类型的大小可选择。其大小足以保证存储内存中对象的大小(简单理解为 unsigned int 就可以了,64 位系统中为 long unsigned int)。通常用 sizeof(X) 操作,这个操作所得到的结果就是 size_t 类型。

类似的还有 wchar_tptrdiff_t

  • wchar_t 就是 wide char type,一种用来记录一个宽字符的数据类型。
  • ptrdiff_t 就是 pointer difference type,一种用来记录两个指针之间的距离的数据类型。

通常,size_tptrdiff_t 都是用 typedef 来实现的,如下,你可能在某个头文件里找到类似的语句。而 wchar_t 在一些旧的编译器中可能是用 typedef 来实现,但是新的标准中,wchar_t 已经是 C/C++ 语言的关键字,其地位与 charint 等同。

typedef unsigned int size_t;

3. 为什么需要 size_t

在C标准库中的许多函数使用的参数或返回值都表示的是用字节表示的对象大小,比如 malloc(n) 函数的参数 n 指明了需要申请的空间大小、memcpy(s1, s2, n) 的最后一个参数表明需要复制的内存大小、strlen(s) 函数的返回值表明了以 \0 结尾的字符串的长度(不包括 \0)。

或许你会认为这些参数或返回值应该被申明为 int 类型(或者 long 或者 unsigned),但事实上并不是。C标准中将他们定义为 size_t。标准中记载 mallocmemcpystrlen 的声明:

// malloc()
void *malloc(size_t n);

// memcpy()
void *memcpy(void *s1, void const *s2, size_t n);

// strlen()
size_t strlen(char const *s);

size_t 还经常出现在 C++ 标准库中,此外 C++ 库中经常会使用一个相似的类型 size_type

  • size_t 是全局定义的类型;size_type 是 STL 类中定义的类型属性,用以保存任意 stringvector 类对象的长度。
  • string::size_type 类型一般就是 unsigned int,但是不同机器环境长度可能不同 win32 和 win64 上长度差别;size_type 一般也是 unsigned int

4. 可移植性问题

早期的C语言并没有提供 size_t 类型,C标准委员会为了解决移植性问题,将 size_t 引入,从而让程序有良好的可移植性。

例如:对于一个可移植的标准 memcpy(s1, s2, n) 函数,其将 s2 指向地址开始的 n 个字节拷贝到 s1 指向的地址并返回 s1。这个函数可以拷贝任何数据类型,所以参数和返回值类型应为 void*。而源地址不应被改变,所以第二个参数 s2 类型应该为 const void*

问题在于我们如何声明参数,它代表了源对象的大小:

void *memcpy(void *s1, void const *s2, int n);

4.1 使用 int

使用 int 类型在大部分情况下都是可以的,但并不是所有情况下都可以。int 是有符号的,它可以表示负数,但大小不可能是负数。

4.2 使用 unsigned int

使用 unsigned int 代替它让第三个参数表示的范围更大,大部分机器上其最大值是 int 最大值的两倍。尽管如此,但在给定平台上 intunsigned int 对象的大小是一样的。因此使用 unsigned int 修饰第三个参数的代价与 int 是相同的:

void *memcpy(void *s1, void const *s2, unsigned int n);  

4.3 使用 unsigned long int

unsigned int 可以表示最大类型的对象大小只有在整型和指针类型具有相同大小的情况下,比如说在 IP16 中,整形和指针都占 2 个字节,16 位;而在 IP32 上面,整形和指针都占 4 个字节,32位。

但在 I16LP32 架构上(整形是 16 位,长整形和指针类型时 32 位)则不够用了,比如摩托罗拉第一代处理器 68000,处理器可能拷贝的数据大于 65535 个字节,但是这个函数第三个参数 n 不能处理这么大的数据。

void *memcpy(void *s1, void const *s2, unsigned long n); 

当使用 unsigned long int 则可以在 I16LP32 架构上使用这个函数了,它可以处理更大的数据,且在 IP16 和 IP32 平台上也可行。

4.4 问题

但是在 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 类型时,编译器会根据不同系统来替换标准类型。每一个标准C实现应该选择足够大的无符号整形来代表该平台上最大可能出现的对象大小。在使用 size_t 类型时,编译器会根据不同系统来替换标准类型。

5. size_t 与 sizeof()

5.1 sizeof 的作用

  • sizeof 是C语言的运算符之一,用于获取操作数被分配的内存空间,以字节单位表示;
  • 其操作数可以是变量,也可以是数据类型如 intfloat 等。所以可以通过它来获取本地C库定义的基本类型的范围。

5.2 sizeof 的使用

  • 对于一般变量,形式有2种:sizeof asizeof(a)
  • 对于数据类型,必须使用带括号的方式,如 sizeof(int)

5.3 sizeof 和 size_t

常常会有人认为在 C/C++ 中 sizeof 是一个函数,而实际上 C/C++ 中的 sizeof 是一个运算符。sizeof 运算符的结果是 size_t ,它是由 typedef 机制定义出来的”新”类型。

C/C++ 用 typedefsize_t 作为 unsigned intunsigned long 的别名,可以简单的理解为 size_t 有如下两种定义:

typedef unsigned int size_t
typedef unsigned long size_t12
//可以用%zd(C99标准新增)、%u、%lu 转换说明用于 printf() 显示 size_t 类型的值
#include "stdio.h"
int main()
{
  size_t intsize = sizeof (int);
  
  printf("%zd\n", sizeof (int)); // 4
  printf("%zd\n", intsize); // 4
  printf("%u\n", intsize); // 4
  printf("%lu\n", intsize); // 4
  
  return 0;
}
  • 0
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值