सूचक को पूर्णांक में परिवर्तित करना


85

मैं एक मौजूदा कोड को 64 बिट मशीन में बदलने की कोशिश कर रहा हूं। मुख्य समस्या यह है कि एक फ़ंक्शन में, पिछले कोडर एक शून्य * तर्क का उपयोग करता है जिसे फ़ंक्शन में ही उपयुक्त प्रकार में परिवर्तित किया जाता है। एक छोटा उदाहरण:

void function(MESSAGE_ID id, void* param)
{
    if(id == FOO) {
        int real_param = (int)param;
        // ...
    }
}

बेशक, 64 बिट मशीन पर, मुझे त्रुटि मिलती है:

error: cast from 'void*' to 'int' loses precision

मैं इसे ठीक करना चाहूंगा ताकि यह अभी भी 32 बिट मशीन पर और यथासंभव सफाई से काम करे। कोई उपाय ?


5
मुझे पता है कि यह एक पुरानी पोस्ट को खोद रहा है, लेकिन ऐसा लगता है कि स्वीकृत उत्तर बिल्कुल सही नहीं है। size_tकाम नहीं करने का एक ठोस उदाहरण i386 खंडित मेमोरी है। एक 32-बिट मशीन हालांकि, sizeofरिटर्न 2के लिए size_tनीचे एलेक्स का उत्तर सही प्रतीत होता है। एलेक्स का जवाब और uintptr_tहर जगह और उसके अब मानक के बारे में काम करता है। यह C ++ 11 उपचार प्रदान करता है, और यह C ++ 03 हेडर गार्ड भी देता है।
17

जवाबों:


68

का उपयोग करें intptr_tऔर uintptr_t

यह सुनिश्चित करने के लिए कि इसे पोर्टेबल तरीके से परिभाषित किया गया है, आप इस तरह कोड का उपयोग कर सकते हैं:

#if defined(__BORLANDC__)
    typedef unsigned char uint8_t;
    typedef __int64 int64_t;
    typedef unsigned long uintptr_t;
#elif defined(_MSC_VER)
    typedef unsigned char uint8_t;
    typedef __int64 int64_t;
#else
    #include <stdint.h>
#endif

बस कुछ .h फ़ाइल में रखें और जहाँ भी आपको इसकी आवश्यकता हो सम्मिलित करें।

वैकल्पिक रूप से, आप Microsoft के stdint.hफ़ाइल के संस्करण को यहाँ से डाउनलोड कर सकते हैं या यहाँ से एक पोर्टेबल का उपयोग कर सकते हैं


कैसे एक stdint.h प्राप्त करने के लिए MSVC (और संभवतः बोरलैंड) के साथ काम करने के बारे में जानकारी के लिए stackoverflow.com/questions/126279/… देखें ।
माइकल बूर

2
दोनों लिंक टूटे!
एंटोनियो

1
यह उत्तर C से संबंधित है, लेकिन भाषा C ++ का टैग है, इसलिए यह वह उत्तर नहीं है जो मैं खोज रहा था।
हसीबी मीर

@HaSeeBMiR यदि आप डाउनलोड करते हैं, तो <cstdint>उपयुक्त स्विच ऑन या स्विच करने के लिए उपयुक्त cstdintहै stdint.h
जस्टिन टाइम -

1
@HaSeeBMiR C ++ के बजाय C से संबंधित एकमात्र कारण यह है कि यह C C हेडर के बजाय C हेडर का उपयोग करता है। C प्रीप्रोसेसर C ++ cstdintका एक हिस्सा है , और C ++ मानक का एक हिस्सा है, क्योंकि सभी प्रकार के नाम वहां परिभाषित किए गए हैं। यह वास्तव में निर्दिष्ट टैग के लिए उपयुक्त है। ... मैं मैन्युअल रूप से प्रकारों को परिभाषित करने से असहमत हूं, लेकिन ऐसा करने वाले कंपाइलरों के साथ काम करते समय यह आवश्यक हो सकता है।
जस्टिन टाइम -

91

मैं कहूंगा कि यह आधुनिक C ++ तरीका है।

#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);

संपादित करें :

पूर्णांक में सही प्रकार

