什么是C中的size_t?

Vij*_*jay 590 c int size-t

size_t在C中感到困惑.我知道它是由sizeof运营商返回的.但究竟是什么呢?它是数据类型吗?

假设我有一个for循环:

for(i = 0; i < some_size; i++)
Run Code Online (Sandbox Code Playgroud)

我应该使用int i;size_t i;

sbl*_*lom 440

来自维基百科:

根据1999 ISO C标准(C99),size_t是一个至少为16位的无符号整数类型(参见第7.17和7.18.3节).

size_t是由几个C/C++标准定义的无符号数据类型,例如C99 ISO/IEC 9899标准stddef.h.1它可以通过包含stdlib.h内部子包含作为此文件进一步导入 stddef.h.

此类型用于表示对象的大小.采用或返回大小的库函数期望它们是类型或具有返回类型size_t.此外,最常用的基于编译器的运算符sizeof应该计算为与之兼容的常量值 size_t.

作为一个含义,size_t是一种保证保存任何数组索引的类型.

  • @Draemon这条评论反映了一个根本的混乱.`size_t`用于内存中的对象.C标准甚至没有定义`stat()`或`off_t`(那些是POSIX定义)或任何与磁盘或文件系统有关的东西 - 它在`FILE`流停止.就大小要求而言,虚拟内存管理与文件系统和文件管理完全不同,因此提及`off_t`与此无关. (61认同)
  • @Draemon好点.这个答案引用了维基百科,在我看来,在这种情况下没有最好的解释.C标准本身更加清晰:它将`size_t`定义为`sizeof`运算符的结果类型(7.17p2关于`<stddef.h>`).第6.5节准确解释了C表达式的工作原理(6.5.3.4表示`sizeof`).由于你不能将`sizeof`应用于磁盘文件(主要是因为C甚至没有定义磁盘和文件的工作方式),所以没有混淆的余地.换句话说,责怪维基百科(这个引用维基百科的答案,而不是实际的C标准). (27认同)
  • "获取或返回大小的库函数期望它们的类型为... size_t"除了stat()使用off_t作为文件大小 (4认同)
  • @ jw013:我几乎没有把它称为根本混乱,但你提出了一个有趣的观点.尽管如此,引用的文本并没有说"内存中对象的大小",而"偏移"对于大小类型来说几乎不是一个好名称,无论它恰好存储在何处. (3认同)
  • @Draemon - 我也同意"基本混乱"评估.如果您还没有阅读过C/C++标准,您可能会认为"对象"指的是"面向对象的编程",而它并不是.阅读C标准,它既没有那些OOP对象,但却有对象,并找出答案.答案可能会让你大吃一惊! (2认同)

Alo*_*hal 212

size_t是一种无符号类型.因此,它不能代表任何负值(<0).你在计算某些东西时使用它,并确保它不会是负面的.例如,strlen()返回a,size_t因为字符串的长度必须至少为0.

在您的示例中,如果循环索引始终大于0,则使用size_t或任何其他无符号数据类型可能有意义.

当您使用size_t对象时,您必须确保在使用它的所有上下文中,包括算术,您需要非负值.例如,假设你有:

size_t s1 = strlen(str1);
size_t s2 = strlen(str2);
Run Code Online (Sandbox Code Playgroud)

你想找到的长度的差异str2str1.你做不到:

int diff = s2 - s1; /* bad */
Run Code Online (Sandbox Code Playgroud)

这是因为分配给的值diff总是为正数,即使是s2 < s1,因为计算是使用无符号类型完成的.在这种情况下,根据您的用例,您可能最好使用int(或long long)for s1s2.

