একটি পয়েন্টারকে পূর্ণসংখ্যায় রূপান্তর করা


88

আমি একটি বিদ্যমান কোডটি একটি bit৪ বিট মেশিনের সাথে মানিয়ে নেওয়ার চেষ্টা করছি। মূল সমস্যাটি হ'ল এক ফাংশনে, পূর্ববর্তী কোডারটি একটি শূন্য * আর্গুমেন্ট ব্যবহার করে যা ফাংশনটিতেই উপযুক্ত ধরণের রূপান্তরিত হয়। একটি সংক্ষিপ্ত উদাহরণ:

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 বিট মেশিনে এবং যতটা সম্ভব পরিষ্কারভাবে কাজ করে। কোন ধারণা ?


4
আমি জানি এটি একটি পুরানো পোস্ট খনন করছে, তবে মনে হচ্ছে এটি গৃহীত উত্তরটি বেশ সঠিক নয়। size_tকাজ না করার একটি দৃ concrete় উদাহরণ হ'ল i386 খণ্ডিত মেমরি। যদিও একটি 32-বিট মেশিন, এর জন্য sizeofফিরে আসে । নীচে অ্যালেক্স উত্তর সঠিক প্রদর্শিত হবে। অ্যালেক্সের উত্তর এবং প্রায় সর্বত্র এবং এখনকার স্ট্যান্ডার্ডে কাজ করে। এটি একটি সি ++ 11 চিকিত্সা সরবরাহ করে এবং এটি সি ++ 03 শিরোনাম রক্ষীদেরও দেয়। 2size_tuintptr_t
jww

উত্তর:


70

ব্যবহার করুন 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 ফাইলে রাখুন এবং আপনার যেখানেই প্রয়োজন সেখানে অন্তর্ভুক্ত করুন।

অন্যথা, আপনি মাইক্রোসফট এর সংস্করণ ডাউনলোড করতে পারেন stdint.hথেকে ফাইল এখানে বা থেকে পোর্টেবল ব্যবহার এখানে


দেখুন stackoverflow.com/questions/126279/... কিভাবে stdint.h যে MSVC সাথে কাজে (এবং সম্ভবত Borland) পেতে উপর তথ্য জন্য।
মাইকেল বারার

4
দুটি লিঙ্কই ভেঙে গেছে!
আন্তোনিও

4
এই উত্তরটি সি এর সাথে সম্পর্কিত তবে ভাষাটি ট্যাগ করা হয়েছে সি ++ সুতরাং আমি যা খুঁজছিলাম তার উত্তর নয় not
হাসিব মীর

@ হাসিবিএমআইআর একটি উপযুক্ত ফিক্সটি হ'ল স্যুইচ করা <cstdint>, বা আপনি যদি ডাউনলোড করে থাকেন তবে উপযুক্ত cstdintডাউনলোড করা stdint.h
জাস্টিন সময় - মনিকা

4
@ হাইসিবিএমআইআর উত্তরটির সাথে সি ++ এর পরিবর্তে সি সম্পর্কিত কেবল কারণ হ'ল এটি সমমানের সি ++ শিরোনামের পরিবর্তে সি শিরোনাম ব্যবহার করে। সি প্রিপ্রসেসর হ'ল সি ++ এর cstdintএকটি অংশ, এবং সি ++ স্ট্যান্ডার্ডের একটি অংশ, যেমন সেখানে বর্ণিত সমস্ত প্রকারের নাম রয়েছে। এটি নির্দিষ্ট ট্যাগগুলির জন্য সত্যই উপযুক্ত। ... আমি ম্যানুয়ালি ধরণের সংজ্ঞা প্রদানের সাথে একমত নই, তবে এমন সংকলকগুলির সাথে কাজ করার সময় এটি প্রয়োজন হতে পারে যা এটি না করে।
জাস্টিন সময় - মনিকা

94

আমি বলব এটি আধুনিক সি ++ উপায়।

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

সম্পাদনা :