इसलिए सही तरीके से एक सूचक स्टोर करने के लिए के रूप में एक पूर्णांक है उपयोग करने के लिए uintptr_tया intptr_tप्रकार। ( C99 के लिए cppreference पूर्णांक प्रकारों में भी देखें )।

इन प्रकारों को <stdint.h>C99 के लिए और stdC ++ 11 के लिए नामस्थान में <cstdint>( C ++ के लिए पूर्णांक प्रकार देखें ) में परिभाषित किया गया है ।

सी ++ 11 (और बाद में) संस्करण

#include <cstdint>
std::uintptr_t i;

सी ++ 03 संस्करण

extern "C" {
#include <stdint.h>
}

uintptr_t i;

C99 संस्करण

#include <stdint.h>
uintptr_t i;

सही कास्टिंग ऑपरेटर

C में केवल एक ही कास्ट होता है और C ++ में C का उपयोग करने पर इसे फेंक दिया जाता है (इसलिए C ++ में इसका उपयोग न करें)। C ++ में अलग-अलग जाती है। reinterpret_castइस रूपांतरण के लिए सही कास्ट है ( यहां भी देखें )।

सी ++ 11 संस्करण

auto i = reinterpret_cast<std::uintptr_t>(p);

सी ++ 03 संस्करण

uintptr_t i = reinterpret_cast<uintptr_t>(p);

C संस्करण

uintptr_t i = (uintptr_t)p; // C Version

संबंधित सवाल


6
एकमात्र उत्तर जिसमें ठीक से पुनर्व्याख्या_कास्ट का उल्लेख है
प्लाज़्मासेल

यदि आप <cstdint> को शामिल करना चाहते हैं, तो आप शायद इसके बजाय std :: uintptr_t का उपयोग करना चाहते हैं।
लिनलीनो

बहुत बढ़िया ... कलाकार वही है जिसकी मुझे तलाश थी। यदि हमें uintptr_tइसके बजाय उपयोग करने के लिए कहा जाता है size_t, तो इसकी आवश्यकता क्यों है reinterpret_cast? ऐसा लगता है जैसे एक साधारण static_castको करना चाहिए, क्योंकि मानक विशेष रूप से संगत डेटा प्रकार प्रदान करता है ...
jww

1
@jww पढ़ा: en.cppreference.com/w/cpp/language/static_cast मेरी समझ यहां यह है कि static_castप्रकार परिवर्तित कर सकता है या यदि यह पॉइंटर पॉइंटर समायोजन कर सकता है यदि प्रकार की आवश्यकता है। reinterpret_castवास्तव में सिर्फ अंतर्निहित मेमोरी पैटर्न (कोई म्यूटेशन) के प्रकार को बदल रहा है। स्पष्ट करने के लिए: static_castयहाँ समान व्यवहार करता है।
अलेक्जेंडर ओह

2
इसके बजाय चयनित उत्तर के रूप में चिह्नित किया जाना चाहिए, क्योंकि यह सभी विवरण प्रदान करता है कि C और C ++ में कैसे डाला जाए ।
HaseeB Mir

42

आपकी संरचना (जो भी हो) से मेल खाने के लिए 'size_t' और 'ptrdiff_t' की आवश्यकता होती है। इसलिए, मुझे लगता है कि 'int' का उपयोग करने के बजाय, आपको 'size_t' का उपयोग करने में सक्षम होना चाहिए, जो कि 64 बिट सिस्टम पर 64 बिट प्रकार का होना चाहिए।

यह चर्चा अहस्ताक्षरित int बनाम size_t थोड़ा और अधिक विस्तार में जाती है।


34
जबकि size_t आमतौर पर एक पॉइंटर को पकड़ने के लिए काफी बड़ा होता है, यह जरूरी नहीं कि ऐसा ही हो। बेहतर होगा कि stdint.h हेडर का पता लगाएं (यदि आपके कंपाइलर में पहले से कोई नहीं है) और uintptr_t का उपयोग करें।
माइकल बूर

3
दुर्भाग्य से इस पर एकमात्र अड़चन size_tयह है कि इसे किसी भी परिणाम को धारण करना चाहिए sizeof()। जरूरी नहीं कि यह 64 बिट्स x64 पर बनाये। यह भी देखें
एंटोनी

3
size_t गैर-सदस्य सूचक के मान को सुरक्षित रूप से संग्रहीत कर सकता हैEn.cppreference.com/w/cpp/types/size_t देखें ।
एंडीजोस्ट

