Std :: size_t का उपयोग कब करें?


201

मैं सोच रहा हूं कि क्या मुझे 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?

जवाबों:


185

अंगूठे का एक अच्छा नियम ऐसी किसी भी चीज़ के लिए है जिसकी आपको लूप स्थिति में किसी ऐसी चीज़ से तुलना करने की ज़रूरत है जो स्वाभाविक रूप से std::size_tस्वयं है।

std::size_tकिसी भी sizeofअभिव्यक्ति का प्रकार है और जैसा कि सी ++ में किसी भी वस्तु (किसी भी सरणी सहित) के अधिकतम आकार को व्यक्त करने में सक्षम होने की गारंटी है। विस्तार से यह किसी भी सरणी इंडेक्स के लिए पर्याप्त बड़ा होने की गारंटी है, इसलिए यह एक सरणी पर इंडेक्स द्वारा लूप के लिए एक प्राकृतिक प्रकार है।

यदि आप केवल एक संख्या तक गिनती कर रहे हैं, तो या तो उस चर के प्रकार का उपयोग करना अधिक स्वाभाविक हो सकता है जो उस संख्या को रखता है या intया unsigned int(यदि काफी बड़ा है) क्योंकि ये मशीन के लिए एक प्राकृतिक आकार होना चाहिए।


40
यह ध्यान देने योग्य है कि जब आपको सुरक्षा बग का नेतृत्व करना चाहिए तो इसका उपयोग नहींsize_t करना चाहिए ।
ब्लूराजा - डैनी पफ्लुघोफ्ट

5
न केवल int "प्राकृतिक" है, लेकिन हस्ताक्षर किए गए और अहस्ताक्षरित प्रकार के मिश्रण से सुरक्षा कीड़े भी हो सकते हैं। अहस्ताक्षरित सूचकांकों को संभालने के लिए एक दर्द है और एक कस्टम वेक्टर वर्ग का उपयोग करने का एक अच्छा कारण है।
जो सो

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++) ...
पेशीफिल

1
यहां तक ​​कि अगर यह एक हस्ताक्षरित प्रकार था, तो आपको सीमा स्थितियों के लिए बाहर देखना होगा, शायद इससे भी अधिक हस्ताक्षरित पूर्णांक अतिप्रवाह अपरिभाषित व्यवहार है।
एड्रियन मैकार्थी

2
नीचे सही ढंग से गणना निम्न (बदनाम) तरीके से की जा सकती है:for (size_t i = strlen (str); i --> 0;)
जो सो

