簡體   English   中英

何時使用 std::size_t?

[英]When to use std::size_t?

我只是想知道我應該使用std::size_t循環和東西而不是int嗎? 例如:

#include <cstdint>

int main()
{
    for (std::size_t i = 0; i < 10; ++i) {
        // std::size_t OK here? Or should I use, say, unsigned int instead?
    }
}

一般來說,關於何時使用std::size_t的最佳實踐是什么?

一個好的經驗法則是,您需要在循環條件中與自然是std::size_t本身的東西進行比較。

std::size_t是任何sizeof表達式的類型,並且保證能夠在 C++ 中表達任何對象(包括任何數組)的最大大小。 通過擴展,它也保證對於任何數組索引都足夠大,因此它是數組索引循環的自然類型。

如果您只是計算一個數字,那么使用保存該數字的變量類型或intunsigned int (如果足夠大)可能更自然,因為這些應該是機器的自然大小。

size_tsizeof運算符的結果類型。

size_t用於對數組中的大小或索引進行建模的變量。 size_t傳達語義:您立即知道它表示以字節或索引為單位的大小,而不僅僅是另一個整數。

此外,使用size_t表示以字節為單位的大小有助於使代碼具有可移植性。

size_t類型旨在指定某物的大小,因此使用它是很自然的,例如,獲取字符串的長度,然后處理每個字符:

for (size_t i = 0, max = strlen (str); i < max; i++)
    doSomethingWith (str[i]);

當然,必須注意邊界條件,因為它是無符號類型。 頂端的邊界通常不是那么重要,因為最大值通常很大(盡管可能到達那里)。 大多數人只是將int用於此類事情,因為他們很少有結構或數組足夠大以超過該int的容量。

但請注意以下事項:

for (size_t i = strlen (str) - 1; i >= 0; i--)

由於無符號值的包裝行為,這將導致無限循環(盡管我已經看到編譯器對此提出警告)。 這也可以通過(稍微難以理解但至少不受包裝問題的影響)來緩解:

for (size_t i = strlen (str); i-- > 0; )

通過將減量轉換為繼續條件的檢查后副作用,這會檢查減量之前的值是否繼續,但仍使用循環內的減量值(這就是循環從len .. 1運行的原因而不是len-1 .. 0 )。

根據定義, size_tsizeof運算符的結果。 創建size_t是為了引用尺寸。

您做某事的次數(在您的示例中為 10 次)與大小無關,那么為什么要使用size_t呢? intunsigned int應該沒問題。

當然,您在循環中對i所做的操作也很重要。 例如,如果將它傳遞給采用unsigned int的函數,請選擇unsigned int

無論如何,我建議避免隱式類型轉換。 使所有類型轉換顯式。

簡短的回答:

幾乎從不

長答案:

每當您需要在 32 位系統上擁有大於 2gb 的 char 向量時。 在所有其他用例中,使用有符號類型比使用無符號類型更安全。

例子:

std::vector<A> data;
[...]
// calculate the index that should be used;
size_t i = calc_index(param1, param2);
// doing calculations close to the underflow of an integer is already dangerous

// do some bounds checking
if( i - 1 < 0 ) {
    // always false, because 0-1 on unsigned creates an underflow
    return LEFT_BORDER;
} else if( i >= data.size() - 1 ) {
    // if i already had an underflow, this becomes true
    return RIGHT_BORDER;
}

// now you have a bug that is very hard to track, because you never 
// get an exception or anything anymore, to detect that you actually 
// return the false border case.

return calc_something(data[i-1], data[i], data[i+1]);

size_t的有符號等效項是ptrdiff_t ,而不是int 但是在大多數情況下使用int仍然比 size_t 好得多。 ptrdiff_t在 32 和 64 位系統上很long

這意味着每當您與 std::containers 交互時,您總是必須在 size_t 之間進行轉換,這不是很漂亮。 但是在一個正在進行的本地會議上,c++ 的作者提到用無符號 size_t 設計 std::vector 是一個錯誤。

如果您的編譯器在從 ptrdiff_t 到 size_t 的隱式轉換時向您發出警告,您可以使用構造函數語法使其顯式:

calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);

如果只是想迭代一個集合,沒有邊界檢查,使用基於范圍的:

for(const auto& d : data) {
    [...]
}

這里是 Bjarne Stroustrup(C++ 作者)在生化時的一些話

對於某些人來說,STL 中的這種有符號/無符號設計錯誤是足夠的理由,不使用 std::vector,而是使用自己的實現。

