我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是一种保证保存任何数组索引的类型.
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)
你想找到的长度的差异str2和str1.你做不到:
int diff = s2 - s1; /* bad */
Run Code Online (Sandbox Code Playgroud)
这是因为分配给的值diff总是为正数,即使是s2 < s1,因为计算是使用无符号类型完成的.在这种情况下,根据您的用例,您可能最好使用int(或long long)for s1和s2.
C/POSIX中有一些功能可以/应该使用size_t,但不是因为历史原因.例如,fgets理想情况下应该是第二个参数size_t,但是int.
Arj*_*ran 70
size_t 是一种可以保存任何数组索引的类型.
根据实施情况,它可以是以下任何一种:
unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
以下size_t是stddef.h我的机器中的定义:
typedef unsigned long size_t;
Run Code Online (Sandbox Code Playgroud)
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外观
malloc取size_t作为参数,所以它确定可分配的最大尺寸.
而且由于它也被返回sizeof,我认为它限制了任何数组的最大大小.
另请参阅:C中数组的最大大小是多少?
sup*_*cat 18
由于还没有人提到它,主要的语言意义size_t是sizeof运算符返回该类型的值.同样,主要的意义ptrdiff_t是从另一个指针中减去一个指针将产生该类型的值.接受它的库函数这样做是因为它允许这些函数在可能存在这些对象的系统上使用大小超过UINT_MAX的对象,而不会强迫调用者浪费代码在较大类型的系统上传递大于"unsigned int"的值对所有可能的对象都足够了.
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
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参数)。
为了让您了解大概的堆栈大小:
许多 C 库函数都喜欢malloc,memcpy并将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 编译器完成容易出错的指针算术工作”。
size_t并且int不可互换.例如,64位Linux的size_t大小为64位(即sizeof(void*)),但int为32位.
另请注意,这size_t是未签名的.如果您需要签名版本,那么ssize_t在某些平台上它会与您的示例更相关.
作为一般规则,我建议int对大多数一般情况使用,并且只在特定需要时使用size_t/ ssize_t(mmap()例如).
要探讨为什么size_t需要存在以及我们如何到达这里:
在实用方面,size_t并ptrdiff_t保证是64个位宽上的64位的实施方案中,32个比特宽上的32位实现,等等。他们不能在不破坏原有代码的情况下,在每个编译器上强制使用任何现有类型来表示这一点。
甲size_t或者ptrdiff_t不一定是相同的作为intptr_t或uintptr_t。他们对仍然在使用的时候一定结构不同size_t而ptrdiff_t被添加到标准的80年代后期,并变得过时,当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中。不幸的是,许多其他遗留代码也假定a long足够宽以容纳指针,文件偏移,自1970年以来已经过去的秒数等等。POSIX现在提供了一种标准的方法来强制使后一个假设正确,而不是前一个假设,但这两个都不是可移植的假设。
可能不是int因为90年代只有极少数的编译器制作了int64位宽。然后,通过保持long32位宽,他们真的变得很奇怪。该标准的下一个修订版声明int,其宽度大于long,int是非法的,但在大多数64位系统上仍为32位宽。
它不可能是long long int,后来又添加了它,因为即使在32位系统上,它被创建为至少64位宽。
因此,需要一种新的类型。即使不是,所有其他类型也意味着数组或对象中的偏移量以外的其他内容。而且,如果从32到64位迁移的惨败中汲取了一个教训,那就是要明确类型需要具备的属性,而不要在不同的程序中使用含义不同的属性。