1
@JoSo, यह वास्तव में काफी साफ सुथरी चाल है, हालांकि मुझे यकीन नहीं है कि मुझे -->"ऑपरेटर" (" stackoverflow.com/questions/1642028/… देखें ) का परिचय पसंद है । अपने सुझाव को उत्तर में शामिल किया है।
paxdiablo

क्या आप if (i == 0) break;लूप के अंत में एक साधारण काम कर सकते हैं (जैसे for (size_t i = strlen(str) - 1; ; --i),। (मुझे आपका साथ बेहतर लगता है, लेकिन सिर्फ यह सोचकर कि अगर यह ठीक काम करेगा)
RastaJedi

13

परिभाषा के अनुसार, ऑपरेटर size_tका परिणाम है sizeofsize_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 बिट सिस्टम पर 2 जीबी है। हर दूसरे उपयोग के मामले में, हस्ताक्षरित प्रकार का उपयोग करना एक अहस्ताक्षरित प्रकार का उपयोग करने की तुलना में अधिक सुरक्षित है।

उदाहरण:

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। लेकिन intsize_t की तुलना में अधिकांश मामलों में उपयोग करना अभी भी बेहतर है। ptrdiff_tहै long32 और 64 बिट सिस्टम पर।

इसका मतलब है कि जब भी आप एक std :: कंटेनरों के साथ बातचीत करते हैं, तो आपको हमेशा size_t में कनवर्ट करना पड़ता है, जो कि बहुत सुंदर नहीं है। लेकिन जा रहे मूल सम्मेलन में c ++ के लेखकों ने उल्लेख किया कि डिजाइनिंग std :: वेक्टर एक अहस्ताक्षरित size_t के साथ एक गलती थी।

यदि आपका संकलक आपको 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) {
    [...]
}

देशी जाने पर बज़्ने स्ट्रॉस्ट्रुप (C ++ लेखक) के कुछ शब्द

कुछ लोगों के लिए एसटीएल में इस हस्ताक्षरित / अहस्ताक्षरित डिज़ाइन त्रुटि पर्याप्त कारण है, एसटीडी :: वेक्टर का उपयोग न करने के लिए, बल्कि इसके स्वयं के कार्यान्वयन के लिए।


1
मैं समझता हूं कि वे कहां से आ रहे हैं, लेकिन मुझे अब भी लगता है कि यह लिखना अजीब है for(int i = 0; i < get_size_of_stuff(); i++)। अब, निश्चित रूप से, आप बहुत सारे कच्चे लूप नहीं करना चाह सकते हैं, लेकिन - चलो, आप उनका भी उपयोग करते हैं।
einpoklum 12

एकमात्र कारण है कि मैं कच्चे छोरों का उपयोग करता हूं, ऐसा इसलिए है क्योंकि c ++ एल्गोरिथ्म लाइब्रेरी को बहुत बुरी तरह से डिज़ाइन किया गया है। स्काला जैसी भाषाएं हैं, जिनके पास संग्रह पर काम करने के लिए एक बेहतर और अधिक विकसित पुस्तकालय है। फिर कच्चे छोरों का उपयोग मामला बहुत हद तक समाप्त हो गया है। एक नए और बेहतर एसटीएल के साथ सी ++ में सुधार करने के लिए भी दृष्टिकोण हैं, लेकिन मुझे संदेह है कि यह अगले दशक के भीतर होगा।
अर्ने

1
मुझे वह अहस्ताक्षरित i = 0 मिलता है; मुखर (i-1, MAX_INT); लेकिन मुझे समझ में नहीं आता कि आप यह क्यों कहते हैं "अगर मैं पहले से ही एक अंतर्प्रवाह था, तो यह सच हो जाता है" क्योंकि अहस्ताक्षरित चींटियों पर अंकगणित का व्यवहार हमेशा परिभाषित होता है, अर्थात। परिणाम सबसे बड़े प्रतिनिधित्व योग्य पूर्णांक के आकार को मापता है। इसलिए यदि i == 0, तो i-- MAX_INT हो जाता है और फिर i ++ फिर से 0 हो जाता है।
शुभम

@ मब्राहम मैंने ध्यान से देखा, और आप सही हैं, समस्या को दिखाने के लिए मेरा कोड सबसे अच्छा नहीं है। आम तौर पर यह x + 1 < yबराबर होता है x < y - 1, लेकिन वे अनइजेंडर पूर्णांकों के साथ नहीं होते हैं। जब चीजें बदल दी जाती हैं जो आसानी से समतुल्य मानी जाती हैं, तो वे बग का परिचय दे सकते हैं।
आर्ने

8

C- शैली सरणियों को अनुक्रमण / गिनने के लिए std :: size_t का उपयोग करें।

एसटीएल कंटेनरों के लिए, आपके पास (उदाहरण के लिए) होगा vector<int>::size_type, जिसका उपयोग वेक्टर तत्वों को अनुक्रमित और गिनने के लिए किया जाना चाहिए।

व्यवहार में, वे आमतौर पर दोनों अहस्ताक्षरित ints हैं, लेकिन इसकी गारंटी नहीं है, खासकर जब कस्टम आवंटनकर्ताओं का उपयोग करते हुए।


2
लिनक्स पर gcc के साथ, std::size_tआमतौर पर unsigned long(4 बाइट्स पर 8 बाइट्स) होता है unisgned int(4 बाइट्स) के बजाय ।
रफाक

5
size_tहालांकि, सी-स्टाइल सरणियों को अनुक्रमित नहीं किया जाता है, क्योंकि अनुक्रमणिका नकारात्मक हो सकती है। size_tयदि कोई व्यक्ति ऋणात्मक नहीं जाना चाहता है, तो वह इस तरह के एक सरणी के अपने उदाहरण के लिए उपयोग कर सकता है ।
जोहान्स शाउब -

क्या U32 पर तुलनाएं उतनी ही तेज़ हैं जितनी कि u32s पर तुलना? मैं u8s और u16s को पाश प्रहरी के रूप में उपयोग करने के लिए गंभीर प्रदर्शन दंड दिया है, लेकिन मुझे नहीं पता कि इंटेल ने 64 के दशक में एक साथ अपना कार्य किया है या नहीं।
क्रैशवर्क

2
चूंकि सी-स्टाइल ऐरे इंडेक्सिंग ऑपरेटर +पर पॉइंटर्स का उपयोग करने के बराबर है , इसलिए ऐसा लगता है कि ptrdiff_tयह सूचकांकों के लिए उपयोग करने वाला है।
पावेल मिनेव

8
के रूप में vector<T>::size_type(और अन्य सभी कंटेनरों के लिए डिट्टो), यह वास्तव में नहीं बल्कि बेकार है, क्योंकि यह प्रभावी रूप से होने की गारंटी है size_t- यह करने के लिए typedef'd है Allocator::size_typeसम्मान के साथ उस पर प्रतिबंध के लिए, और कंटेनरों को देखने के लिए 20.1.5 / 4 - विशेष रूप से, size_typeजरूरी होना चाहिए size_t, और difference_typeहोना चाहिए ptrdiff_t। बेशक, डिफ़ॉल्ट std::allocator<T>उन आवश्यकताओं को पूरा करता है। तो बस छोटे का उपयोग करें size_tऔर बहुत सारे के साथ परेशान न करें :)
पावेल मिनावे

7

जल्द ही अधिकांश कंप्यूटर 64-बिट OS के साथ 64-बिट आर्किटेक्चर होंगे: तों अरबों तत्वों के कंटेनर पर चलने वाले प्रोग्राम। तो फिर तुम चाहिए का उपयोग size_tकरने के बजाय int, पाश सूचकांक के रूप में अन्यथा अपने सूचकांक होगा लपेट 2 ^ 32 पर:, वें तत्व दोनों 32- और 64-बिट सिस्टम पर।

भविष्य के लिए तैयार!


आपका तर्क केवल उतना ही आगे बढ़ता है जहाँ अर्थ की आवश्यकता होती है एक की long intबजाय int। यदि size_t64-बिट OS पर प्रासंगिक है तो यह 32-बिट OS पर उतना ही प्रासंगिक था।
einpoklum

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 ((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

मेरा कहना है कि जब आप घटाते हैं तो इंट आमतौर पर सुरक्षित होता है।
केमिन झोउ

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");
}

अहस्ताक्षरित पूर्णांक के उपयोग से इस प्रकार के सूक्ष्म मुद्दों को बनाने की क्षमता है। इसलिए imho मैं size_t का उपयोग करना पसंद करता हूं जब मैं कंटेनरों / प्रकारों के साथ बातचीत करता हूं जिनकी आवश्यकता होती है।


एवरोन इस बग के बारे में परेशान किए बिना size_t का उपयोग करने के लिए लगता है, और मैंने इसे कठिन तरीके से सीखा
प्रांजल गुप्ता

-2

size_tएक अहस्ताक्षरित प्रकार है जो आपके आर्किटेक्चर के लिए अधिकतम पूर्णांक मान को धारण कर सकता है, इसलिए इसे साइन के कारण पूर्णांक ओवरफ्लो से सुरक्षित किया जाता है ( 0x7FFFFFFF1 द्वारा अनुमानित इंट्रेक्ट आपको -1 देगा) या लघु आकार (1 के बिना बढ़ाए गए संक्षिप्त int 0xFFFF) 0)।

यह मुख्य रूप से सरणी अनुक्रमण / छोरों / पते अंकगणित और इतने पर उपयोग किया जाता है। memset()समान और समान कार्य ही स्वीकार करते size_tहैं, क्योंकि सैद्धांतिक रूप से आपके पास आकार की मेमोरी 2^32-1(32 बिट प्लेटफॉर्म पर) का एक ब्लॉक हो सकता है ।

ऐसे सरल छोरों के लिए परेशान नहीं है और सिर्फ int का उपयोग करें।


-3

size_t एक अहस्ताक्षरित अभिन्न प्रकार है, जो आपके सिस्टम पर सबसे बड़े पूर्णांक का प्रतिनिधित्व कर सकता है। यदि आपको बहुत बड़े सरणियों, मैट्रीस आदि की आवश्यकता हो तो ही इसका उपयोग करें।

कुछ फ़ंक्शंस size_t लौटाते हैं और यदि आप तुलना करने की कोशिश करते हैं तो आपका कंपाइलर आपको चेतावनी देगा।

उचित हस्ताक्षरित / अहस्ताक्षरित डेटाटाइप का उपयोग करके या तेज़ हैक के लिए बस टाइपकास्ट करने से बचें।


4
यदि आप बग और सुरक्षा छिद्रों से बचना चाहते हैं तो इसका उपयोग करें।
क्रेग मैकक्वीन

2
यह वास्तव में आपके सिस्टम पर सबसे बड़े पूर्णांक का प्रतिनिधित्व करने में सक्षम नहीं हो सकता है।
एड्रियन मैक्कार्थी

-4

size_t अहस्ताक्षरित int है। इसलिए जब भी आप अहस्ताक्षरित int चाहते हैं तो आप इसका उपयोग कर सकते हैं।

जब मैं सरणी का आकार निर्दिष्ट करना चाहता हूं तो इसका उपयोग करता हूं, काउंटर एक्ट ...

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

10
वास्तव में यह जरूरी नहीं कि अहस्ताक्षरित int के समान हो। यह है अहस्ताक्षरित, लेकिन यह बड़ा हो सकता है (या मैं छोटे हालांकि मैं किसी भी प्लेटफॉर्म जहां यह सच है के बारे में पता नहीं है लगता है) एक पूर्णांक से।
टोड गैंबलिन

उदाहरण के लिए, 64 बिट मशीन पर एक size_tअहस्ताक्षरित 64 बिट पूर्णांक हो सकता है, जबकि 32 बिट मशीन पर यह केवल 32 बिट अहस्ताक्षरित पूर्णांक है।
हर्पडेरपिंगटन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.