std :: size_tを使用する場合


201

std::size_t代わりにforループなどを使用する必要があるの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かに関するベストプラクティスは何ですか?

回答:


186

目安としては、ループ状態で自然に発生するものと比較する必要があるすべてのものを std::size_tそれ自体。

std::size_t のタイプです sizeof式、C ++で任意のオブジェクト(任意の配列を含む)の最大サイズを表現できることが保証されています。拡張により、配列のインデックスに対して十分な大きさであることが保証されているため、配列のインデックスによるループの自然な型です。

数まで数える場合は、その数を保持する変数のタイプ、intまたはunsigned int(十分な大きさの場合)またはマシンの自然なサイズであるため、またはのいずれかを使用するほうが自然な場合があります。


41
セキュリティバグにつながる可能性があるときに使用しないこと言及する価値がありsize_tます。
BlueRaja-Danny Pflughoeft、

5
intが「自然」であるだけでなく、符号付きタイプと符号なしタイプを混在させると、セキュリティバグが発生する可能性もあります。符号なしインデックスは扱いが面倒であり、カスタムベクトルクラスを使用する十分な理由です。
Jo So

2
@JoSo ssize_t符号付きの値もあります。
EntangledLoops

70

size_tsizeof演算子の結果タイプです。

size_t配列のサイズまたはインデックスをモデル化する変数に使用します。size_tセマンティクスを伝達します。これは、単に別の整数ではなく、バイト単位のサイズまたはインデックスを表すことがすぐにわかります。

また、size_tサイズをバイト単位で表すのに使用すると、コードの移植性が高まります。


32

この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)。


14
ところで、strlenループの各繰り返しを呼び出すのは悪い習慣です。::)あなたはこのような何か行うことができますfor (size_t i = 0, len = strlen(str); i < len; i++) ...
musiphil

1
符号付きの型であっても、境界条件に注意する必要があります。符号付き整数のオーバーフローは未定義の動作であるため、さらに注意が必要です。
エイドリアン・マッカーシー2014年

2
:正しくカウント・ダウンすることは、以下の(悪名高い)方法で行うことができますfor (size_t i = strlen (str); i --> 0;)
ジョーだから、

1
@JoSo、-->"goes to"演算子(stackoverflow.com/questions/1642028/…を参照)の導入が好きかどうかはわかりませんが、それは実際にはかなり巧妙なトリックです。あなたの提案を答えに取り入れました。
paxdiablo 2016

if (i == 0) break;forループの最後で簡単にできますか(例:for (size_t i = strlen(str) - 1; ; --i)。(私はあなたのほうが好きですが、これでもうまくいくかどうか疑問に思います)
RastaJedi

13

定義size_tにより、はsizeof演算子の結果です。size_tサイズを参照するために作成されました。

何かをする回数(例では10)はサイズに関するものではないので、なぜ使用するのsize_tですか?intまたはunsigned int、大丈夫です。

もちろんi、ループ内で何をするかにも関係があります。unsigned intたとえばを受け取る関数に渡す場合は、を選択しunsigned intます。

いずれにせよ、暗黙的な型変換を避けることをお勧めします。すべての型変換を明示的にします。


10

size_tアイテムのサイズ寸法を指定するための非常に読みやすい方法です-文字列の長さ、ポインタバイトの量がかかる、などこれは、プラットフォーム間でも移植だ-あなたは、64ビットを見つけ、システム機能とうまく両方の振る舞いを32ビットとしますsize_t- unsigned int実行できない可能性があるもの(たとえば、いつ使用するべきか)unsigned long


9

短い答え:

ほとんどは決してない

長い答え:

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_tptrdiff_t、ではなくintです。しかしint、ほとんどの場合、size_tよりも使用する方がはるかに優れています。ptrdiff_tですlong 32ビットおよび64ビットシステムで。

これは、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 ++作成者)からのいくつかの単語

一部の人々にとって、STD :: vectorを使用せずに、独自の実装を使用するには、STLでのこの署名付き/署名なしの設計エラーが十分な理由です。


1
どこから来たのかはわかりますが、書くのは変だと思いますfor(int i = 0; i < get_size_of_stuff(); i++)。さて、確かに、あなたは生のループをたくさんしたくないかもしれませんが-さあ、あなたもそれらを使います。
アインポクルム

私が生のループを使用する唯一の理由は、c ++アルゴリズムライブラリの設計がかなり悪いためです。Scalaのような、コレクションを操作するためのはるかに優れた、より進化したライブラリーを持つ言語があります。次に、生のループの使用例がほとんどなくなります。新しい、より優れたSTLでc ++を改善するためのアプローチもありますが、これが今後10年以内に起こるとは思えません。
Arne

1
符号なしのi = 0を取得します。assert(i-1、MAX_INT); しかし、なぜ「私がすでにアンダーフローを起こしていたとしても、これは真になる」とあなたが言う理由がわかりません。結果は、表現可能な最大の整数のサイズを法とする結果です。したがって、i == 0の場合、i--はMAX_INTになり、i ++は再び0になります。
マブラハム2016