2
@AndyJost नहीं यह नहीं हो सकता। यहां तक ​​कि आपका अपना लिंक भी इसकी पुष्टि करता है।
yyny

1
@YoYoYonnY: "कई प्लेटफार्मों पर (एक अपवाद खंडों को संबोधित करने वाली प्रणाली है) std :: size_t सुरक्षित रूप से किसी भी गैर-सदस्य सूचक के मूल्य को संग्रहीत कर सकता है, जिस स्थिति में यह std का पर्याय है :: uintrr_t।" - आप जो भी बात कर रहे हैं?
स्लैशमाईस


8

कई उत्तरों ने 'समाधान' के रूप में uintptr_tऔर इशारा किया है #include <stdint.h>। यही है, मैं सुझाव देता हूं, उत्तर का हिस्सा है, लेकिन पूरे उत्तर नहीं। आपको यह भी देखने की आवश्यकता है कि फ़ंक्शन को FOO के संदेश आईडी के साथ कहां बुलाया गया है।

इस कोड और संकलन पर विचार करें:

$ cat kk.c
#include <stdio.h>
static void function(int n, void *p)
{
    unsigned long z = *(unsigned long *)p;
    printf("%d - %lu\n", n, z);
}

int main(void)
{
    function(1, 2);
    return(0);
}
$ rmk kk
        gcc -m64 -g -O -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith \
            -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes \
            -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE kk.c -o kk 
kk.c: In function 'main':
kk.c:10: warning: passing argument 2 of 'func' makes pointer from integer without a cast
$

आप देखेंगे कि कॉलिंग स्थान ( main()) में एक समस्या है - एक पूर्णांक को एक डाली के बिना एक सूचक में परिवर्तित करना। आपको function()यह देखने के लिए कि इसके मूल्यों को कैसे पारित किया जाता है, आपको अपने सभी उपयोगों में विश्लेषण करने की आवश्यकता है। function()अगर कॉल लिखी जाती तो मेरे अंदर का कोड काम करता:

unsigned long i = 0x2341;
function(1, &i);

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

इसके अलावा, यदि आप void *पैरामीटर के मान को रूपांतरित करने जा रहे हैं (रूपांतरित किया गया है), <inttypes.h>हेडर को ध्यान से देखें (इसके बजाय stdint.h- inttypes.hकी सेवाएं प्रदान करता है stdint.h, जो असामान्य है, लेकिन C99 मानक का कहना है कि [t] उसने हेडर को <inttypes.h>शामिल किया है <stdint.h>और इसे होस्ट की गई कार्यान्वयन द्वारा प्रदान की गई अतिरिक्त सुविधाओं के साथ विस्तारित करता है ) और अपने प्रारूप के तारों में PRIxxx मैक्रोज़ का उपयोग करें।

साथ ही, मेरी टिप्पणियाँ C ++ के बजाय C पर सख्ती से लागू होती हैं, लेकिन आपका कोड C ++ के सबसेट में है जो C और C ++ के बीच पोर्टेबल है। संभावना अच्छी है कि मेरी टिप्पणी लागू होती है।


3
मुझे लगता है कि आप मेरे सवाल पर इस बिंदु से चूक गए। कोड एक सूचक में पूर्णांक के मान को संग्रहीत कर रहा है। और कोड का वह हिस्सा विपरीत कार्य कर रहा है (उदाहरण के लिए पूर्णांक के मान को निकालना जो एक सूचक के रूप में लिखा गया था )।
पियरेबीडीआर

@PierreBdR फिर भी वह एक बहुत ही मान्य बिंदु बनाता है। यह कोड को देखने के लिए हमेशा इतना सरल नहीं होता है (जब संकलक इसके बारे में चेतावनी देते हैं) जो एक हस्ताक्षर किए गए इंट का उपयोग करता है, लेकिन एक आकार के लिए उपयोग किया जाता है और इसे अहस्ताक्षरित में बदलने के लिए ठीक लगता है। दुर्भाग्य से यह हमेशा सरल नहीं होता है। आपको प्रत्येक मामले को स्पष्ट रूप से देखना होगा जब तक कि आप संभावित बग - और उस पर सूक्ष्म कीड़े पैदा नहीं करना चाहते।
22

