C ++ में long long int बनाम long int बनाम int64_t


87

मैंने C ++ प्रकार के लक्षणों का उपयोग करते हुए कुछ अजीब व्यवहार का अनुभव किया और अपनी समस्या को इस विचित्र छोटी समस्या तक सीमित कर दिया है जिसके लिए मैं एक टन स्पष्टीकरण दूंगा क्योंकि मैं गलत व्याख्या के लिए कुछ भी खुला नहीं छोड़ना चाहता।

कहो आपके पास एक कार्यक्रम है जैसे:

#include <iostream>
#include <cstdint>

template <typename T>
bool is_int64() { return false; }

template <>
bool is_int64<int64_t>() { return true; }

int main()
{
 std::cout << "int:\t" << is_int64<int>() << std::endl;
 std::cout << "int64_t:\t" << is_int64<int64_t>() << std::endl;
 std::cout << "long int:\t" << is_int64<long int>() << std::endl;
 std::cout << "long long int:\t" << is_int64<long long int>() << std::endl;

 return 0;
}

GCC (और 32- और 64-बिट MSVC) के साथ 32-बिट संकलन में, प्रोग्राम का आउटपुट होगा:

int:           0
int64_t:       1
long int:      0
long long int: 1

हालाँकि, 64-बिट GCC संकलन से उत्पन्न प्रोग्राम आउटपुट करेगा:

int:           0
int64_t:       1
long int:      1
long long int: 0

के बाद से यह उत्सुक है long long intएक हस्ताक्षरित 64-बिट पूर्णांक है और सभी इरादों और मकसदों, के समान के लिए, है long intऔर int64_tप्रकार है, इसलिए तार्किक, int64_t, long intऔर long long intविधानसभा उत्पन्न होती है जब इन प्रकार का उपयोग कर समान है - बराबर प्रकार होगा। एक नज़र stdint.hमुझे बताता है क्यों:

# if __WORDSIZE == 64
typedef long int  int64_t;
# else
__extension__
typedef long long int  int64_t;
# endif

एक 64-बिट संकलन में, int64_tहै long intन एक, long long int(जाहिर है)।

इस स्थिति के लिए तय करना बहुत आसान है:

#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif

लेकिन यह बुरी तरह से हैक किया गया है और अच्छी तरह से (पदार्थ के वास्तविक कार्यों uint64_t, आदि) को स्केल नहीं करता है । तो मेरा सवाल यह है: क्या संकलक को यह बताने का एक तरीका है कि ए long long intभी एक है int64_t, जैसे long intहै?


मेरे शुरुआती विचार हैं कि यह संभव नहीं है, जिस तरह से सी / सी ++ प्रकार की परिभाषाएं काम करती हैं। कंपाइलर के लिए बुनियादी डेटा प्रकारों के प्रकार को निर्दिष्ट करने का कोई तरीका नहीं है, क्योंकि यह कंपाइलर का काम है (और यह अनुमति देता है कि बहुत सी चीजों को तोड़ सकता है) और typedefकेवल एक ही रास्ता जाता है।

मैं यहां एक उत्तर पाने से भी चिंतित नहीं हूं, क्योंकि यह एक सुपर-डुपर एज मामला है कि मुझे संदेह नहीं है कि किसी को भी इस बात की परवाह होगी कि जब उदाहरण बुरी तरह से वंचित न हों (क्या इसका मतलब यह समुदाय विकि होना चाहिए?) ।


परिशिष्ट : इस कारण से कि मैं एक आसान उदाहरण के बजाय आंशिक टेम्पलेट विशेषज्ञता का उपयोग कर रहा हूं:

void go(int64_t) { }

int main()
{
    long long int x = 2;
    go(x);
    return 0;
}

यह कहा जाता है कि उदाहरण अभी भी संकलित होगा, क्योंकि long long intसंक्षेप में एक के लिए परिवर्तनीय है int64_t


