我对C中的size_t
感到困惑。我知道它是由sizeof
运算符返回的。但究竟是什么呢?它是数据类型吗?
假设我有一个for
循环:
for(i = 0; i < some_size; i++)
我应该使用int i;
还是size_t i;
?
答案 0 :(得分:415)
根据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
。而且,最多 常用的基于编译器 operator sizeof应该评估为a 与...兼容的常数值size_t
。
作为一种含义,size_t
是一种保证保存任何数组索引的类型。
答案 1 :(得分:201)
size_t
是无符号类型。因此,它不能表示任何负值(&lt; 0)。你在计算某些东西时使用它,并确保它不会是负面的。例如,strlen()
返回size_t
,因为字符串的长度必须至少为0.
在您的示例中,如果循环索引始终大于0,则使用size_t
或任何其他无符号数据类型可能是有意义的。
使用size_t
对象时,必须确保在使用它的所有上下文中(包括算术),您需要非负值。例如,假设你有:
size_t s1 = strlen(str1);
size_t s2 = strlen(str2);
您希望找到str2
和str1
长度的差异。你做不到:
int diff = s2 - s1; /* bad */
这是因为分配给diff
的值始终为正数,即使s2 < s1
也是如此,因为计算是使用无符号类型完成的。在这种情况下,根据您的使用案例,您可能最好使用int
(或long long
)来s1
和s2
。
C / POSIX中有一些功能可以/应该使用size_t
,但由于历史原因不行。例如,fgets
的第二个参数理想情况下应为size_t
,但为int
。
答案 2 :(得分:66)
size_t
是一种可以保存任何数组索引的类型。
根据实施情况,它可以是以下任何一种:
unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
以下是我的机器size_t
中stddef.h
的定义方式:
typedef unsigned long size_t;
答案 3 :(得分:62)
如果您是经验型,
echo | gcc -E -xc -include 'stddef.h' - | grep size_t
Ubuntu 14.04 64位GCC 4.8的输出:
typedef long unsigned int size_t;
请注意,stddef.h
由GCC提供,而不是在GCC 4.2中src/gcc/ginclude/stddef.h
下的glibc。
有趣的C99外观
malloc
将size_t
作为参数,因此它确定了可以分配的最大大小。
由于它也由sizeof
返回,我认为它限制了任何数组的最大大小。
答案 4 :(得分:20)
types.h的联合帮助页说:
size_t应为无符号整数类型
答案 5 :(得分:15)
由于还没有人提及它,size_t
的主要语言意义是sizeof
运算符返回该类型的值。同样,ptrdiff_t
的主要意义是从另一个指针中减去一个指针将产生该类型的值。接受它的库函数这样做是因为它允许这些函数在可能存在这些对象的系统上使用大小超过UINT_MAX的对象,而不会强迫调用者浪费代码传递大于&#34; unsigned int&#34;在较大类型足以满足所有可能对象的系统上。
答案 6 :(得分:7)
size_t
和int
不可互换。例如,在64位Linux上,size_t
的大小为64位(即sizeof(void*)
),但int
为32位。
另请注意,size_t
未签名。如果您需要签名版本,那么在某些平台上会有ssize_t
,这与您的示例更相关。
作为一般规则,我建议您针对大多数一般情况使用int
,并在特定需要时使用size_t
/ ssize_t
(使用mmap()
例子)。
答案 7 :(得分:4)
要探讨为什么size_t
必须存在以及我们如何到达这里:
用务实的话来说,size_t
和ptrdiff_t
在64位实现中保证为64位宽,在32位实现中保证为32位宽,依此类推。他们不能在不破坏原有代码的情况下,在每个编译器上强制使用任何现有类型来表示这一点。
size_t
或ptrdiff_t
不一定与intptr_t
或uintptr_t
相同。在80年代后期将size_t
和ptrdiff_t
添加到标准中时,在某些仍在使用的体系结构上它们有所不同,而当C99添加许多新类型但尚未消失时(例如, 16位Windows)。 16位保护模式下的x86具有分段存储器,其中最大可能的数组或结构的大小只能是65,536字节,但是far
指针需要32位宽,比寄存器宽。在这些寄存器上,intptr_t
的宽度为32位,但是size_t
和ptrdiff_t
的宽度为16位并适合寄存器。谁知道将来会写什么样的操作系统?从理论上讲,i386体系结构提供了具有48位指针的32位分段模型,这是操作系统从未真正使用过的。
内存偏移量的类型不能为long
,因为太多的旧代码假定long
正好是32位宽。该假设甚至已内置到UNIX和Windows API中。不幸的是,许多其他遗留代码也假定long
的宽度足以容纳指针,文件偏移,自1970年以来经过的秒数,等等。 POSIX现在提供了一种标准化的方法来强制使后一个假设正确,而不是前一个假设,但这两个都不是可移植的假设。
不可能是int
,因为在90年代只有极少数的编译器制作了int
64位宽。然后,他们通过保持long
的32位宽度确实感到很奇怪。该标准的下一修订版宣布int
的宽度大于long
是非法的,但是int
在大多数64位系统上仍为32位宽。
它不可能是long long int
,后来又添加了,因为它被创建为至少64位宽,即使在32位系统上也是如此。
因此,需要一种新类型。即使不是,其他所有类型也意味着数组或对象中的偏移量以外的其他内容。而且,如果从32到64位迁移的惨败中汲取了一个教训,那就是要明确类型需要具备的属性,而不要在不同的程序中使用不同的含义。
答案 8 :(得分:3)
通常,如果从0开始向上,则始终使用无符号类型以避免溢出将您带入负值情况。这非常重要,因为如果您的数组边界恰好小于循环的最大值,但是您的循环最大值恰好大于您的类型的最大值,那么您将回绕负数并且可能会遇到{{3} (SIGSEGV)。所以,一般来说,永远不要使用int作为从0开始向上的循环。使用未签名的。
答案 9 :(得分:2)
size_t是无符号整数数据类型。在使用GNU C库的系统上,这将是unsigned int或unsigned long int。 size_t通常用于数组索引和循环计数。
答案 10 :(得分:1)
size_t 或任何无符号类型可能被视为循环变量,因为循环变量通常大于或等于0.
当我们使用 size_t 对象时,我们必须确保在使用它的所有上下文中,包括算术,我们只需要非负值。例如,以下程序肯定会给出意想不到的结果:
// C program to demonstrate that size_t or
// any unsigned int type should be used
// carefully when used in a loop
#include<stdio.h>
int main()
{
const size_t N = 10;
int a[N];
// This is fine
for (size_t n = 0; n < N; ++n)
a[n] = n;
// But reverse cycles are tricky for unsigned
// types as can lead to infinite loop
for (size_t n = N-1; n >= 0; --n)
printf("%d ", a[n]);
}
Output
Infinite loop and then segmentation fault
答案 11 :(得分:1)
size_t
是无符号整数数据类型,只能分配0且大于0的整数值。它测量任何对象大小的字节,并由sizeof
运算符返回。
const
是size_t
的语法表示形式,但是如果没有const
,则可以运行程序。
const size_t number;
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);
}
输出-: 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);
}
输出-: size = 800
答案 12 :(得分:1)
这是特定于平台的 typedef
。例如,在特定机器上,它可能是 unsigned int
或 unsigned long
。您应该使用此定义来提高代码的可移植性。
答案 13 :(得分:-4)
根据我的理解,size_t
是一个unsigned
整数,其位大小足以容纳本机架构的指针。
所以:
sizeof(size_t) >= sizeof(void*)