C++ 中倒序存取陣列的型別問題
前言
問題起源於這個迴圈,有時,我們會需要倒著存取陣列的元素。
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
主要用於
byte 數量
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_t
或 ptrdiff_t
上來說,或多或少都要確認實際運作範圍以及可移植性(POSIX),語意上仍然有點不一。不過 ptrdiff_t
比 ssize_t
比較好的地方在於他是 STL 就有的東西。要選擇把 size_t
換掉的話,我偏向 ptrdiff_t
。