将 size_t 转换为 unsigned long int 是否安全?
Is it safe to cast size_t to unsigned long int?
我需要一种可移植的方式来打印类型为 size_t
的变量 n 的值。因为我使用 ANSI C89,所以我不能使用 z
长度修饰符。我目前的做法是将值转换为 long unsigned int
:
printf("%lu\n", (long unsigned int) n);
假设 size_t
定义为 unsigned int
或 long unsigned int
我看不出这会失败。施法安全吗?
在C89中,size_t
被定义为无符号整数类型。与未来的标准不同,C89定义了无符号整数类型列表如下:
- 无符号字符
- 无符号短
- 无符号整数
- 无符号长整数
因此,C89 中的 size_t
永远不会大于 unsigned long
,因此转换始终是安全的 - 因为它不会导致任何未定义的行为,而且它总是足够大以容纳整个价值。
值得注意; C89 标准规定:
"A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any strictly conforming program"
这意味着没有扩展可以改变这种行为——同时仍然符合 C89 标准,因为无符号整数类型已被特别列出,因此不能改变。
在未来的标准中,这不是保证,虽然您不会得到未定义的行为 - 您可能会丢失 unsigned long
小于 size_t
的数据,这意味着您会显示不正确的数据你的用户。在这种情况下,我会犹豫是否将其标记为 "safe".
作为重要的附加说明;此答案指的是符合 C89 标准的编译器。在上述方面,您的 C89 编译器可能是 "less than compliant",在这种情况下 - 将行为视为类似于 C99 或更新版本的行为,您不会看到未定义的行为,但如果 size_t
大于 unsigned long
。需要明确的是,这不符合 C89 标准。
除此之外,我对标准(1.7 合规性)的解释是,虽然它声明扩展不得改变 "strictly conforming program" 的行为,因此不能改变 size_t
最多必须 unsigned long
的事实而不遵守;它不会改变 这样的扩展确实存在 的事实。例如 GNU GCC 确实提供了添加 unsigned long long
的扩展。在我看来,这是不合规的,但现实是你必须准备好处理这样的事情 - 虽然标准说你正在做的事情是完全安全的,但你必须为潜在的数据做好准备使用不兼容的编译器或扩展的损失。
有关此主题的先前讨论,请参阅此处:
size_t n = foo();
printf("%lu\n", (long unsigned int) n);
Provided that size_t
is defined as either unsigned int
or long unsigned int
... Is the cast safe?
是的,C89、C99、C11 上的转换是安全的,没有未定义的行为,也没有信息丢失。
但是 如果条件不成立会怎样?
假设size_t
的范围会在unsigned long
的范围内是很合理的。添加编译时测试:ref
#include <limits.h>
#if defined(__STDC__)
#if defined(__STDC_VERSION__)
#if (__STDC_VERSION__ >= 199901L)
#include <stdint.h>
#if SIZE_MAX > ULONG_MAX
#error Re-work printf size code
#endif
#endif
#endif
#endif
关键是当代码有依赖时 - 添加一个测试。即使它在今天和历史上所有已知的机器上都可以接受,但未来还是未知数。
C,在今天,以其巨大的灵活性确实允许 SIZE_MAX > ULONG_MAX
,但它肯定是很少见的。国际海事组织,SIZE_MAX > ULONG_MAX
是 beyond the pale。
这样的测试不时很常见,虽然可能,但编写 super 可移植代码根本不可行或预算不多。
#include <limits.h>
#if CHAR_BIT != 8 && CHAR_BIT != 16 && CHAR_BIT != 32 && CHAR_BIT != 64
#error Code depends on char size as a common power of 2.
#endif
I need a portable way to print the value of a variable n of type size_t.
然而,为了实现 OP 的顶级目标,可以编写一个简单的可移植辅助函数。
// This approach works with any unsigned type
void print_size_t(size_t n) {
if (n >= 10) print_size_t(n/10);
putchar((int) (n%10) + '0');
}
为了避免递归,稍微长一点的函数:
#include <limits.h>
void print_size_t(size_t n) {
char buf[sizeof n * CHAR_BIT / 3 + 2]; // 1/3 is more than log2(10)
char *p = &buf[sizeof buf - 1]; // Start at end of buf[]
*p = '[=14=]';
do {
p--;
*p = (char) (n%10 + '0');
n /= 10;
} while (n); // Use a do {} while so print_size_t(0) prints something
fputs(p, stdout);
}
我需要一种可移植的方式来打印类型为 size_t
的变量 n 的值。因为我使用 ANSI C89,所以我不能使用 z
长度修饰符。我目前的做法是将值转换为 long unsigned int
:
printf("%lu\n", (long unsigned int) n);
假设 size_t
定义为 unsigned int
或 long unsigned int
我看不出这会失败。施法安全吗?
在C89中,size_t
被定义为无符号整数类型。与未来的标准不同,C89定义了无符号整数类型列表如下:
- 无符号字符
- 无符号短
- 无符号整数
- 无符号长整数
因此,C89 中的 size_t
永远不会大于 unsigned long
,因此转换始终是安全的 - 因为它不会导致任何未定义的行为,而且它总是足够大以容纳整个价值。
值得注意; C89 标准规定: "A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any strictly conforming program" 这意味着没有扩展可以改变这种行为——同时仍然符合 C89 标准,因为无符号整数类型已被特别列出,因此不能改变。
在未来的标准中,这不是保证,虽然您不会得到未定义的行为 - 您可能会丢失 unsigned long
小于 size_t
的数据,这意味着您会显示不正确的数据你的用户。在这种情况下,我会犹豫是否将其标记为 "safe".
作为重要的附加说明;此答案指的是符合 C89 标准的编译器。在上述方面,您的 C89 编译器可能是 "less than compliant",在这种情况下 - 将行为视为类似于 C99 或更新版本的行为,您不会看到未定义的行为,但如果 size_t
大于 unsigned long
。需要明确的是,这不符合 C89 标准。
除此之外,我对标准(1.7 合规性)的解释是,虽然它声明扩展不得改变 "strictly conforming program" 的行为,因此不能改变 size_t
最多必须 unsigned long
的事实而不遵守;它不会改变 这样的扩展确实存在 的事实。例如 GNU GCC 确实提供了添加 unsigned long long
的扩展。在我看来,这是不合规的,但现实是你必须准备好处理这样的事情 - 虽然标准说你正在做的事情是完全安全的,但你必须为潜在的数据做好准备使用不兼容的编译器或扩展的损失。
有关此主题的先前讨论,请参阅此处:
size_t n = foo();
printf("%lu\n", (long unsigned int) n);
Provided that
size_t
is defined as eitherunsigned int
orlong unsigned int
... Is the cast safe?
是的,C89、C99、C11 上的转换是安全的,没有未定义的行为,也没有信息丢失。
但是 如果条件不成立会怎样?
假设size_t
的范围会在unsigned long
的范围内是很合理的。添加编译时测试:ref
#include <limits.h>
#if defined(__STDC__)
#if defined(__STDC_VERSION__)
#if (__STDC_VERSION__ >= 199901L)
#include <stdint.h>
#if SIZE_MAX > ULONG_MAX
#error Re-work printf size code
#endif
#endif
#endif
#endif
关键是当代码有依赖时 - 添加一个测试。即使它在今天和历史上所有已知的机器上都可以接受,但未来还是未知数。
C,在今天,以其巨大的灵活性确实允许 SIZE_MAX > ULONG_MAX
,但它肯定是很少见的。国际海事组织,SIZE_MAX > ULONG_MAX
是 beyond the pale。
这样的测试不时很常见,虽然可能,但编写 super 可移植代码根本不可行或预算不多。
#include <limits.h>
#if CHAR_BIT != 8 && CHAR_BIT != 16 && CHAR_BIT != 32 && CHAR_BIT != 64
#error Code depends on char size as a common power of 2.
#endif
I need a portable way to print the value of a variable n of type size_t.
然而,为了实现 OP 的顶级目标,可以编写一个简单的可移植辅助函数。
// This approach works with any unsigned type
void print_size_t(size_t n) {
if (n >= 10) print_size_t(n/10);
putchar((int) (n%10) + '0');
}
为了避免递归,稍微长一点的函数:
#include <limits.h>
void print_size_t(size_t n) {
char buf[sizeof n * CHAR_BIT / 3 + 2]; // 1/3 is more than log2(10)
char *p = &buf[sizeof buf - 1]; // Start at end of buf[]
*p = '[=14=]';
do {
p--;
*p = (char) (n%10 + '0');
n /= 10;
} while (n); // Use a do {} while so print_size_t(0) prints something
fputs(p, stdout);
}