理解size_t和ssize_t类型

Last Updated: 2023-12-12 04:52:35 Tuesday

-- TOC --

size_t类型与具体CPU架构有关,它所覆盖的范围与进程虚拟内存空间的范围一致,sizeof操作符返回值的类型就是size_t。ssize_t就是singed size_t。

_t这个后缀,一般都指type,在C语言中,就是用typedef定义出来的类型,多出现在标准库的定义中。因此,不建议自己定义的类型也是用_t这个后缀,可能会造成冲突。

size_t

简单地说,size_t类型(size type的联合简写体)就是一个unsigned整数,至于是unsigned int还是unsigned long等,就要看平台了,即CPU。

32位CPU对应unsigned int的size_t,64位CPU对应unsigned long的size_t。

size_t类型来自C语言标准头文件stdio.h,只要include此头文件,就可以直接在代码使用size_t。

早期的C语言(由Brian Kernighan 和 Dennis Ritchie 在The C Programming Language书中所写,Prentice-Hall, 1978)并没有提供size_t类型,C标准委员会为了解决代码移植性问题,将size_t引入。因此,最经典的C89就有这个类型,C标准库中很多函数的参数类型,都是它,比如malloc(size_t n)

为什么有size_t这个类型?它的作用是什么?

C标准库中提供size_t类型的目的,是为了让C代码具有更好的可移植性和执行效率

size type中的size,我理解是指进程的虚拟地址空间,比如在32位系统中,进程的寻址空间是4G,正好对应4字节的unsigned int类型。假设进程有一个超大的字符数组,大小就是4G(实际上不可能),此时操作这个数组的index,就应该被定义为4字节的unsigned int类型。但有些系统是16位的,平台提供的int是2个字节,但指针大小还是4字节。而现在主流CPU都是64位的,但int还是4字节。如果代码对于数组index的定义一直使用unsigned int,在前述16位系统上,就会无法访问超过65535大小的数组;在前述64位系统上,也会出现无法访问长度超过4G的数组。

32位系统中,进程的虚拟地址空间,一般是低2G,不可能用满4G,OS kernel还要占一部分。64位系统中,就很大了,128T。

如果16位系统,指针也是16位的,如果代码将index定义为unsigned long,4字节,虽然可以覆盖全部内存地址,但是执行效率上又会受到影响。(16位的CPU寄存器操作32位数据)

size_t类型的出现,就解决了以上说明的代码可移植性和执行效率的问题。

由于size_t类型是标准库中定义的,它的大小一定对应了CPU的寄存器位数,就不会再出现无法访问的地址段,代码也具有了更好的可移植性。size_t始终对应平台指针大小,既保证了覆盖所有地址,也保证了效率。

因此,数组index之类的数据结构,都可以考虑使用size_t类型来申明其大小,代码的可读性也更好。一看到size_t,就知道这个类型的大小由编译平台决定,可以保证代码可移植性和执行效率。

stackoverflow上的一段文字:

  1. Because unsigned int is not the only unsigned integer type. size_t could be any of unsigned char, unsigned short, unsigned int, unsigned long or unsigned long long, depending on the implementation.
  2. On one system, it might make sense to use unsigned int to represent sizes; on another, it might make more sense to use unsigned long or unsigned long long. (size_t is unlikely to be either unsigned char or unsigned short, but that's permitted).
  3. The purpose of size_t is to relieve the programmer from having to worry about which of the predefined types is used to represent sizes.
  4. Code that assumes sizeof yields an unsigned int would not be portable. Code that assumes it yields a size_t is more likely to be portable.

There are 5 standard unsigned integer types in C:

unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long

with various requirements for their sizes and ranges (briefly, each type's range is a subset of the next type's range, but some of them may have the same range).

size_t 在 VC++2010 的 crtdefs.h 文件中的定义如下所示:

#ifndef _SIZE_T_DEFINED
#ifdef  _WIN64
typedef unsigned __int64    size_t;
#else
typedef _W64 unsigned int   size_t;
#endif
#define _SIZE_T_DEFINED
#endif

ssize_t

In short, ssize_t is the same as size_t, but is a signed type - read ssize_t as signed size_t. ssize_t is able to represent the number -1, which is returned by several system calls and library functions as a way to indicate error. For example, the read and write system calls:

#include <sys/types.h>
#include <unistd.h>

ssize_t read(int fildes, void *buf, size_t nbyte);
ssize_t write(int fildes, const void *buf, size_t nbyte);

虽然我们用size_t表示size,但是实际上可能出现的最大size,远小于size_t的最大值,这就给sszie_t类型提供了空间来同时表达真实的size和错误时的-1。比如C++中的string::npos,它的类型虽然是size_t,但其值为string::npos == (size_t)-1

本文链接:https://cs.pynote.net/sf/c/202111196/

-- EOF --

-- MORE --