@mabraham私は注意深く調べました、そしてあなたは正しいです、私のコードは問題を示すのに最適ではありません。通常、これはとx + 1 < y同等ですがx < y - 1、非固有整数ではありません。同等であると想定されているものが変換されるときに、バグが簡単に発生する可能性があります。
Arne

8

Cスタイルの配列のインデックス付け/カウントにはstd :: size_tを使用します。

STLコンテナーの場合は(たとえば)がありvector<int>::size_type、これはベクター要素のインデックス作成とカウントに使用する必要があります。

実際には、通常両方ともunsigned intですが、特にカスタムアロケーターを使用する場合は保証されません。


2
Linuxのgccでは、std::size_t通常(4バイト)unsigned longではなくunisgned int(64ビットシステムでは8 バイト)。
rafak 2009

5
Cスタイルの配列はsize_t、インデックスが負になる可能性があるため、インデックスが付けられません。size_tただし、負になりたくない場合は、このような配列の独自のインスタンスを使用できます。
Johannes Schaub-litb 2009

u64での比較はu32での比較と同じくらい高速ですか?ループセンチネルとしてu8とu16を使用すると、パフォーマンスが大幅に低下する場合がありますが、64秒でIntelが一緒に動作しているかどうかはわかりません。
Crashworks、

2
Cスタイルの配列のインデックス付けは+、ポインターに演算子を使用することと同等であるptrdiff_tため、インデックスに使用するのはそれだと思われます。
Pavel Minaev 2009

8
vector<T>::size_type(他のすべてのコンテナの同上)、効果的であることが保証されているため、実際にはかなり無用だsize_t-それがにtypedefされますAllocator::size_type特に、 - 、およびコンテナに関して、その上の制限については、20.1.5 / 4参照size_type必見ことsize_t、およびdifference_typeなければなりませんptrdiff_t。もちろん、デフォルトstd::allocator<T>はこれらの要件を満たしています。ですから、短い方size_tを使用し、残りの部分は気にしないでください:)
Pavel Minaev

7

間もなく、ほとんどのコンピュータは64ビットOSの64ビットアーキテクチャになり、何十億もの要素のコンテナで動作するプログラムを実行します。次に、ループインデックスの代わりに使用する必要あります。そうしないと、32ビットシステムと64ビットシステムの両方で、インデックスが2 ^ 32:番目の要素で折り返されます。size_tint

未来に備えよう!


あなたの議論は、long intでなくが必要であるという意味でのみ有効intです。場合はsize_t、64ビットOSに関連しているそれは、32ビットOS上で同じように関連しました。
einpoklum 2016年

4

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式ではfalseになります。これを実現するのに数日かかりました(コードは非常にシンプルなので、単体テストを実行しませんでした)が、問題の原因を特定するには数分しかかかりません。キャストするか、ゼロを使用する方が良いかわかりません。

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

他人のコメントをお願いします。


2
(int)(i - 7)は、int後にキャストさint(i) - 7れるアンダーフローですが、最初にに変換してiからint減算するため、アンダーフローではないことに注意してください7。さらに、私はあなたの例を混乱させました。
hochl 2018

私のポイントは、あなたが減算を行うとき、intは通常より安全であるということです。
Kemin Zhou、

4

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を使うことを好む。


Everoneはこのバグを気にせずにループでsize_tを使用しているようです。私はこれを難しい方法で学びました
Pranjal Gupta

-2

size_t は、ご使用のアーキテクチャーの最大整数値を保持できる符号なしタイプであるため、符号(signed int)による整数オーバーフローから保護されています 0x7FFFFFFFを1ずつインクリメントすると-1が返されます)または短いサイズ(unsigned short int 0xFFFFを1ずつインクリメントするとます) 0)。

主に配列のインデックス付け/ループ/アドレス演算などに使用されます。のような関数は、理論的にはサイズのメモリブロックがあるため、memset()受け入れるsize_tだけです。2^32-1(32ビットプラットフォーム)。

そのような単純なループの場合、気にせず、intだけを使用します。


-3

size_tは、システムで最大の整数を表すことができる符号なし整数型です。非常に大きな配列、行列などが必要な場合にのみ使用してください。

一部の関数はsize_tを返し、比較を行おうとするとコンパイラーから警告が出されます。

適切な署名済み/未署名のデータ型を使用するか、高速ハックのために単純に型キャストすることで、これを回避します。


4
バグやセキュリティホールを回避したい場合にのみ使用してください。
Craig McQueen

2
実際には、システムで最大の整数を表すことができない場合があります。
エイドリアン・マッカーシー2014年

-4

size_tは符号なし整数です。そのため、unsigned intが必要なときはいつでも使用できます。

配列のサイズを指定したいときに使用します...

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

10
実際には、必ずしもunsigned intと同じではありません。これは、ある符号なし、それが大きくなることがあります(または私はこれが本当である任意のプラットフォームを知りませんが小さいと思います)int型より。
Todd Gamblin、

たとえば、64ビットマシンでsize_tは、符号なし64ビット整数になる可能性がありますが、32ビットマシンでは、32ビットの符号なし整数のみになります。
HerpDerpington
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.