পূর্ণসংখ্যার সঠিক টাইপ

সুতরাং একটি পূর্ণসংখ্যা হিসাবে পয়েন্টার সঞ্চয় করার সঠিক উপায়টি uintptr_tবা intptr_tপ্রকারগুলি ব্যবহার করা । (সি 99 এর জন্য সিপ্রিফারেন্স পূর্ণসংখ্যার ধরণগুলিতেও দেখুন )।

এই <stdint.h>ধরণেরগুলি C99 এর জন্য এবং stdসি ++ 11 এর নেমস্পেসে <cstdint>( সি ++ এর জন্য পূর্ণসংখ্যার প্রকারগুলি দেখুন ) সংজ্ঞায়িত করা হয়েছে ।

সি ++ 11 (এবং এরপরে) সংস্করণ

#include <cstdint>
std::uintptr_t i;

সি ++ 03 সংস্করণ

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

uintptr_t i;

সি 99 সংস্করণ

#include <stdint.h>
uintptr_t i;

সঠিক ingালাই অপারেটর

সি-তে কেবল একটি কাস্ট রয়েছে এবং সি ++ তে সি কাস্ট ব্যবহার করা ভ্রূণ্য হয় (সুতরাং এটি সি ++ এ ব্যবহার করবেন না)। সি ++ তে আলাদা আলাদা কাস্ট থাকে। reinterpret_castএই রূপান্তরটির জন্য সঠিক কাস্ট (এটি এখানেও দেখুন )।

সি ++ 11 সংস্করণ

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

সি ++ 03 সংস্করণ

uintptr_t i = reinterpret_cast<uintptr_t>(p);

সি সংস্করণ

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

সম্পর্কিত প্রশ্নাবলী


6
একমাত্র উত্তর যা সঠিকভাবে পুনরায় ব্যাখ্যা_কাস্টের উল্লেখ করেছে
প্লাজম্যাসেল

আপনি যদি <cstdint> অন্তর্ভুক্ত করতে চেয়েছিলেন তবে আপনি সম্ভবত পরিবর্তে std :: uintptr_t ব্যবহার করতে চান।
লিলেনো

দুর্দান্ত ... কাস্ট হ'ল আমি যা খুজছিলাম। যদি আমাদের uintptr_tপরিবর্তে ব্যবহার করতে বলা হয় size_t, তবে এটির প্রয়োজন কেন reinterpret_cast? এটি static_cast
সাধারণের

4
@ jjp পড়ুন: en.cppreferences.com/w/cpp/language/static_cast এখানে আমার বোঝাপড়াটি static_castএই ধরণের রূপান্তর করতে পারে বা যদি কোনও পয়েন্টারটি টাইপটির প্রয়োজন হয় তবে পয়েন্টার সামঞ্জস্য করতে পারে। reinterpret_castপ্রকৃতপক্ষে কেবল অন্তর্নিহিত মেমরি প্যাটার্নের ধরণ পরিবর্তন করছে (কোনও রূপান্তর নেই)। পরিষ্কার করা: static_castএখানে অভিন্ন আচরণ করে।
আলেকজান্ডার ওহ

4
এটি পরিবর্তে নির্বাচিত উত্তর হিসাবে চিহ্নিত করা উচিত, কারণ এটি সি এবং সি ++ এ কীভাবে কাস্ট করা যায় তার সমস্ত বিশদ সরবরাহ করে ।
হাসিব মীর

43

আপনার আকারের সাথে (আকার যাই হোক না কেন) মাপতে 'সাইজ_টি' এবং 'পিটিার্ডিফ_টি' প্রয়োজন। অতএব, আমি 'ইনট' ব্যবহার না করে মনে করি, আপনার 'সাইজ_টি' ব্যবহার করা উচিত, যা bit৪ বিট সিস্টেমে একটি bit৪ বিট প্রকারের হওয়া উচিত।

এই আলোচনা স্বাক্ষরযুক্ত ইন্ট বনাম সাইজ_টি কিছুটা আরও বিশদে চলে।


34
যদিও সাইজ_টি পয়েন্টার ধরে রাখার জন্য যথেষ্ট বড়, তবে এটি অবশ্যই প্রয়োজন হয় না। একটি stdint.h শিরোনাম সনাক্ত করা ভাল (যদি আপনার সংকলকটি ইতিমধ্যে একটি না থাকে) এবং uintptr_t ব্যবহার করুন।
মাইকেল বুড়

4
দুর্ভাগ্যক্রমে একমাত্র সীমাবদ্ধতা size_tহ'ল এটি অবশ্যই যে কোনও ফলাফল রাখা উচিত sizeof()। এটি অগত্যা x64 এ 64 বিট তৈরি করে না। আরও দেখুন
এন্টোইন

4
size_t কোনও সদস্যবিহীন পয়েন্টারের মানটি নিরাপদে সংরক্ষণ করতে পারে। En.cppreferences.com/w/cpp/tyype/size_t দেখুন ।
অ্যান্ডি জাস্ট

4
@ অ্যান্ডি জাস্ট না এটি পারবেন না। এমনকি আপনার নিজের লিঙ্কও তা নিশ্চিত করে।
yyny

4
@YYYOYonnY: "অনেক প্ল্যাটফর্মে (একটি ব্যতিক্রমী অংশীদারি ঠিকানা সম্বলিত সিস্টেম) std :: আকার_t নিরাপদে কোনও অ-সদস্য পয়েন্টারের মান সংরক্ষণ করতে পারে, সেক্ষেত্রে এটি std :: uintptr_t এর সমার্থক হয়।" - আপনি যা কিছু কথা বলছেন?
স্ল্যাশমেজ


8

বেশ কিছু উত্তর দিকে তাক আছে uintptr_tএবং #include <stdint.h>'' সমাধান হিসেবে। তা হ'ল, আমি পরামর্শ দিয়েছি, উত্তরের একটি অংশ, তবে পুরো উত্তরটি নয়। আপনার এফওওর বার্তা আইডি সহ ফাংশনটি কোথায় ডাকা হয়েছে তাও আপনাকে দেখতে হবে।

এই কোড এবং সংকলন বিবেচনা করুন:

$ 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()মানগুলি কীভাবে চলেছে তা দেখার জন্য আপনাকে তার সমস্ত ব্যবহারের ক্ষেত্রে আপনার বিশ্লেষণ করতে হবে need function()কলগুলি লিখিত থাকলে আমার ভিতরে কোডটি কাজ করবে:

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

যেহেতু আপনার সম্ভবত অন্যরকম লেখা হয়েছে, আপনাকে ফাংশনটি যেখানে ডাকা হয়েছে সেই মানটি ব্যবহার করার জন্য এটি বোধগম্য হয় তা নিশ্চিত করে পর্যালোচনা করা উচিত need ভুলে যাবেন না, আপনি একটি সুপ্ত বাগ খুঁজে পেতে পারেন।

এছাড়াও, আপনি যদি void *প্যারামিটারটির মান রূপান্তর করতে চলেছেন (রূপান্তরিত হিসাবে) তবে <inttypes.h>শিরোনামটি সাবধানতার সাথে দেখুন (পরিবর্তে stdint.h- inttypes.hএর পরিষেবাগুলি সরবরাহ করে stdint.hযা অস্বাভাবিক, তবে সি 99 স্ট্যান্ডার্ড বলছে [টি] তিনি শিরোনামটিতে <inttypes.h>শিরোনাম <stdint.h>এবং এটি হোস্টেড বাস্তবায়ন দ্বারা সরবরাহিত অতিরিক্ত সুবিধার সাথে প্রসারিত করে ) এবং আপনার ফর্ম্যাট স্ট্রিংগুলিতে পিআরআইএক্সএক্সএক্সএক্স ম্যাক্রোগুলি ব্যবহার করুন।

