【问题标题】:C size_t and ssize_t negative valueC size_t 和 ssize_t 负值
【发布时间】:2012-08-29 09:59:50
【问题描述】:

size_t 被声明为unsigned int,因此它不能表示负值。
所以有ssize_t 这是size_t签名 类型,对吗?
这是我的问题:

#include <stdio.h>
#include <sys/types.h>

int main(){
size_t a = -25;
ssize_t b = -30;
printf("%zu\n%zu\n", a, b);
return 0;
}

我为什么得到:

18446744073709551591
18446744073709551586

结果?
我知道size_t 这可能是可能的,因为它是无符号类型,但为什么ssize_t 也得到错误结果??

【问题讨论】:

  • 您使用了错误的格式说明符。
  • ssize_t 不在标准 C 中,它来自 POSIX。 ssize_t 在标准 C 中的关闭是 ptrdiff_t
  • size_t 是无符号整数类型,但通常不是unsigned int。通常相当于unsigned longunsigned long long

标签: c size-t


【解决方案1】:

在第一种情况下,您分配给一个无符号类型 - a。在第二种情况下,您使用了错误的格式说明符。第二个说明符应该是%zd 而不是%zu

【讨论】:

  • ssize_t 是否定义为与size_t 相同的大小?如果不是,"%zd" 不是一个可移植的答案。建议printf("%jd\n", (intmax_t) b);
  • @chux %zd 是为ssize_t 规定的说明符。 以下整数转换对应于size_tssize_t 参数。
  • @chux 我在 printf 上引用了一个 linux 手册页。 The standard 也提到了它,虽然不是很清楚。
  • @cnicutar 再次感谢:Goggled "linux man page on printf" 并找到了它。注意:提供的链接(开放组基本规范)在 ssize_t 上没有任何内容。
  • @cnicutar 有点晚了,但我没有读到与 size_t 参数相对应的有符号整数类型,因为它明确引用了ssize_tssize_t 的定义永远不会以任何方式将其链接到 size_tentire definition is merely:“用于字节计数或错误指示。”和“ssize_t 类型应能够存储至少在 [-1, {SSIZE_MAX}] 范围内的值。”而the defintion of SSIZE_MAX 是循环的。
【解决方案2】:

首先你应该检查这两种类型的实际大小。 像下面的 sn-p 应该做的事情:

#include <stdio.h>
#include <unistd.h>

int main() {
  printf( "sizeof(  size_t ) = %d bytes\n",(int) sizeof( size_t) );
  printf( "sizeof( ssize_t ) = %d bytes\n",(int) sizeof( ssize_t) );
  return 0;
}

在这两种情况下,我得到 (64bit Linux, GCC v7.2) “8 bytes”,这与 long int 和 long long int 相同,即 CPU 原生整数的最大值。

当大小相同时(并且应该始终如此),size_t 的绝对值可以比ssize_t 的“绝对值大 2 倍”,而ssize_t 反过来可以有符号(正或负)值。

如果它们不同,则较大的会...更大,因此可以容纳更大的值。

但归根结底,ssize_tsize_t 是用于“谈论”大小、长度、内存量等的两种不同类型。

前者只是为值舍弃 1 位,以获得发出某种错误信号所需的符号。

最后,这两种类型不可互换,至少并非总是如此。 当大小可以超过 2^63 字节/项目时,差异就很明显了。 size_t 不会溢出,而 ssize_t 会溢出。

在“正常”情况下,您可以从一个投射到另一个。 对于我前面提到的情况,你不应该混合它们。

作为参考,strlen()malloc() 都使用 size_t,而 read()readv() 都使用 ssize_t。

所以,ssize_t 不是size_t 的签名版本,因为ssize_t 中的某些值无法映射到size_t(如-1),反之亦然。库函数要么使用一种类型,要么使用另一种类型。

那么,对于您的问题,您看到的两个数字相差 5 个单位,这正是您所期望的。当您看到unsigned long 时,您看到的是这两个变量的值。尝试将它们打印为 signed long (%ld),以便您仍然可以看到标志。

【讨论】:

  • 注意:Linux Programmer's Manual PRINTF(3) 确实有“z:以下整数转换对应于 size_tssize_t 参数”。
  • 尽管有(错误的)措辞,z 是长度修饰符,而不是转换说明符。恕我直言,我会根据它们的具体用法和含义使用%ld%lu。但这也取决于个人喜好。
  • “他们有不重叠的领域。” - false,它们重叠并且数字 0、1、2、3... 在重叠部分
  • 啊啊啊! :-D 当然。我的意思是函数要么使用size_t 要么使用ssize_t,从不同时使用tmk。
【解决方案3】:

...为什么我在ssize_t 上也得到了错误的结果??

使用

ssize_t b = -30;
printf("%jd\n", (intmax_t) b); 

使用匹配的说明符,对于否定的ssize_t,它不是 %zu 也肯定是"%zd"
How to use “zd” specifier with printf()?

ssize_t b = -30;
printf("%zu\n", b);  // problem.

ssize_t 没有 C 指定的打印说明符。 C 甚至没有指定ssize_t

C 的各种扩展都指定了ssize_t,在 Linux 的情况下,还指定了打印说明符。 Linux Programmer's Manual 确实有:

z:后面的整数转换对应size_tssize_t参数”,

printf("%zd\n", b);  // OK for that Linux

POSIX B.2.12 Data Types

ssize_t 这是size_t 的有符号模拟。措辞是这样的,实现可以选择使用更长的类型,或者只是使用size_t 基础类型的签名版本。


由于ssize_t 可能(不常见)比size_t 宽,使用"%zd" 可能会调用未定义的行为 (UB)。将自 C99 以来最广泛的标准签名类型转换为 intmax_t 并打印很简单。

printf("%jd\n", (intmax_t) b);  // OK for general use

【讨论】:

    【解决方案4】:

    溢出因为 size_t 是 UNSIGNED 当您尝试将 size_t 设置为 (-val) 你得到溢出并得到 SIZE_T_MAX - val

    例如: 尺寸_t val = -20; //val == 18446744073709551615 - 20;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-22
      • 2015-11-02
      • 2017-01-13
      • 2012-03-26
      • 1970-01-01
      • 2019-09-25
      • 2010-11-02
      • 2020-05-18
      相关资源
      最近更新 更多