# C++ 中倒序存取陣列的型別問題

## 前言

問題起源於這個迴圈，有時，我們會需要倒著存取陣列的元素。

```cpp
std::vector<int> xs(10);

for (size_t i = 9; i >= 0; --i) {
	// ... xs[i]
}
```

這一部份有個問題，就是這會是無窮迴圈。

一個保留 `size_t` 型別的方式，是這樣寫：

```cpp
for (size_t i = 9 + 1; i--;) {
	// ... xs[i]
}
```

這種寫法雖然不會對正確性產生影響，但是可能會對可讀性和可維護性造成一定程度的負面影響。

有沒有其他方式？在這裏，我們不考慮直接使用 `int64_t` 之類的，我們希望有一個型別在語意上可以符合我們需求的地方。

## `size_t`

size\_t 會要是 `sizeof` 的回傳型別，大小**通常**就會是 cpu 的位元數，例如 64 位元的 cpu 就是 64 bits ，因為他需要代表一個物件的大小。

[STL 文件](https://en.cppreference.com/w/cpp/types/size_t)明確的指出，它必然會是無號整數型別，也就是說，**上面 for 迴圈** `--i` **的部分會讓 i 從 0 跳到 size\_t 的最大值。**

## `ssize_t`

在POSIX標準中，ssize\_t是一種整數型別，用於表示一個有符號的整數，其大小足以容納某些操作的結果，例如讀寫一個文件。該型別在POSIX標準中的定義如下：

```cpp
typedef long int ssize_t;
```

 根據 [Open Group 的文件](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html) 裡面，`ssize_t` 主要用於

1. byte 數量
    
2. error 意思的回傳值
    

> **ssize\_t**
> 
> Used for a count of bytes or an error indication

距離我們需要的語意上來說，仍然有一點點差距。

## `ptrdiff_t`

在 c++ 20 裡面，有提供了一個叫做 `std::ssize` 的函數。他會讓一個陣列的大小（或 .size()）以「有號」的方式回傳：

```cpp
template< class T, std::ptrdiff_t N >
constexpr std::ptrdiff_t ssize( const T (&array)[N] ) noexcept;
```

```cpp
template< class C >
constexpr auto ssize( const C& c )
    -> std::common_type_t<std::ptrdiff_t,
                          std::make_signed_t<decltype(c.size())>>;
```

這時我們可以注意到，它其實不是回傳 `ssize_t` ，而是另一個叫做 `ptrdiff_t` 的東西。

在 [STL 文件](https://en.cppreference.com/w/cpp/types/ptrdiff_t)裡面，它會是「兩個指針的差」。

> `std::ptrdiff_t` is the signed integer type of the result of subtracting two pointers.

距離我們需要的語意上來說，仍然有一點點差距。

## 一些結論

就使用 `ssize_t` 或 `ptrdiff_t` 上來說，或多或少都要確認實際運作範圍以及可移植性（POSIX），語意上仍然有點不一。不過 `ptrdiff_t` 比 `ssize_t` 比較好的地方在於他是 STL 就有的東西。要選擇把 `size_t` 換掉的話，我偏向 `ptrdiff_t` 。
