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

·

1 min read

前言

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

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

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

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

一個保留 size_t 型別的方式,是這樣寫:

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

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

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

size_t

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

STL 文件明確的指出,它必然會是無號整數型別,也就是說,上面 for 迴圈 --i 的部分會讓 i 從 0 跳到 size_t 的最大值。

ssize_t

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

typedef long int ssize_t;

根據 Open Group 的文件 裡面,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())以「有號」的方式回傳:

template< class T, std::ptrdiff_t N >
constexpr std::ptrdiff_t ssize( const T (&array)[N] ) noexcept;
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 文件裡面,它會是「兩個指針的差」。

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

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

一些結論

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