4
  1. #include <stdint.h>
  2. uintptr_tशामिल मानक हेडर फ़ाइल में परिभाषित मानक प्रकार का उपयोग करें ।

3

SQLite के स्रोत कोड का अध्ययन करते समय मैं इस सवाल पर आया था

में sqliteInt.h , वहाँ पूर्णांक और सूचक के बीच एक मैक्रो परिवर्तित परिभाषित कोड का एक पैरा है। लेखक ने पहले एक बहुत अच्छा बयान दिया कि यह एक संकलक पर निर्भर समस्या होनी चाहिए और फिर वहाँ से बाहर के अधिकांश लोकप्रिय संकलक के लिए समाधान का कार्यान्वयन किया।

#if defined(__PTRDIFF_TYPE__)  /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X)  ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X)  ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__)       /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X)  ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X)  ((int)(((char*)X)-(char*)0))
#elif defined(HAVE_STDINT_H)   /* Use this case if we have ANSI headers */
# define SQLITE_INT_TO_PTR(X)  ((void*)(intptr_t)(X))
# define SQLITE_PTR_TO_INT(X)  ((int)(intptr_t)(X))
#else                          /* Generates a warning - but it always works     */
# define SQLITE_INT_TO_PTR(X)  ((void*)(X))
# define SQLITE_PTR_TO_INT(X)  ((int)(X))
#endif

और यहाँ अधिक विवरण के लिए टिप्पणी का एक उद्धरण है:

/*
** The following macros are used to cast pointers to integers and
** integers to pointers.  The way you do this varies from one compiler
** to the next, so we have developed the following set of #if statements
** to generate appropriate macros for a wide range of compilers.
**
** The correct "ANSI" way to do this is to use the intptr_t type.
** Unfortunately, that typedef is not available on all compilers, or
** if it is available, it requires an #include of specific headers
** that vary from one machine to the next.
**
** Ticket #3860:  The llvm-gcc-4.2 compiler from Apple chokes on
** the ((void*)&((char*)0)[X]) construct.  But MSVC chokes on ((void*)(X)).
** So we have to define the macros in different ways depending on the
** compiler.
*/

क्रेडिट कमिटर्स के पास जाता है।


2

सबसे अच्छी बात यह है कि पॉइंटर टाइप से नॉन-पॉइंटर टाइप में कनवर्ट करने से बचें। हालाँकि, यह आपके मामले में स्पष्ट रूप से संभव नहीं है।

जैसा कि सभी ने कहा, uintptr_t वह है जिसका आपको उपयोग करना चाहिए।

इस लिंक में 64-बिट कोड को परिवर्तित करने के बारे में अच्छी जानकारी है।

Comp.std.c पर भी इसकी अच्छी चर्चा है


2

मुझे लगता है कि इस मामले में शून्य का "अर्थ" एक सामान्य संभाल है। यह किसी मान का सूचक नहीं है, यह स्वयं मूल्य है। (यह सिर्फ यह बताया जाता है कि C और C ++ प्रोग्रामर द्वारा कैसे void * का उपयोग किया जाता है।)

यदि यह एक पूर्णांक मान रखता है, तो यह पूर्णांक सीमा के भीतर बेहतर था!

यहाँ पूर्णांक के लिए आसान प्रतिपादन है:

int x = (char*)p - (char*)0;

यह केवल एक चेतावनी देना चाहिए।


0

के बाद से uintptr_tकिया गया है सी ++ / C ++ 11 में वहाँ होने की गारंटी नहीं , अगर यह एक एक तरह से रूपांतरण आप विचार कर सकते हैं है uintmax_t, हमेशा के रूप में परिभाषित <cstdint>

auto real_param = reinterpret_cast<uintmax_t>(param);

सुरक्षित खेलने के लिए, एक कोड में कहीं भी जोड़ सकता है:

static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
              "No suitable integer type for conversion from pointer type");

यदि आपके पास uintptr_t नहीं है, तो uintmax_t एक उत्तर नहीं है: या तो कोई गारंटी नहीं है कि आप इसमें एक पॉइंटर का मान संग्रहीत कर सकते हैं! कोई पूर्णांक प्रकार नहीं हो सकता है जो ऐसा करता है।
पियरब्रेब
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.