परिशिष्ट : एकमात्र उत्तर अब तक मानता है कि मैं जानना चाहता हूं कि क्या एक प्रकार 64-बिट्स है। मैं लोगों को यह सोचकर गुमराह नहीं करना चाहता था कि मैं इस बारे में परवाह करता हूं और संभवत: इस बात का अधिक उदाहरण देना चाहिए कि यह समस्या कहां प्रकट होती है।

template <typename T>
struct some_type_trait : boost::false_type { };

template <>
struct some_type_trait<int64_t> : boost::true_type { };

इस उदाहरण में, some_type_trait<long int>एक होगा boost::true_type, लेकिन some_type_trait<long long int>नहीं होगा। हालांकि यह सी ++ के प्रकारों के बारे में सोचता है, यह वांछनीय नहीं है।

एक अन्य उदाहरण एक क्वालीफायर का उपयोग कर रहा है जैसे same_type(जो कि C ++ 0x अवधारणाओं में उपयोग करने के लिए बहुत सामान्य है):

template <typename T>
void same_type(T, T) { }

void foo()
{
    long int x;
    long long int y;
    same_type(x, y);
}

यह उदाहरण संकलित करने में विफल रहता है, क्योंकि C ++ (सही तरीके से) देखता है कि प्रकार भिन्न हैं। g ++ एक त्रुटि के साथ संकलित करने में विफल रहेगा जैसे: कोई मिलान फ़ंक्शन कॉल नहीं same_type(long int&, long long int&)

मैं इस बात पर जोर देना चाहूंगा कि मुझे समझ में आ रहा है कि ऐसा क्यों हो रहा है, लेकिन मैं ऐसे वर्कअराउंड की तलाश कर रहा हूं जो मुझे सभी जगह कोड दोहराने के लिए मजबूर न करे।


जिज्ञासा से बाहर, क्या आपका नमूना कार्यक्रम sizeofप्रत्येक प्रकार के लिए समान परिणाम देता है ? शायद संकलक long long intअलग-अलग आकार का इलाज कर रहा है ।
ब्लेयर होलोवे

क्या आपने C ++ 0x सक्षम किया है? C ++ 03 में ऐसा नहीं है <cstdint>, इसलिए हो सकता है कि इस तथ्य को "यह एक विस्तार है" (जो यह है) यह कहना है।
GManNickG

हां, मुझे संभवतः निर्दिष्ट करना चाहिए कि मैं उपयोग कर रहा हूं --std=c++0x। और हाँ, sizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8
ट्रैविस गोकेल

1
किसी ने अभी तक इसका उल्लेख नहीं किया है, लेकिन मामले में इसे नजरअंदाज कर दिया गया: longऔर long longअलग-अलग प्रकार हैं (भले ही उनका आकार और प्रतिनिधित्व एक ही हो)। int64_tहमेशा एक अन्य मौजूदा प्रकार के लिए एक उपनाम है (इसके नाम के बावजूद, typedefनए प्रकार का निर्माण नहीं करता है, यह सिर्फ एक उपनाम है जो पहले से मौजूद है)
MM

3
उत्तर / टिप्पणियों में से एक महत्वपूर्ण कथन गायब है, जिसने मुझे इस क्विक को हिट करने में मदद की: कभी-कभी महत्वपूर्ण टेम्पलेट्स के लिए निश्चित आकार के प्रकारों का उपयोग न करें। हमेशा बुनियादी प्रकारों का उपयोग करें और सभी संभावित मामलों को कवर करें (भले ही आप उन टेम्प्लेटों को पलटने के लिए निश्चित आकार के प्रकारों का उपयोग करें)। सभी संभव मामलों साधन: यदि आप के साथ का दृष्टांत करने की जरूरत है int16_t, तो साथ विशेषज्ञ shortऔर intऔर आप सुरक्षा दी जाएगी। (और signed charअगर आप रोमांच महसूस कर रहे हैं)
इरफाइ

जवाबों:


49

ऐसा कुछ देखने के लिए आपको 64-बिट पर जाने की आवश्यकता नहीं है। int32_tसामान्य 32-बिट प्लेटफार्मों पर विचार करें । यह typedef'के रूप में intया एक के रूप में एड हो सकता है long, लेकिन स्पष्ट रूप से एक समय में दो में से केवल एक। intऔर longनिश्चित रूप से अलग प्रकार के होते हैं।

यह देखना मुश्किल नहीं है कि कोई वर्कअराउंड नहीं है जो int == int32_t == long32-बिट सिस्टम पर बनाता है। उसी कारण से, long == int64_t == long long64-बिट सिस्टम पर बनाने का कोई तरीका नहीं है।

यदि आप कर सकते हैं, तो संभावित परिणाम कोड के लिए दर्दनाक होंगे जो अतिभारित होते हैं foo(int), foo(long)और foo(long long)- अचानक उन्हें एक ही अधिभार के लिए दो परिभाषाएं होती हैं ?!

सही समाधान यह है कि आपका टेम्पलेट कोड आमतौर पर एक सटीक प्रकार पर निर्भर नहीं होना चाहिए, लेकिन उस प्रकार के गुणों पर। पूरे same_typeतर्क अभी भी विशिष्ट मामलों के लिए ठीक हो सकते हैं:

long foo(long x);
std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);

यानी, foo(int64_t)जब यह बिल्कुल वैसा ही हो तो ओवरलोड को परिभाषित नहीं किया जाता है foo(long)

[संपादित करें] C ++ 11 के साथ, अब हमारे पास इसे लिखने का एक मानक तरीका है:

long foo(long x);
std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);

[संपादित करें] या सी ++ २०

long foo(long x);
int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);

1
दुखद खबर है, उदाहरण के लिए 64bit MSVC19 (2017) sizeof() longऔर intसमान है, लेकिन std::is_same<long, int>::valueरिटर्न false। OSX HighSierra पर AppleClang 9.1 पर समान विचित्रता।
Ax3l

3
@ Ax3l: यह अजीब नहीं है। वस्तुतः आईएसओ सी 90 के बाद से हर कंपाइलर में कम से कम एक जोड़ी होती है।
एमएसल्टर्स

यह सच है, वे अलग प्रकार के होते हैं।
Ax3l

6

क्या आप जानना चाहते हैं कि क्या एक प्रकार int64_t के समान है या क्या आप जानना चाहते हैं कि कुछ 64 बिट्स है? आपके प्रस्तावित समाधान के आधार पर, मुझे लगता है कि आप बाद के बारे में पूछ रहे हैं। उस मामले में, मैं कुछ ऐसा करूंगा

template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64

1
आप एक returnऔर एक अर्धविराम याद नहीं कर रहे हैं ?
कैसाब्लांका

1
फिर भी, आपको इसके लिए उपयोग करना चाहिए sizeof
बेन वोइगट

5
लंबे लंबे int और long int एक ही प्रकार के नहीं होते हैं चाहे वे एक ही आकार के हों या न हों। व्यवहार गलत नहीं है। वह सिर्फ C ++ है।
लोगन कैपालो

5
यह मामूली टाइपिंग की सीमा नहीं है। यह व्यर्थ नाममात्र टाइपिंग की सीमा है । पुराने दिनों में, वास्तविक मानक short= 16 बिट्स, long= 32 बिट्स, और int= देशी आकार था। 64-बिट के इन दिनों में, intऔर longनहीं है मतलब अब और कुछ भी।
dan04