size_t是一種非常易讀的方式來指定項目的大小維度 - 字符串的長度、指針占用的字節數等。它還可以跨平台移植 - 你會發現 64 位和 32 位在系統函數和size_t - unsigned int可能不會做的事情(例如,什么時候應該使用unsigned long

使用 std::size_t 對 C 樣式數組進行索引/計數。

對於 STL 容器,您將擁有(例如) vector<int>::size_type ,它應該用於索引和計數向量元素。

在實踐中,它們通常都是無符號整數,但不能保證,尤其是在使用自定義分配器時。

很快,大多數計算機將采用 64 位體系結構和 64 位操作系統:運行在包含數十億個元素的容器上運行的程序。 然后您必須使用size_t而不是int作為循環索引,否則您的索引將在 32 位和 64 位系統上的第 2^32:th 元素處環繞

為未來做准備!

size_t 由各種庫返回以指示該容器的大小非零。 當你回來時使用它:0

但是,在上面的示例中,在 size_t 上循環是一個潛在的錯誤。 考慮以下:

for (size_t i = thing.size(); i >= 0; --i) {
  // this will never terminate because size_t is a typedef for
  // unsigned int which can not be negative by definition
  // therefore i will always be >= 0
  printf("the never ending story. la la la la");
}

使用無符號整數有可能產生這些類型的微妙問題。 因此恕我直言,我更喜歡僅在與需要它的容器/類型交互時才使用 size_t 。

使用 size_t 時請注意以下表達式

size_t i = containner.find("mytoken");
size_t x = 99;
if (i-x>-1 && i+x < containner.size()) {
    cout << containner[i-x] << " " << containner[i+x] << endl;
}

無論您對 x 有什么值,您都將在 if 表達式中得到錯誤。 我花了幾天的時間才意識到這一點(代碼太簡單了,我沒有做單元測試),盡管只需要幾分鍾就可以找出問題的根源。 不確定進行強制轉換或使用零會更好。

if ((int)(i-x) > -1 or (i-x) >= 0)

兩種方式都應該有效。 這是我的測試運行

size_t i = 5;
cerr << "i-7=" << i-7 << " (int)(i-7)=" << (int)(i-7) << endl;

輸出:i-7=18446744073709551614 (int)(i-7)=-2

我想聽聽別人的意見。

通常最好不要在循環中使用 size_t。 例如,

vector<int> a = {1,2,3,4};
for (size_t i=0; i<a.size(); i++) {
    std::cout << a[i] << std::endl;
}
size_t n = a.size();
for (size_t i=n-1; i>=0; i--) {
    std::cout << a[i] << std::endl;
}

第一個循環沒問題。 但是對於第二個循環:
當 i=0 時, i-- 的結果將是 ULLONG_MAX(假設 size_t = unsigned long long),這不是您在循環中想要的。
此外,如果 a 為空,則 n=0 且 n-1=ULLONG_MAX 也不好。

size_t是一種無符號類型,可以為您的體系結構保存最大整數值,因此可以防止由於符號(有符號 int 0x7FFFFFFF遞增 1 將給您 -1)或短大小(無符號短整數 0xFFFF 遞增 1 將給你0)。

主要用於數組索引/循環/地址運算等。 memset()之類的函數只接受size_t ,因為理論上你可能有一塊大小為2^32-1的內存(在 32 位平台上)。

對於這樣簡單的循環,不要打擾,只需使用 int。

我一直在努力理解什么以及何時使用它。 但是 size_t 只是一個無符號整數數據類型,它在各種頭文件中定義,例如<stddef.h>, <stdio.h>, <stdlib.h>, <string.h>, <time.h>, <wchar.h>

它用於以字節為單位表示對象的大小,因此它被 sizeof 運算符用作返回類型。 最大允許大小取決於編譯器; 如果編譯器是 32 位,那么它只是 unsigned int 的 typedef(別名),但如果編譯器是 64 位,那么它將是 unsigned long long 的 typedef。 size_t 數據類型永遠不會是負數(不包括 ssize_t),因此許多 C 庫函數,如malloc, memcpy and strlen將它們的參數和返回類型聲明為size_t

/ Declaration of various standard library functions.
  
// Here argument of 'n' refers to maximum blocks that can be
// allocated which is guaranteed to be non-negative.
void *malloc(size_t n);
  
// While copying 'n' bytes from 's2' to 's1'
// n must be non-negative integer.
void *memcpy(void *s1, void const *s2, size_t n);
  
// the size of any string or `std::vector<char> st;` will always be at least 0.
size_t strlen(char const *s);

size_t或任何無符號類型可能被視為循環變量,因為循環變量通常大於或等於 0。

size_t 是無符號整數類型,可以表示系統上的最大整數。 僅當您需要非常大的數組、矩陣等時才使用它。

一些函數返回一個 size_t,如果您嘗試進行比較,您的編譯器會警告您。

通過使用適當的有符號/無符號數據類型或簡單地進行類型轉換以進行快速破解來避免這種情況。

size_t 是無符號整數。 所以每當你想要 unsigned int 時,你都可以使用它。

當我想指定數組的大小時使用它,計數器等...

void * operator new (size_t size); is a good use of it.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM