C语言中的size_t是多少?


626

size_t对C 感到困惑。我知道它是由sizeof运算符返回的。但是到底是什么呢?它是数据类型吗?

假设我有一个for循环:

for(i = 0; i < some_size; i++)

我应该使用int i;还是size_t i;


11
如果这些是您唯一的选择,请使用intif some_size签名,size_t如果未签名。
Nate

8
@Nate这是不正确的。POSIX具有ssize_t类型,但实际使用的正确类型是ptrdiff_t。
史蒂文·斯图尔特·加卢斯

2
答案不像英特尔®64上的“低级编程:C,汇编和程序执行”那样清晰。如书中所述,使用索引int i可能不足以解决庞大的数组。因此,通过使用size_t i您可以解决更多的索引,即使您有一个庞大的数组也不应该成为问题。size_t是一种数据类型:通常为a,unsigned long int但这取决于您的系统。
bruno

Answers:


461

从维基百科

根据1999 ISO C标准(C99),它size_t是至少16位的无符号整数类型(请参阅第7.17和7.18.3节)。

size_t是由多种C / C ++标准(例如在中定义的C99 ISO / IEC 9899标准)定义的无符号数据类型stddef.h1可以通过包含进一步导入, stdlib.h因为此文件在内部包含了stddef.h

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

暗示地,size_t一种保证包含任何数组索引的类型。


4
“采用或返回大小的库函数期望它们的类型为... size_t”,除了stat()使用off_t作为文件大小之外
Draemon 2010年

64
@Draemon该评论反映了一个基本的混乱。 size_t用于内存中的对象。C标准甚至没有定义stat()off_t(这些都是POSIX的定义)或任何与磁盘或文件系统一样-它停止本身在FILE流。就大小要求而言,虚拟内存管理与文件系统和文件管理完全不同,因此off_t此处无关紧要。
jw013 2013年

3
@ jw013:我很难称其为根本性的混乱,但是您提出了一个有趣的观点。尽管如此,引用的文字并没有说“内存对象的大小”,并且“偏移量”对于大小类型而言几乎不是一个好名字,无论它存储在什么地方。
Draemon 2013年

30
@Draemon好点。这个答案引用了维基百科,在我看来,在这种情况下,它没有最好的解释。C标准本身更加清晰:它定义size_tsizeof运算符结果的类型(大约为7.17p2 <stddef.h>)。6.5节准确地解释了C表达式的工作原理(6.5.3.4 sizeof)。由于您无法申请sizeof磁盘文件(主要是因为C甚至都没有定义磁盘和文件的工作方式),因此没有混淆的余地。换句话说,怪罪维基百科(这个答案是引用维基百科而不是实际的C标准)。
jw013

2
@Draemon-我也同意“基本困惑”评估。如果您尚未阅读C / C ++标准,则可能会认为“对象”是指“面向对象的程序设计”,而并非如此。阅读C标准,该标准没有那些OOP对象,但是有对象,然后找出来。答案可能会让你大吃一惊!
Heath Hunnicutt

220

size_t是无符号类型。因此,它不能表示任何负值(<0)。您在计数某物时使用它,并确保它不能为负数。例如,strlen()返回a,size_t因为字符串的长度必须至少为0。

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

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

size_t s1 = strlen(str1);
size_t s2 = strlen(str2);

你想找到的长度的差异str2str1。您不能:

int diff = s2 - s1; /* bad */

这是因为diff,即使使用s2 < s1,分配给的值也总是正数,因为计算是使用无符号类型完成的。在这种情况下,这取决于你的使用情况是什么,你可能会关闭使用更好的int(或long long)为s1s2

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


8
@Alok:两个问题:1)的大小是size_t多少?2)为什么我要喜欢size_t类似的东西unsigned int
Lazer 2010年

2
@Lazer:的大小size_tsizeof(size_t)。C标准保证操作符将返回SIZE_MAX至少65535 size_t的类型sizeof,并在标准库中使用(例如strlenreturn size_t)。正如Brendan所说,size_t不必与相同unsigned int
Alok Singhal 2010年

4
@Lazer-是的,size_t保证是无符号类型。
Alok Singhal 2010年

2
@Celeritas不,我的意思是,无符号类型只能表示非负值。我可能应该说“它不能代表负值”。
Alok Singhal

4
@ JasonOster,C标准中不需要二进制补码。如果s2 - s1an 值溢出int,则行为未定义。
Alok Singhal

73

size_t 是可以容纳任何数组索引的类型。

根据实现的不同,它可以是以下任意一种:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

size_tstddef.h我的机器中的定义方式:

typedef unsigned long size_t;

4
当然typedef unsigned long size_t取决于编译器。还是您建议始终如此?
chux-恢复莫妮卡2014年

4
@chux:的确,仅仅因为一个实现定义了它,并不意味着全部都定义了。例子:64位Windows。unsigned long是32位,size_t是64位。
TimČas2014年

2
size_t的目的到底是什么?当我可以为自己创建一个变量时:“ int mysize_t;” 或“ long mysize_t”或“ unsigned long mysize_t”。为什么有人应该为我创建此变量?
2013年

1
@midkin size_t不是变量。当您要表示内存中对象的大小时,可以使用这种类型。
阿琼·斯雷达拉恩

1
size_t在32位计算机上总是32位,是否同样是64位,这是真的吗?
John Wu

70

如果您是经验类型

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提供的,而不是src/gcc/ginclude/stddef.hGCC 4.2中的glibc 。

有趣的C99外观

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

    并且由于它也由返回sizeof,因此我认为它限制了任何数组的最大大小。

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


1
我有相同的环境,但是,我已经通过GCC的“ -m32”选项对其进行了32位测试,结果是:“ typedef unsigned int size_t”。感谢分享这个很棒的命令@Ciro,它对我有很大帮助!:-)
silvioprog

2
事情本身并不令人困惑。试图提出许多问题并给出许多答案的是令人困惑的头脑。令我惊讶的是,这个答案以及Arjun Sreedharan的答案仍然没有阻止人们提问和回答。
biocyberman

1
伟大的答案,因为它实际上告诉你什么size_t,至少在流行的Linux发行版。
Andrey Portnoy


19

由于没有人提及它,因此语言的主要意义size_tsizeof运算符返回该类型的值。同样,的主要含义ptrdiff_t是从另一个指针中减去一个指针将产生该类型的值。接受它的库函数之所以这样做,是因为它将允许此类函数与大小可能超过UINT_MAX的对象一起工作(在可能存在此类对象的系统上),而不会强迫调用者浪费代码在较大类型的系统上传递大于“ unsigned int”的值满足所有可能的对象。


我的问题一直是:如果sizeof不存在,是否需要size_t?
Dean P

@DeanP:也许不是,尽管这样会出现一个问题,例如,应该使用哪种参数类型malloc()。就个人而言,我本来希望看到带有类型为intlong和作为参数的版本long long,其中一些实现促进了较短的类型,而另一些实现则实现了lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);}[ 例如,在某些平台上,调用imalloc(123)会比调用便宜lmalloc(123);,甚至在size_t16 的平台上也是如此。位,想要分配以“长”值计算的大小的代码...
supercat,

...如果值大于分配器可以处理的大小,则应该能够依靠分配失败。
超级猫

11

要探讨为什么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位迁移的惨败中汲取了一个教训,那就是要明确类型需要具备的属性,而不要在不同的程序中使用不同的含义。


不赞成“,size_t并且ptrdiff_t在64位实现中保证为64位宽”,等等。该保证被夸大了。的范围size_t主要由实现的存储容量决定。“ n位实现”主要是整数的本机处理器宽度。当然,许多实现使用相似大小的内存和处理器总线宽度,但是存在内存不足的较宽的本机整数,或存在大量内存的较窄的处理器,这确实将这两个实现属性分开。
chux-恢复莫妮卡

8

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

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

作为一般规则,我建议int在大多数一般情况下使用,仅在有特殊需要时才使用size_t/ ssize_tmmap()例如)。


3

通常,如果您从0开始并向上移动,请始终使用无符号类型,以避免溢出导致您进入负值情况。这非常重要,因为如果您的数组边界恰好小于循环的最大值,但是循环的最大值恰好大于您的类型的最大值,那么您将环绕负值,并且可能会遇到分段错误(SIGSEGV )。因此,一般而言,永远不要将int用作从0开始向上的循环。使用未签名的。


3
我不能接受你的论点。您说最好是溢出错误以静默方式导致访问数组中的有效数据吗?
maf-soft

1
@ maf-soft是正确的。如果未检测到错误,则它将比程序崩溃更严重。为什么这个答案被投票赞成?
yoyo_fun

如果它访问数组中的有效数据,那么这不是错误,因为未签名类型不会以极限已签名类型溢出。这个逻辑家伙是什么?假设出于某种原因,您使用char来遍历256个元素数组... signed将在127处溢出,第128个元素将为sigsegv,但是如果您使用unsigned,则它将按预期遍历整个数组。再说一次,当您使用int时,您的数组实际上不会大于20亿个元素,因此无论哪种方式都没有关系……
Purple Ice

1
我无法想象整数溢出不是正负的任何情况。仅仅因为您没有遇到段错误就并不意味着您看到了正确的行为!无论偏移量是正值还是负值,您都可能遇到分段错误。这一切都取决于您的内存布局。@PurpleIce,我不认为您是在说这个答案。您的论据似乎是您应该选择一个足够大的数据类型,以容纳要放入的最大值,这只是普通常识。
索伦·比约恩斯塔德

也就是说,我确实更喜欢在语义上为循环索引使用unsigned类型;如果您的变量永远不会为负,那么您最好在选择的类型中指出。它还可能使编译器发现一个值最终为负的错误,尽管GCC至少在发现该特定错误时非常糟糕(有一次我将unsigned初始化为-1却没有得到警告)。同样,size_t在语义上适合于数组索引。
索伦·比约恩斯塔德

3

size_t是无符号整数数据类型。在使用GNU C库的系统上,这将是unsigned int或unsigned long int。size_t通常用于数组索引和循环计数。


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

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


-3

据我了解,size_t是一个unsigned整数,其位大小足以容纳本机体系结构的指针。

所以:

sizeof(size_t) >= sizeof(void*)

16
不对。指针的大小可以大于size_t。几个示例:x86实模式下的C编译器可以具有32位FARHUGE指针,但是size_t仍为16位。另一个例子:Watcom C曾经有一个特殊的胖指针用于扩展内存,它只有48位宽,但size_t没有。在具有哈佛架构的嵌入式控制器上,您也没有任何关联,因为两者都涉及不同的地址空间。
PatrickSchlüter2013年

1
在该stackoverflow.com/questions/1572099/…上,还有更多示例AS / 400,具有128位指针和32位size_t
PatrickSchlüter2013年

这是公然的错误。但是,让我们将其保留在这里
Antti Haapala
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.