1
@ dan04: वे कभी भी कम या ज्यादा सार्थक नहीं थे। shortहै कम से कम , 16 बिट intमें कम से कम 16 बिट है, और longकम से कम 32 बिट, साथ (लापरवाही अंकन इस प्रकार) कम <= पूर्णांक <= लंबा है। "पुराने दिन" जिसका आप कभी भी अस्तित्व में न होना देखें; भाषा द्वारा लगाए गए प्रतिबंधों के भीतर हमेशा भिन्नताएं रही हैं। "ऑल द वर्ल्ड ऑफ ए x86" फॉरमेसी सिर्फ उतना ही खतरनाक है जितना पुराना "ऑल वर्ल्ड अ वैक्स फॉलिकैस।
कीथ थॉम्पसन

1

तो मेरा प्रश्न यह है: क्या संकलक को यह बताने का कोई तरीका है कि एक लंबा लंबा int भी एक int64_t है, जैसे कि वह सबसे लंबा है?

यह एक अच्छा सवाल या समस्या है, लेकिन मुझे लगता है कि इसका जवाब नहीं है।

इसके अलावा, एक long intनहीं हो सकता है long long int


# if __WORDSIZE == 64
typedef long int  int64_t;
# else
__extension__
typedef long long int  int64_t;
# endif

मेरा मानना ​​है कि यह libc है। मुझे संदेह है कि आप गहराई में जाना चाहते हैं।

GCC (और 32- और 64-बिट MSVC) के साथ 32-बिट संकलन में, प्रोग्राम का आउटपुट होगा:

int:           0
int64_t:       1
long int:      0
long long int: 1

32-बिट लिनक्स ILP32 डेटा मॉडल का उपयोग करता है। इंटेगर, लॉन्ग और पॉइंटर्स 32-बिट हैं। 64-बिट प्रकार एक है long long

Microsoft डेटा टाइप रेंज में श्रेणियों का दस्तावेज़ देता है । कहना long longसमतुल्य है __int64

हालाँकि, 64-बिट GCC संकलन से उत्पन्न प्रोग्राम आउटपुट करेगा:

int:           0
int64_t:       1
long int:      1
long long int: 0

64-बिट लिनक्स LP64डेटा मॉडल का उपयोग करता है । लोंग 64-बिट हैं और long long64-बिट हैं। 32-बिट के रूप में, Microsoft डेटा प्रकार रेंज और लंबे समय तक रेंज का दस्तावेज है __int64

एक ILP64डेटा मॉडल है जहां सब कुछ 64-बिट है। अपने word32प्रकार के लिए एक परिभाषा प्राप्त करने के लिए आपको कुछ अतिरिक्त काम करना होगा । 64-बिट प्रोग्रामिंग मॉडल जैसे पेपर भी देखें : एलपी 64 क्यों?


लेकिन यह बुरी तरह से हैक किया गया है और अच्छी तरह से (पदार्थ के वास्तविक कार्यों, uint64_t, आदि) को स्केल नहीं करता है ...

हाँ, यह और भी बेहतर हो जाता है। जीसीसी मिक्स और घोषणाओं से मेल खाता है जो कि 64 बिट प्रकार लेने वाले हैं, इसलिए किसी विशेष डेटा मॉडल का पालन करने के बावजूद भी मुश्किल में पड़ना आसान है। उदाहरण के लिए, निम्न संकलन त्रुटि का कारण बनता है और आपको उपयोग करने के लिए कहता है -fpermissive:

#if __LP64__
typedef unsigned long word64;
#else
typedef unsigned long long word64;
#endif

// intel definition of rdrand64_step (http://software.intel.com/en-us/node/523864)
// extern int _rdrand64_step(unsigned __int64 *random_val);

// Try it:
word64 val;
int res = rdrand64_step(&val);

यह परिणाम है:

error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'

इसलिए, LP64इसे अनदेखा करें और इसे इसमें बदलें:

typedef unsigned long long word64;

फिर, 64-बिट ARM IoT गैजेट पर घूमते हैं जो LP64नियॉन को परिभाषित और उपयोग करते हैं:

error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.