如何正确使用 size_t 和其他整数类型一起使用?

How to correctly use size_t and other integer types together?

对于 C 中的一个典型循环,可以这样写:

for (unsigned int i = 0; i < 10; i++) {}

但是,有些人建议改用 size_t

for (size_t i = 0; i < 10; i++) {}

因为当索引超过 UINT_MAXunsigned int 可能会失败。


虽然我明白这一点,但有时我需要将size_t与其他定长类型(例如uint32_t)一起使用,或者将多个size_t写入网络。如果处理不当,这些操作很容易出错。我认为这同样适用于其他整数类型,例如 ssize_tsocklen_t

处理这些不同整数和大小类型的混合并在必要时安全地转换它们的正确方法是什么?

编辑。关于类型长度的另一个问题是在 printf 语句中打印它的值。 size_t 有自己的格式说明符 z。并非所有整数类型都有这种奢侈。如果 printf 在变量上接受 typeof 运算符可能会更容易,但不幸的是它不接受。

规则摘要如下:

http://en.cppreference.com/w/c/language/types

总而言之,固定长度类型是唯一可以而且应该依赖其大小的类型。类型之间存在相对限制(例如 long 永远不会比 int 短),并且整数类型具有强制性的 minumum 长度(例如 int 保证至少为 16 位).

因此对于网络通信,您需要固定长度类型(以网络字节顺序进行传输和接收)或编码协议(例如 google 协议缓冲区)。

您可以将 target/source 整数类型的范围与 size 类型的范围进行比较。这些数字应自动提升为足够大的类型以进行比较(假设不涉及负数)。

size_t 的最大值在 stdint.h (C99 起): SIZE_MAX 中定义。固定宽度整数类型的限制也可以在 header 中找到,例如 UINT64_MAX.

整数类型的范围在limits.h中定义:例如ULONG_MAX

供参考:

What is the right way to handle a mix of these different integer and size types and convert them safely when necessary?

是的,这是具有挑战性和广泛性的。
下面是一些有用但还远未完成的公理。有些有限制:

  1. 对数组 [] 索引使用 size_t。 (如果代码知道索引范围很小,可以使用更小的类型。)

  2. 注意 size_t

    低于零
    size_t i,a,b;
    // for (i = a; i-1 < b; i++) { // `i-1` may wrap
    for (i = a; i < b + 1; i++) { // `b+1u` may wrap but far less likely.
    
  3. 当两种类型具有相同的符号性或已知有符号类型比无符号类型宽时,只需编写代码而不使用强制转换

    if (sg <  un)
    
  4. 否则单独处理潜在的负值

    if (sg < 0 || sg < un)
    
  5. 通常要安全转换,测试范围:

    // typea --> typeb
    if (vara < typeb_MIN || vara > typeb_MAX) Handle_Error()
    else varb = vara;
    
  6. 避免将 typea 转换为 typeb,除非代码 必须 转换为 typeb。而是乘以 ((typeb)1)。这将防止截断(另见前面)

    (typeb) vara // avoid
    ((typeb) 1) * vara
    
  7. 当打印和代码在类型 range/width 上不清楚时,使用最大宽度类型

    ssize_t ssz = foo();
    printf("%jd\n", ((intmax_t)1) * ssz);
    printf("%lld\n", 1LL * ssz);
    
  8. 计算二维数组字节大小时,先乘以size_t类型

    p = malloc(width * height * sizeof *p);   // potential overflow in width * height
    p = malloc(sizeof *p * width * height);   
    

注意:一些 C 整数类型

  • 签名:signed charshortintlong long long intmax_t
  • 无符号:unsigned charunsigned shortunsignedunsigned long unsigned long long uintmax_t
  • 另外 char 是有符号或无符号的。
  • 特殊:(无符号)size_t(有符号)ptrdiff_t
  • 精确宽度(可选)示例:int8_tuint32_t 以及令人惊讶的示例 uint24_t
  • 最小宽度(至少 8 种类型)int_least8_t,...,uint_least64_t
  • 快速宽度(至少 8 种)int_fast8_t,...,uint_fast64_t
  • 还有 wchar_twint_tsig_atomic_tmax_align_t、...、...、...等
  • 位域。

其中许多类型只是 typedef 其他类型的。其他像 intlong 可能具有相同的范围但保持其独特的类型。