C/POSIX中有一些功能可以/应该使用size_t,但不是因为历史原因.例如,fgets理想情况下应该是第二个参数size_t,但是int.

  • @Alok:两个问题:1)[`size_t`](http://codepad.org/osT7Ex1q#output)的大小是多少?2)为什么我更喜欢`size_t`而不是`unsigned int`? (8认同)
  • 请记住,在 64 位 Linux 上,int 始终是 32 位,但 size_t 是 64 位。所以 size_t 和 int 不可互换。 (6认同)
  • @Lazer - 是的,`size_t`保证是无符号类型. (4认同)
  • @Lazer:`size_t` 的大小是 `sizeof(size_t)`。C 标准保证`SIZE_MAX` 至少为65535。`size_t` 是`sizeof` 运算符返回的类型,并在标准库中使用(例如`strlen` 返回`size_t`)。正如 Brendan 所说,`size_t` 不必与 `unsigned int` 相同。 (2认同)
  • @Celeritas不,我的意思是无符号类型只能表示非负值.我可能应该说"它不能代表负面价值". (2认同)
  • @JasonOster,两个补码不是C标准的要求.如果`s2 - s1`的值溢出`int`,则行为未定义. (2认同)

Arj*_*ran 70

size_t 是一种可以保存任何数组索引的类型.

根据实施情况,它可以是以下任何一种:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

以下size_tstddef.h我的机器中的定义:

typedef unsigned long size_t;
Run Code Online (Sandbox Code Playgroud)

  • @jameshfisher 谁说“unsigned char”不能是 16 位?! (5认同)
  • @chux:的确,仅仅因为一个实现定义它并不意味着所有人都这样做.例证:64位Windows.`unsigned long`是32位,`size_t`是64位. (4认同)
  • 当然`typedef unsigned long size_t`是编译器相关的.或者你是否建议总是如此? (3认同)
  • size_t的目的到底是什么?当我可以为自己创建一个变量时:“ int mysize_t;” 或“ long mysize_t”或“ unsigned long mysize_t”。为什么有人应该为我创建此变量? (2认同)

Cir*_*四事件 66

如果你是经验类型,

echo | gcc -E -xc -include 'stddef.h' - | grep size_t
Run Code Online (Sandbox Code Playgroud)

Ubuntu 14.04 64位GCC 4.8的输出:

typedef long unsigned int size_t;
Run Code Online (Sandbox Code Playgroud)

注意,stddef.h由GCC提供,而不是src/gcc/ginclude/stddef.hGCC 4.2中的glibc .

有趣的C99外观

  • mallocsize_t作为参数,所以它确定可分配的最大尺寸.

    而且由于它也被返回sizeof,我认为它限制了任何数组的最大大小.

    另请参阅:C中数组的最大大小是多少?

  • 这件事本身并不令人困惑.试图提出许多问题并提出许多答案是令人困惑的头脑.我很惊讶这个答案和Arjun Sreedharan的答案仍然不能阻止人们提问和回答. (2认同)

cod*_*ict 22

types.h的联机帮助页说:

size_t应为无符号整数类型


sup*_*cat 18

由于还没有人提到它,主要的语言意义size_tsizeof运算符返回该类型的值.同样,主要的意义ptrdiff_t是从另一个指针中减去一个指针将产生该类型的值.接受它的库函数这样做是因为它允许这些函数在可能存在这些对象的系统上使用大小超过UINT_MAX的对象,而不会强迫调用者浪费代码在较大类型的系统上传递大于"unsigned int"的值对所有可能的对象都足够了.


Kal*_*ana 8

size_t是一种无符号整数数据类型,它只能分配 0 和大于 0 的整数值。它测量任何对象大小的字节,并由sizeof运算符返回。

const是 的语法表示size_t,但没有const你可以运行程序。

const size_t number;
Run Code Online (Sandbox Code Playgroud)

size_t经常用于数组索引和循环计数。如果编译器是32-bit它会在unsigned int. 如果编译器是64-bit它也可以工作unsigned long long int。最大大小size_t取决于编译器类型。

size_t在已经定义<stdio.h>的头文件,但它也可以由所定义的 <stddef.h><stdlib.h><string.h><time.h>,和<wchar.h>标头。

示例(带const

#include <stdio.h>

int main()
{
    const size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}
Run Code Online (Sandbox Code Playgroud)

输出: size = 800


示例(没有const

#include <stdio.h>

int main()
{
    size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0; i < value; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}
Run Code Online (Sandbox Code Playgroud)

输出: size = 800


l3x*_*l3x 8

size_t是一个 typedef,用于表示任何对象的大小(以字节为单位)。(Typedef 用于为另一种数据类型创建附加名称/别名,但不会创建新类型。)

找到它的定义stddef.h如下:

typedef unsigned long long size_t;
Run Code Online (Sandbox Code Playgroud)

size_t也定义在<stdio.h>.

size_t被 sizeof 运算符用作返回类型。

使用size_t与 sizeof 结合使用来定义数组大小参数的数据类型,如下所示:

#include <stdio.h>

void disp_ary(int *ary, size_t ary_size)
{
    for (int i = 0; i < ary_size; i++)
    {
        printf("%d ", ary[i]);
    }
}
 
int main(void)
{
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    int ary_size = sizeof(arr)/sizeof(int);
    disp_ary(arr, ary_size);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

size_t保证足够大以包含主机系统可以处理的最大对象的大小。

请注意,数组的大小限制实际上是编译和执行此代码的系统堆栈大小限制的一个因素。您应该能够在链接时调整堆栈大小(请参阅ld命令的 --stack-size参数)。

为了让您了解大概的堆栈大小:

  • 嵌入式设备上的 4K
  • Win10 1M
  • Linux 上 7.4M

许多 C 库函数都喜欢mallocmemcpy并将strlen它们的参数和返回类型声明为size_t

size_t使程序员能够通过添加/减去所需元素的数量而不是使用以字节为单位的偏移量来处理不同类型。

让我们size_t通过检查它在 C 字符串和整数数组的指针算术运算中的用法来更深入地了解它能为我们做些什么:

下面是一个使用 C 字符串的示例:

const char* reverse(char *orig)
{
  size_t len = strlen(orig);
  char *rev = orig + len - 1;
  while (rev >= orig)
  {
    printf("%c", *rev);
    rev = rev - 1;  // <= See below
  }
  return rev;
}

int main() {
  char *string = "123";
  printf("%c", reverse(string));
}
// Output: 321

0x7ff626939004 "123"  // <= orig
0x7ff626939006 "3"    // <= rev - 1 of 3
0x7ff626939005 "23"   // <= rev - 2 of 3
0x7ff626939004 "123"  // <= rev - 3 of 3
0x7ff6aade9003 ""     // <= rev is indeterminant. This can be exploited as an out of bounds bug to read memory contents that this program has no business reading.
Run Code Online (Sandbox Code Playgroud)

这对于理解使用的好处不是很有帮助,size_t因为无论您的体系结构如何,字符都是一个字节。

当我们处理数字类型时,size_t变得非常有益。

size_ttype 就像一个整数,优点是可以保存物理内存地址;该地址根据执行平台的类型而改变其大小。

以下是我们在传递整数数组时如何利用 sizeof 和 size_t 的方法:

void print_reverse(int *orig, size_t ary_size)
{
  int *rev = orig + ary_size - 1;
  while (rev >= orig)
  {
    printf("%i", *rev);
    rev = rev - 1;
  }
}

int main()
{
  int nums[] = {1, 2, 3};
  print_reverse(nums, sizeof(nums)/sizeof(*nums));

  return 0;
}

0x617d3ffb44 1  // <= orig
0x617d3ffb4c 3  // <= rev - 1 of 3
0x617d3ffb48 2  // <= rev - 2 of 3
0x617d3ffb44 1  // <= rev - 3 of 3
Run Code Online (Sandbox Code Playgroud)

上面,我们看到 int 占用 4 个字节(由于每个字节有 8 位,因此 int 占用 32 位)。

如果我们要创建一个 long 数组,我们会发现 long 在 linux64 操作系统上需要 64 位,但在 Win64 系统上只需要 32 位。因此,使用t_size, 将节省大量编码和潜在的错误,特别是在不同体系结构上运行执行地址算术的 C 代码时。

所以这个故事的寓意是“使用size_t并让你的 C 编译器完成容易出错的指针算术工作”。


dto*_*oux 7

size_t并且int不可互换.例如,64位Linux的size_t大小为64位(即sizeof(void*)),但int为32位.

另请注意,这size_t是未签名的.如果您需要签名版本,那么ssize_t在某些平台上它会与您的示例更相关.

作为一般规则,我建议int对大多数一般情况使用,并且只在特定需要时使用size_t/ ssize_t(mmap()例如).


Dav*_*lor 5

要探讨为什么size_t需要存在以及我们如何到达这里:

在实用方面,size_tptrdiff_t保证是64个位宽上的64位的实施方案中,32个比特宽上的32位实现,等等。他们不能在不破坏原有代码的情况下,在每个编译器上强制使用任何现有类型来表示这一点。

size_t或者ptrdiff_t不一定是相同的作为intptr_tuintptr_t。他们对仍然在使用的时候一定结构不同size_tptrdiff_t被添加到标准的80年代后期,并变得过时,当C99增加了许多新类型,但尚未消失(如16位Windows)。16位保护模式下的x86具有分段存储器,其中最大可能的数组或结构的大小只能是65,536字节,但是far指针需要32位宽,比寄存器宽。在这些,intptr_t本来是32个位宽,但size_tptrdiff_t可以是16位宽并适合寄存器。谁知道将来会写什么样的操作系统?从理论上讲,i386体系结构提供了具有48位指针的32位分段模型,这是操作系统从未真正使用过的模型。

内存偏移量的类型之所以不能,是long因为太多的旧代码假定该long宽度恰好是32位宽。该假设甚至已内置到UNIX和Windows API中。不幸的是,许多其他遗留代码也假定a long足够宽以容纳指针,文件偏移,自1970年以来已经过去的秒数等等。POSIX现在提供了一种标准的方法来强制使后一个假设正确,而不是前一个假设,但这两个都不是可移植的假设。

可能不是int因为90年代只有极少数的编译器制作了int64位宽。然后,通过保持long32位宽,他们真的变得很奇怪。该标准的下一个修订版声明int,其宽度大于longint是非法的,但在大多数64位系统上仍为32位宽。

它不可能是long long int,后来又添加了它,因为即使在32位系统上,它被创建为至少64位宽。

因此,需要一种新的类型。即使不是,所有其他类型也意味着数组或对象中的偏移量以外的其他内容。而且,如果从32到64位迁移的惨败中汲取了一个教训,那就是要明确类型需要具备的属性,而不要在不同的程序中使用含义不同的属性。