এছাড়াও, আমার মন্তব্যগুলি সি ++ এর পরিবর্তে সিটিতে কঠোরভাবে প্রযোজ্য, তবে আপনার কোডটি সি ++ এর সাবসেটে রয়েছে যা সি এবং সি ++ এর মধ্যে বহনযোগ্য। আমার মন্তব্যগুলি কার্যকর হওয়ার সম্ভাবনাগুলি ন্যায্য।


4
আমার মনে হয় আপনি আমার প্রশ্নের বিন্দুটি মিস করেছেন। কোডটি একটি পয়েন্টারে একটি পূর্ণসংখ্যার মান সংরক্ষণ করে। এবং কোডটির সেই অংশটি বিপরীতভাবে করছে (উদাহরণস্বরূপ যে পয়েন্টার হিসাবে লেখা হয়েছিল পূর্ণসংখ্যার মান বের করা )।
পিয়েরবিডিআর

@ পিয়ারবিডিআর তবুও তিনি খুব কার্যকর পয়েন্ট করেছেন। কোডটি দেখার পক্ষে এতটা সহজ নয় (সহ কম্পাইলাররা এটি সম্পর্কে সতর্কতা সহ) যা একটি স্বাক্ষরিত ইনট ব্যবহার করে তবে একটি আকারের জন্য ব্যবহৃত হয় এবং এটি স্বাক্ষরবিহীন অবস্থায় পরিবর্তন করা ঠিক বলে মনে করে। দুর্ভাগ্যক্রমে এটি সর্বদা সহজ নয়। আপনি সম্ভাব্য ত্রুটিগুলি - এবং এটিতে সূক্ষ্ম বাগ তৈরি করতে না চাইলে আপনাকে প্রতিটি কেস স্পষ্টভাবে দেখতে হবে।
প্রাইফটান

4
  1. #include <stdint.h>
  2. uintptr_tঅন্তর্ভুক্ত স্ট্যান্ডার্ড শিরোনাম ফাইলে সংজ্ঞায়িত স্ট্যান্ডার্ড টাইপ ব্যবহার করুন।

4

এসকিউএলাইটের উত্স কোড অধ্যয়নকালে আমি এই প্রশ্নটি জুড়ে এসেছি ।

SqliteInt.hকোডের অনুচ্ছেদে সংখ্যার ম্যাক্রো রূপান্তর সংজ্ঞা দেওয়া হয় ger লেখক প্রথমে একটি খুব ভাল বক্তব্য দিয়েছিলেন যে এটি একটি সংকলক নির্ভর সমস্যা হতে হবে এবং তারপরে সেখানকার বেশিরভাগ জনপ্রিয় সংকলকগুলির অ্যাকাউন্ট হিসাবে সমাধানটি কার্যকর করে।

#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

আমি মনে করি এই ক্ষেত্রে শূন্য * এর "অর্থ" একটি জেনেরিক হ্যান্ডেল। এটি কোনও মানের কোনও পয়েন্টার নয়, এটি নিজেই মান। (এটি কেবল সি এবং সি ++ প্রোগ্রামারদের দ্বারা কীভাবে অকার্যকর * ব্যবহৃত হয় তা হ'ল))

যদি এটি একটি পূর্ণসংখ্যার মানটি ধরে রাখে তবে এটির পূর্ণসংখ্যার ব্যাপ্তির মধ্যে আরও ভাল হওয়া উচিত!

এখানে পূর্ণসংখ্যায় সহজে উপস্থাপন করা হয়:

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

এটি শুধুমাত্র একটি সতর্কতা দেওয়া উচিত।


0

যেহেতু uintptr_tহয় সি ++ / সি ++ 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 এরও কোনও উত্তর নয়: কোনও গ্যারান্টি নেই আপনি এটিতে একটি পয়েন্টারের মান সংরক্ষণ করতে পারেন! কোনও পূর্ণসংখ্যার প্রকার নেই যা এটি করে।
পিয়েরবিডিআর
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.