奇怪的typedef用于std::ptrdiff_t和std::size_t

5

std::size_t是一种无符号类型,可以存储任何类型的理论上可能存在的最大对象的大小,而std::ptrdiff_t是一种有符号类型,可以保存两个指针之间的差值(这另一种说法是它应该是C++中数组索引的标准整数)。C++标准库已经决定使用std::size_t进行数组索引,但通常会声明std::ptrdiff_t更好。哪一个更好是一个长期的争论,我不想在这里进入,但我一直认为第二个是第一个的无符号版本。在macOS上运行此程序会得到:

#include <cstddef>
#include <cstdio>

void f(int n) { std::printf("int"); };
void f(long n) { std::printf("long"); };
void f(long long n) { std::printf("long long"); };
void f(unsigned int n) { std::printf("unsigned int"); };
void f(unsigned long n) { std::printf("unsigned long"); };
void f(unsigned long long n) { std::printf("unsigned long long"); };

int main() {
  const std::ptrdiff_t n_ptrdiff = 0;
  const std::size_t n_size = 0;

  std::printf("std::ptrdiff_t is an alias for ");
  f(n_ptrdiff);
  std::printf("\n");

  std::printf("std::size_t is an alias for ");
  f(n_size);
  std::printf("\n");

  return 0;
}

在 64 位平台上但编译为 32 位:
clang++ -m32 -std=c++11 type.cpp -o type-32-clang

给我以下结果:

std::ptrdiff_t is an alias for int
std::size_t is an alias for unsigned long

在这种情况下,即使它们具有相同的存储大小(4字节),std::size_t并不是std::ptrdiff_t的无符号版本。尽管它符合标准,但对我来说似乎非常奇怪。
有人知道选择这种方式的理由吗?

“std::size_t”类型是一个无符号类型,它足够大以容纳任何指针。什么意思? - user9212993
1
那将是可选的 std::uintptr_t - StoryTeller - Unslander Monica
1
在32位平台上,一个long的大小可以与一个int相同。检查所有这些类型的sizeof - Bob__
请查看:http://en.cppreference.com/w/cpp/types/ptrdiff_t,其中显示了`typedef /*implementation-defined*/ ptrdiff_t;`。现在您的代码片段可以是:https://ideone.com/rJlmVp - Bob__
@StoryTeller:我修改了我的问题,这样人们就不能指出我的问题中的错误并忘记回答问题。但是我不是那个使它们无效的人。 - InsideLoop
显示剩余5条评论
1个回答

9
TL;DR: 为了与其他系统兼容,这样做是为了与其他系统兼容,以此类推。
clang通常会使用signed longunsigned long作为ptrdiff_tsize_t,但是commit "fix some differences between apple gcc and clang on darwin/x86-32"ptrdiff_t更改为signed int以与gcc兼容。如果在基本的typedef上存在分歧,如这些,那么就无法让clang和gcc使用相同的C++库。

gcc使用unsigned long作为size_t,但使用signed int作为ptrdiff_t,因为这是苹果公司的贡献:commit "Add Darwin (Mac OS X kernel) native support."gcc/config/darwin.h中有所体现:

/* Target definitions for Darwin (Mac OS X) systems.
   Copyright (C) 1989, 1990, 1991, 1992, 1993, 2000, 2001
   Free Software Foundation, Inc.
   Contributed by Apple Computer Inc.

[...]

/* The string value for __SIZE_TYPE__.  */

#ifndef SIZE_TYPE
#define SIZE_TYPE "long unsigned int"
#endif

/* Type used for ptrdiff_t, as a string used in a declaration.  */

#undef  PTRDIFF_TYPE
#define PTRDIFF_TYPE "int" 
没有具体的原因被提到,但是这个文件不特定于任何处理器类型,适用于所有的Darwin系统,并且这个提交还涉及到gcc/config/rs6000/rs6000.h,这使得它很可能是为了与那个兼容,将其带回到1995年commit "Add preliminary V.4 and eABI support."
/* Type used for ptrdiff_t, as a string used in a declaration.  */
#undef  PTRDIFF_TYPE
#define PTRDIFF_TYPE "int"

由于这并没有重新定义SIZE_TYPE,因此其保持默认值为"unsigned long"

对于此平台,gcc不是系统编译器,因此这可能是为了与IBM的编译器兼容。这似乎得到了gcc/config/rs6000/aix43.h的支持:

/* AIX 4.3 typedefs ptrdiff_t as "long" while earlier releases used "int".  */

#undef PTRDIFF_TYPE
#define PTRDIFF_TYPE "long int"

往更早的历史可能不会发生,因为AIX的历史不是公开的。


1
非常感谢您提供这么详细的答案。 - InsideLoop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接