size_t 与 uintptr_t
C 标准保证 size_t
是可以保存任何数组索引的类型。这意味着,从逻辑上讲,size_t
应该能够保存任何指针类型。我在 Google 上发现的一些网站上读到,这是合法的和/或应该始终有效:
void *v = malloc(10);
size_t s = (size_t) v;
因此,在 C99 中,标准引入了 intptr_t
和 uintptr_t
类型,有符号和无符号类型保证能够保存指针:
uintptr_t p = (size_t) v;
那么使用 size_t
和 uintptr_t
之间有什么区别?两者都是无符号的,并且都应该能够保存任何指针类型,因此它们在功能上看起来是相同的。除了清晰之外,是否有任何真正令人信服的理由使用 uintptr_t
(或者更好的是 void *
)而不是 size_t
?在不透明的结构中,字段仅由内部函数处理,有什么理由不这样做?
出于同样的原因,ptrdiff_t 是一种能够保存指针差异的有符号类型,因此能够保存大多数任何指针,那么它与 intptr_t 有何不同?
所有这些类型不是基本上都提供相同功能的不同版本吗?如果没有,为什么?有哪些是我不能对其中一个做而我不能对另一个做的?如果是这样,为什么 C99 向该语言添加了两种本质上多余的类型?
我愿意忽略函数指针,因为它们不适用于当前的问题,但请随意提及它们,因为我偷偷怀疑它们将是“正确”答案的核心。
The C standard guarantees that size_t
is a type that can hold any array index. This means that, logically, size_t
should be able to hold any pointer type. I've read on some sites that I found on the Googles that this is legal and/or should always work:
void *v = malloc(10);
size_t s = (size_t) v;
So then in C99, the standard introduced the intptr_t
and uintptr_t
types, which are signed and unsigned types guaranteed to be able to hold pointers:
uintptr_t p = (size_t) v;
So what is the difference between using size_t
and uintptr_t
? Both are unsigned, and both should be able to hold any pointer type, so they seem functionally identical. Is there any real compelling reason to use uintptr_t
(or better yet, a void *
) rather than a size_t
, other than clarity? In an opaque structure, where the field will be handled only by internal functions, is there any reason not to do this?
By the same token, ptrdiff_t
has been a signed type capable of holding pointer differences, and therefore capable of holding most any pointer, so how is it distinct from intptr_t
?
Aren't all of these types basically serving trivially different versions of the same function? If not, why? What can't I do with one of them that I can't do with another? If so, why did C99 add two essentially superfluous types to the language?
I'm willing to disregard function pointers, as they don't apply to the current problem, but feel free to mention them, as I have a sneaking suspicion they will be central to the "correct" answer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
不一定!回想一下分段 16 位架构的时代,例如:一个数组可能仅限于单个段(因此 16 位
size_t
就可以),但是您可以有多个段(因此 32 位需要 -bit intptr_t 类型来选择段及其内的偏移量)。我知道这些事情在当今统一可寻址的未分段架构中听起来很奇怪,但标准必须满足比“2009 年的正常情况”更广泛的多样性,你知道!-)Not necessarily! Hark back to the days of segmented 16-bit architectures for example: an array might be limited to a single segment (so a 16-bit
size_t
would do) BUT you could have multiple segments (so a 32-bitintptr_t
type would be needed to pick the segment as well as the offset within it). I know these things sound weird in these days of uniformly addressable unsegmented architectures, but the standard MUST cater for a wider variety than "what's normal in 2009", you know!-)关于您的发言:
不幸的是,这是不正确的。指针和数组索引不是一回事。设想一种将数组限制为 65536 个元素但允许指针将任何值寻址到巨大的 128 位地址空间的一致实现是非常合理的。
C99 规定,
size_t
变量的上限由SIZE_MAX
定义,可以低至 65535(请参阅 C99 TR3,7.18.3,在 C11 中未更改)。如果指针在现代系统中被限制在这个范围内,那么它们将受到相当的限制。在实践中,您可能会发现您的假设成立,但这并不是因为标准保证了这一点。因为它实际上并不能保证这一点。
Regarding your statement:
This is unfortunately incorrect. Pointers and array indexes are not the same thing. It's quite plausible to envisage a conforming implementation that limits arrays to 65536 elements but allows pointers to address any value into a massive 128-bit address space.
C99 states that the upper limit of a
size_t
variable is defined bySIZE_MAX
and this can be as low as 65535 (see C99 TR3, 7.18.3, unchanged in C11). Pointers would be fairly limited if they were restricted to this range in modern systems.In practice, you'll probably find that your assumption holds, but that's not because the standard guarantees it. Because it actually doesn't guarantee it.
我会让所有其他答案代表自己关于段限制、奇异架构等的推理。
难道简单的名称差异不足以为正确的事情使用正确的类型吗?
如果您要存储尺寸,请使用
size_t
。如果您要存储指针,请使用intptr_t
。阅读你的代码的人会立即知道“啊哈,这是某个东西的大小,可能以字节为单位”,并且“哦,由于某种原因,这是一个被存储为整数的指针值”。否则,您可以只使用
unsigned long
(或者,在现代,unsigned long long
)来处理所有事情。大小并不代表一切,类型名称具有有用的含义,因为它有助于描述程序。I'll let all the other answers stand for themselves regarding the reasoning with segment limitations, exotic architectures, and so on.
Isn't the simple difference in names reason enough to use the proper type for the proper thing?
If you're storing a size, use
size_t
. If you're storing a pointer, useintptr_t
. A person reading your code will instantly know that "aha, this is a size of something, probably in bytes", and "oh, here's a pointer value being stored as an integer, for some reason".Otherwise, you could just use
unsigned long
(or, in these here modern times,unsigned long long
) for everything. Size is not everything, type names carry meaning which is useful since it helps describe the program.最大数组的大小可能小于指针。考虑分段架构 - 指针可能是 32 位,但单个段可能只能寻址 64KB(例如旧的实模式 8086 架构)。
虽然这些不再在台式机中普遍使用,但 C 标准旨在支持甚至小型的专用体系结构。例如,仍然有一些使用 8 位或 16 位 CPU 的嵌入式系统正在开发中。
It's possible that the size of the largest array is smaller than a pointer. Think of segmented architectures - pointers may be 32-bits, but a single segment may be able to address only 64KB (for example the old real-mode 8086 architecture).
While these aren't commonly in use in desktop machines anymore, the C standard is intended to support even small, specialized architectures. There are still embedded systems being developed with 8 or 16 bit CPUs for example.
我想(这适用于所有类型名称)它可以更好地在代码中传达您的意图。
例如,尽管
unsigned short
和wchar_t
在 Windows 上的大小相同(我认为),但使用wchar_t
而不是unsigned Short
表明您将使用它来存储宽字符,而不仅仅是一些任意数字。I would imagine (and this goes for all type names) that it better conveys your intentions in code.
For example, even though
unsigned short
andwchar_t
are the same size on Windows (I think), usingwchar_t
instead ofunsigned short
shows the intention that you will use it to store a wide character, rather than just some arbitrary number.回顾过去和未来,并回想起各种奇怪的架构分散在景观中,我很确定他们正在尝试包装所有现有系统,并为所有可能的未来系统提供支持。
可以肯定的是,按照事情的解决方式,我们到目前为止不需要那么多类型。
但即使在 LP64(一种相当常见的范例)中,我们也需要 size_t 和 ssize_t 作为系统调用接口。人们可以想象一个更加受限的遗留系统或未来系统,其中使用完整的 64 位类型的成本很高,并且他们可能希望在大于 4GB 的 I/O 操作上下注,但仍然拥有 64 位指针。
我想你一定想知道:可能已经开发了什么,将来可能会发生什么。 (也许是 128 位分布式系统互联网范围的指针,但系统调用中不超过 64 位,甚至可能是“遗留”32 位限制。:-) 遗留系统可能会获得新的 C 编译器。另外
,看看当时周围存在什么。除了无数的 286 实模式内存模型之外,CDC 60 位字/18 位指针主机又如何呢?克雷系列怎么样?别介意普通的 ILP64、LP64、LLP64。 (我一直认为微软对 LLP64 很自命不凡,它应该是 P64。)我当然可以想象一个委员会试图涵盖所有基础......
Looking both backwards and forwards, and recalling that various oddball architectures were scattered about the landscape, I'm pretty sure they were trying to wrap all existing systems and also provide for all possible future systems.
So sure, the way things settled out, we have so far needed not so many types.
But even in LP64, a rather common paradigm, we needed size_t and ssize_t for the system call interface. One can imagine a more constrained legacy or future system, where using a full 64-bit type is expensive and they might want to punt on I/O ops larger than 4GB but still have 64-bit pointers.
I think you have to wonder: what might have been developed, what might come in the future. (Perhaps 128-bit distributed-system internet-wide pointers, but no more than 64 bits in a system call, or perhaps even a "legacy" 32-bit limit. :-) Image that legacy systems might get new C compilers...
Also, look at what existed around then. Besides the zillion 286 real-mode memory models, how about the CDC 60-bit word / 18-bit pointer mainframes? How about the Cray series? Never mind normal ILP64, LP64, LLP64. (I always thought microsoft was pretensious with LLP64, it should have been P64.) I can certainly imagine a committee trying to cover all bases...
除了其他好的答案之外:
size_t
是在、中定义的。 h>、、、、、
。它至少是 16 位的。uintptr_t
在
中定义。它是可选。兼容的库可能不会定义它,可能是因为没有足够宽的整数类型来往返void*
-uintptr_t
-void *
。两者都是无符号整数类型。
注意:可选伴随
intptr_t
是一个有符号整数类型。In addition to other good answers:
size_t
is defined in<stddef.h>, <stdio.h>, <stdlib.h>, <string.h>, <time.h>, <uchar.h>, <wchar.h>
. It is at least 16-bit.uintptr_t
is defined in<stdint.h>
. It is optional. A compliant library might not define it, likely because there is not a wide-enough integer type to round trip avoid*
-uintptr_t
-void *
.Both are unsigned integer types.
Note: the optional companion
intptr_t
is a signed integer type.这意味着 intptr_t 必须始终替代 size_t,反之亦然。
Implying that intptr_t must always substitute for size_t and visa versa.