将 sizeof() 的结果分配给 ssize_t

Assign result of sizeof() to ssize_t

我碰巧需要将 sizeof(x) 的结果与 ssize_t 的结果进行比较。

当然 GCC 报错了(幸运的是我(我用的是 -Wall -Wextra -Werror)),我决定做一个宏来得到 sizeof().

的签名版本
#define ssizeof (ssize_t)sizeof

然后我可以这样使用它:

for (ssize_t i = 0; i < ssizeof(x); i++)

问题是,我能保证SSIZE_MAX >= SIZE_MAX吗?我想,可悲的是,这永远不会是真的。

或者至少 sizeof(ssize_t) == sizeof(size_t),这将减少一半的值,但仍然足够接近。

我在 POSIX 文档中没有找到 ssize_tsize_t 之间的任何关系。

相关问题:

请注意,您实际上不能这样做。

x86 中可能的最大对象 Linux 大小刚好低于 0xB0000000,而 SSIZE_T_MAX 是 0x7FFFFFFF。

我还没有检查 read 和东西是否真的可以处理最大可能的对象,但如果它们可以的话,它会像这样工作:

ssize_t result = read(fd, buf, count);
if (result != -1) {
    size_t offset = (size_t) result;
    /* handle success */
} else {
    /* handle failure */
}

您可能会发现 libc 被破坏了。如果是这样,如果内核是好的,这将起作用:

ssize_t result = sys_read(fd, buf, count);
if (result >= 0 || result < -256) {
    size_t offset = (size_t) result;
    /* handle success */
} else {
    errno = (int)-result;
    /* handle failure */
}

不保证SSIZE_MAX >= SIZE_MAX。事实上,情况不太可能是这样,因为 size_tssize_t 很可能是对应的无符号和有符号类型,所以(在所有实际架构上)SIZE_MAX > SSIZE_MAX。将无符号值转换为无法保存该值的有符号类型是未定义的行为。所以从技术上讲,您的宏有问题。

实际上,至少在 64 位平台上,如果要转换为 ssize_t 的值是实际存在的对象的大小,则不太可能遇到麻烦。但如果对象是理论上的(例如 sizeof(char[3][1ULL<<62])),您可能会感到不愉快。

注意类型ssize_t唯一有效的负值是-1,这是一个错误指示。您可能会混淆由 Posix 定义的 ssize_t 和自 C99 以来在标准 C 中定义的 ptrdiff_t。这两种类型在大多数平台上是相同的,通常是 size_t 对应的有符号整数类型,但其中 none 的行为由任一标准保证。但是,这两种类型的语义是不同的,使用时需要注意:

  • ssize_t 由多个 Posix 接口 return 编辑,以允许函数发出已处理字节数或错误指示的信号;错误指示必须为 -1。没有期望任何可能的尺寸都适合 ssize_t; Posix 理由指出:

    A conforming application would be constrained not to perform I/O in pieces larger than {SSIZE_MAX}.

    对于returnssize_t的大部分接口来说这不是问题,因为Posix一般不需要接口来保证处理所有数据。例如,readwrite 都接受描述缓冲区长度的 size_t read/written 和 return 描述缓冲区长度的 ssize_t实际字节数 read/written;这意味着即使有更多数据可用,也不会超过 SSIZE_MAX 字节 read/written 。然而,Posix 的基本原理还指出,一个特定的实现可能会提供一个扩展,允许处理更大的块("a conforming application using extensions would be able to use the full range if the implementation provided an extended range"),这个想法是该实现可以,例如,指定 return 除了 -1 之外的值将通过将它们转换为 size_t 来解释。这样的扩展是不可移植的;在实践中,大多数实现确实将可以在单个调用中处理的字节数限制为可以在 ssize_t.

  • 中报告的数字
  • ptrdiff_t 是(在标准 C 中)两个指针之间的差异结果的类型。为了明确定义减法指针,两个指针必须指向同一个对象,要么指向对象,要么指向紧跟在对象之后的字节。 C 委员会认识到,如果 ptrdiff_tsize_t 的带符号等价物,那么两个指针之间的差异可能无法表示,从而导致未定义的行为,但他们更喜欢要求 ptrdiff_t 是比 size_t 更大的类型。你可以反对这个决定——很多人都反对——但它自 C90 以来就已经存在,现在似乎不太可能改变。 (来自 §6.5.6/9 的当前标准措辞:"If the result is not representable in an object of that type [ptrdiff_t], the behavior is undefined.")

    与 Posix 一样,C 标准没有定义未定义的行为,因此将其解释为 禁止 两个指针相减是错误的大物体。始终允许实现定义标准未定义的行为结果,因此实现指定如果 PQ 是指向同一对象的两个指针是完全有效的,其中 P >= Q,那么即使减法溢出,(size_t)(P - Q) 也是指针之间数学上正确的差值。当然,依赖于此类扩展的代码不会完全可移植,但如果扩展足够普遍,那可能不是问题。

作为最后一点,使用 -1 作为错误指示(在 ssize_t 中)和作为指针减法的可能可转换结果(在 ptrdiff_t 中)的歧义不太可能如果 size_t 与指针一样大,则在实践中出现。如果 size_t 和指针一样大,那么 P-Q 在数学上正确的值可能是 (size_t)(-1)(又名 SIZE_MAX)的唯一方法是 PQ 指的是大小 SIZE_MAX,假设 size_t 与指针的宽度相同,这意味着对象加上后面的字节占据了所有可能的指针价值。这与某些指针值 (NULL) 与任何有效地址不同的要求相矛盾,因此我们可以得出结论,对象的真实最大大小必须小于 SIZE_MAX

我要把它当作一个 X-Y 问题。您遇到的问题是您想要将有符号数与无符号数进行比较。而不是将 sizeof 的结果转换为 ssize_t,您应该检查 ssize_t 值是否小于零。如果是,那么您知道它小于您的 size_t 值。如果不是,则可以将其转换为 size_t,然后进行比较。

例如,这里有一个比较函数,如果有符号数小于无符号数,则为 returns -1,如果等于则为 0,如果有符号数大于无符号数则为 1数量:

int compare(ssize_t signed_number, size_t unsigned_number) {
    int ret;
    if (signed_number < 0 || (size_t) signed_number < unsigned_number) {
        ret = -1;
    }
    else {
        ret = (size_t) signed_number > unsigned_number;
    }
    return ret;
}

如果您想要的只是等同于 < 的操作,您可以使用类似这样的操作来简化一些操作:

(signed_number < 0 || (size_t) signed_number < unsigned_number))

如果 signed_number 小于 unsigned_number,该行会给你 1 并且它限制了分支开销。只需要一个额外的 < 操作和一个 logical-OR.

ssize_t 是 POSIX 类型,它没有定义为 C 标准的一部分。 POSIX定义ssize_t必须能够处理区间[-1,SSIZE_MAX]内的数字,所以原则上它甚至不需要是普通的有符号类型。这个有点奇怪的定义的原因是唯一使用 ssize_t 的地方是 read/write/etc 的 return 值。函数。

实际上,它始终是与 size_t 大小相同的普通有符号类型。但是如果你想对你的类型真正迂腐,除了处理 IO 系统调用的 return 值之外,你不应该将它用于其他目的。对于一般的 "pointer-sized" 有符号整数类型,C89 定义了 ptrdiff_t。这在实践中将与 ssize_t.

相同

此外,如果您查看 official spec for read(),您会看到 'nbyte' 参数表示 'If the value of nbyte is greater than {SSIZE_MAX}, the result is implementation-defined.'。因此,即使 size_t 能够表示比 SSIZE_MAX 更大的值,使用比 IO 系统调用更大的值是实现定义的行为(唯一使用 ssize_t 的地方,如前所述)。与 write() 等类似