নাল রেফারেন্স কি সম্ভব?


109

এই কোডটি কি বৈধ (এবং সংজ্ঞায়িত আচরণ)?

int &nullReference = *(int*)0;

উভয় ছ ++ এবং ঝনঝন ++, কম্পাইল এটা কোনো সতর্কবাণী ছাড়াই, এমনকি যখন ব্যবহার -Wall, -Wextra, -std=c++98, -pedantic, -Weffc++...

অবশ্যই রেফারেন্সটি আসলে নাল নয়, যেহেতু এটি অ্যাক্সেস করা যায় না (এটির অর্থ নাল পয়েন্টারকে অবজ্ঞা করা হবে), তবে আমরা এটির ঠিকানাটি পরীক্ষা করে এটি বাতিল বা না তা পরীক্ষা করে দেখতে পারি:

if( & nullReference == 0 ) // null reference

4
আপনি কি এমন কোনও মামলা দিতে পারেন যেখানে এটি আসলে কার্যকর হবে? অন্য কথায়, এটি কি কেবল একটি তত্ত্বের প্রশ্ন?
সিডিওউই

আচ্ছা, রেফারেন্সগুলি কি কখনও অনিবার্য? পয়েন্টারগুলি সর্বদা তাদের পরিবর্তে ব্যবহার করা যেতে পারে। যেমন নাল রেফারেন্স আপনাকে রেফারেন্স ব্যবহার করতে দেয় যখন আপনি উল্লেখ করতে কোনও আপত্তি না থাকতে পারে। এটি কতটা নোংরা তা জানেন না, তবে এটি ভাবার আগে আমি এর বৈধতা সম্পর্কে আগ্রহী ছিলাম।
পেরো

8
আমি মনে করি এটি ভ্রষ্ট হয়েছে
ডিফল্ট

23
"আমরা পরীক্ষা করতে পারি" - না, আপনি পারবেন না। এমন সংকলক রয়েছে যা if (false)চেকটি মুছে ফেলাতে স্টেটমেন্টটি রূপান্তরিত করে, ঠিক কারণ কারণ যাইহোক রেফারেন্স নਾਲ হতে পারে না। লিনাক্স কার্নেলে আরও একটি নথিভুক্ত সংস্করণ বিদ্যমান ছিল, যেখানে খুব অনুরূপ এনএলএল চেকটি
মাসাল্টার

4
"পয়েন্টারের পরিবর্তে একটি রেফারেন্স ব্যবহার করার একটি বড় কারণ হ'ল এটি আপনাকে কোনও বৈধ অবজেক্টের উল্লেখ করে কিনা তা পরীক্ষা করে দেখার বোঝা থেকে মুক্তি দেওয়া" ডিফল্টের লিঙ্কে এই উত্তরটি বেশ ভাল লাগছে!
পেরো

উত্তর:


78

উল্লেখগুলি পয়েন্টার নয়।

8.3.2 / 1:

একটি বৈধ অবজেক্ট বা ফাংশন উল্লেখ করার জন্য একটি রেফারেন্স শুরু করা হবে। [দ্রষ্টব্য: বিশেষত, একটি সংজ্ঞায়িত প্রোগ্রামে নাল রেফারেন্সের অস্তিত্ব থাকতে পারে না, কারণ এই জাতীয় রেফারেন্স তৈরির একমাত্র উপায় হ'ল নাল পয়েন্টারকে অবজ্ঞা করে প্রাপ্ত "অবজেক্ট" এর সাথে আবদ্ধ করা, যা অনির্ধারিত আচরণের কারণ হয়। 9.6 তে বর্ণিত হিসাবে, কোনও রেফারেন্স সরাসরি বিট-ফিল্ডের সাথে আবদ্ধ হতে পারে না। ]

১.৯ / ৪:

কিছু আন্তর্জাতিক ক্রিয়াকলাপ এই আন্তর্জাতিক স্ট্যান্ডার্ডে অপরিজ্ঞাত হিসাবে বর্ণিত হয়েছে (উদাহরণস্বরূপ, নাল পয়েন্টারকে অবজ্ঞার প্রভাব)

জোহানেস যেমন মুছে ফেলা উত্তরে বলেছে, "নাল পয়েন্টারকে অবজ্ঞা করা" স্পষ্টভাবে অপরিজ্ঞাত আচরণ বলে উল্লেখ করা উচিত কিনা সে বিষয়ে কিছু সন্দেহ রয়েছে। তবে এটি সন্দেহের জন্ম দেওয়ার ক্ষেত্রে অন্যতম নয়, যেহেতু নাল পয়েন্টারটি অবশ্যই "বৈধ বস্তু বা ক্রিয়া" দেখায় না এবং মান কমিটির মধ্যে নাল রেফারেন্স প্রবর্তনের কোনও ইচ্ছা নেই to


আমি আমার উত্তরটি সরিয়ে নিলাম যেহেতু আমি বুঝতে পেরেছি যে নাল পয়েন্টারটিকে কেবলমাত্র ডিফারেন্স করা এবং একটি লভ্যালু পাওয়ার বিষয়টি বোঝায় যে এটি উল্লেখ করার মতো আসলে একটি রেফারেন্সকে আবদ্ধ করার চেয়ে আলাদা বিষয়। যদিও লভ্যালুগুলি বস্তু বা ফাংশনগুলিকে খুব বেশি উল্লেখ করে বলা হয় (সুতরাং এই ক্ষেত্রে, একটি রেফারেন্স বাইন্ডিংয়ের সাথে আসলেই কোনও পার্থক্য নেই), এই দুটি বিষয় এখনও আলাদা উদ্বেগের বিষয়। নিখরচায় নিখরচায়
জোহানেস

4
@ এসএমএলটার (মুছে ফেলা উত্তরের বিষয়ে মন্তব্য করার জবাব; এখানে প্রাসঙ্গিক) আমি সেখানে উপস্থাপিত যুক্তির সাথে বিশেষত একমত হতে পারি না। যদিও সর্বজনীন &*pহিসাবে এলেডদের পক্ষে সুবিধাজনক হতে পারে তবে এটি অপরিজ্ঞাত pআচরণকে অস্বীকার করে না (যা প্রকৃতিতে এটি "কাজ করে বলে মনে হতে পারে"); এবং আমি একমত নই যে একটি typeidঅভিব্যক্তি যা "dereferencesd নাল পয়েন্টার" টাইপটি নির্ধারণ করতে চায় তা আসলে নাল পয়েন্টারটিকেই অবজ্ঞায়িত করে। আমি লোকেরা গুরুত্ব সহকারে তর্ক &a[size_of_array]করতে দেখেছি যে নির্ভর করা যায় না এবং করা উচিত নয় এবং যাইহোক এটি কেবল সহজভাবে লেখা সহজ এবং নিরাপদ a + size_of_array
কার্ল নচেটেল

[সি ++] ট্যাগগুলিতে @ ডিফল্ট স্ট্যান্ডার্ডগুলি বেশি হওয়া উচিত। আমার উত্তরটি মনে হচ্ছিল উভয় কাজই এক এবং একই জিনিস ছিল :) যখন আপনি "কোনও অবজেক্ট" বোঝায় না এমন কোনও মূল্যবোধ অবলম্বন করতে এবং লাভ করার সময় এটি একটি রেফারেন্সে সংরক্ষণ করে সীমিত সুযোগ থেকে দূরে যায় এবং হঠাৎ প্রভাব ফেলতে পারে আরও অনেক কোড।
জোহানেস স্কাউব -

@ কার্ল ভাল সি ++ এ, "ডেরেফারেন্সিং" এর অর্থ কোনও মান পড়ার অর্থ নয়। কিছু লোক মনে করেন "ডিরিফারেন্স" এর অর্থ আসলে সঞ্চিত মান অ্যাক্সেস বা সংশোধন করা, তবে এটি সত্য নয়। যুক্তিটি হ'ল সি ++ বলে যে একটি লভ্যালু "একটি বস্তু বা ফাংশন" বোঝায়। যদি তা হয়, তবে প্রশ্নটি হল মানটি কী *pবোঝায়, যখন pনাল পয়েন্টার হয়। সি ++ এর বর্তমানে একটি খালি লভ্যালু ধারণা নেই, যা 232 সংখ্যাটি প্রবর্তন করতে চেয়েছিল।
জোহানেস স্কাউব -

typeidশব্দার্থবিজ্ঞানের উপর ভিত্তি করে সিনট্যাক্সের ভিত্তিতে রচনামূলক নাল পয়েন্টার সনাক্তকরণ । অর্থাৎ যদি আপনি না typeid(0, *(ostream*)0)আপনি কি করতে অনির্ধারিত আচরণ আছে - কোন bad_typeidনিক্ষিপ্ত হতে নিশ্চিত করা হয়, যদিও আপনি একটি lvalue একটি নাল পয়েন্টার ডি-রেফারেন্স শব্দার্থগতভাবে ফলে পাস। তবে সিনট্যাক্টিকালি টপলেভেলে এটি কোনও ডিরিফারেন্স নয়, কমা অপারেটর এক্সপ্রেশন।
জোহানেস স্কাউব -

29

উত্তরটি আপনার দৃষ্টিভঙ্গির উপর নির্ভর করে:


আপনি যদি সি ++ স্ট্যান্ডার্ড দ্বারা বিচার করেন তবে আপনি নাল রেফারেন্স পাবেন না কারণ আপনি প্রথমে অপরিজ্ঞাত আচরণ পান। অপরিবর্তিত আচরণের প্রথম ঘটনাটির পরে, মানটি কিছু হতে দেয়। সুতরাং, আপনি যদি লিখেন তবে *(int*)0একটি ভাষা স্ট্যান্ডার্ড দৃষ্টিকোণ থেকে নাল পয়েন্টারটিকে ডিফারেন্স করে আপনার মতো ইতিমধ্যে অপরিবর্তিত আচরণ রয়েছে। প্রোগ্রামটির বাকী অংশগুলি অপ্রাসঙ্গিক, একবার এই অভিব্যক্তিটি কার্যকর হয়ে গেলে আপনি খেলা থেকে বাইরে চলে যান।


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

তবুও, অপ্টিমাইজ করা অপরিজ্ঞাত আচরণটি প্রমাণ করতে সংকলকটির উপর নির্ভর করে, যা করা সম্ভব নয়। একটি ফাইলের মধ্যে এই সাধারণ ফাংশন বিবেচনা করুন converter.cpp:

int& toReference(int* pointer) {
    return *pointer;
}

সংকলকটি যখন এই ফাংশনটি দেখবে, তখন এটি জানে না যে পয়েন্টারটি নাল পয়েন্টার কিনা। সুতরাং এটি কেবল কোড উত্পন্ন করে যা কোনও পয়েন্টারকে সংশ্লিষ্ট রেফারেন্সে পরিণত করে। (বিটিডব্লিউ: এটি একটি নোট, যেহেতু পয়েন্টার এবং রেফারেন্সগুলি হ'ল সমাবেশে সঠিক একই প্রাণীটি same) এখন, যদি আপনার user.cppকোড সহ অন্য কোনও ফাইল থাকে

#include "converter.h"

void foo() {
    int& nullRef = toReference(nullptr);
    cout << nullRef;    //crash happens here
}

সংকলকটি জানে না যে toReference()এটি উত্তীর্ণ পয়েন্টারটিকে অবজ্ঞাপূর্ণ করবে এবং ধরে নেবে যে এটি কোনও বৈধ রেফারেন্স দেয়, যা অনুশীলনে নাল রেফারেন্স হিসাবে ঘটবে। কলটি সফল হয় তবে আপনি যখন রেফারেন্সটি ব্যবহার করার চেষ্টা করবেন তখন প্রোগ্রামটি ক্র্যাশ হয়ে যায়। আশা করি। মানটি গোলাপী হাতির উপস্থিতিসহ কিছু ঘটতে দেয়।

আপনি এটি জিজ্ঞাসা করতে পারেন কেন এটি প্রাসঙ্গিক, সর্বোপরি, অনির্ধারিত আচরণটি ইতিমধ্যে ভিতরে ভিতরে ট্রিগার হয়েছিল toReference()। উত্তরটি ডিবাগ করা হচ্ছে: নাল উল্লেখগুলি নাল পয়েন্টারগুলির মতোই প্রচার ও প্রসারণ করতে পারে। যদি আপনি সচেতন না হন যে নাল উল্লেখ পাওয়া যায়, এবং সেগুলি তৈরি করা এড়াতে শিখেন, তবে আপনি যখন কেবল কোনও সাধারণ পুরানো intসদস্যটি পড়ার চেষ্টা করছেন তখন আপনার সদস্য ফাংশনটি ক্র্যাশ হওয়ার কারণ বলে মনে করার চেষ্টা করতে আপনি বেশ কিছুটা সময় ব্যয় করতে পারেন (উত্তর: উদাহরণ সদস্যের আহ্বানে একটি নাল রেফারেন্স ছিল, thisএকইভাবে নাল পয়েন্টার, এবং আপনার সদস্য ঠিকানা 8 হিসাবে চিহ্নিত বলে গণনা করা হয়েছে)।


তাহলে নাল রেফারেন্সের জন্য কীভাবে পরীক্ষা করা যায়? আপনি লাইন দিয়েছেন

if( & nullReference == 0 ) // null reference

আপনার প্রশ্নে ভাল, এটি কাজ করবে না: স্ট্যান্ডার্ড অনুসারে, আপনি যদি নাল পয়েন্টারকে অবজ্ঞা করে থাকেন এবং আপনার কোনও নাল পয়েন্টারকে অবজ্ঞা না করে নাল রেফারেন্স তৈরি করতে পারবেন না, সুতরাং নাল রেফারেন্সগুলি কেবল অপরিজ্ঞাত আচরণের ক্ষেত্রের ভিতরেই বিদ্যমান exist যেহেতু আপনার সংকলকটি ধরে নিতে পারে যে আপনি অপরিজ্ঞাত আচরণটি ট্রিগার করছেন না, এটি ধরে নিতে পারে যে নাল রেফারেন্সের মতো কোনও জিনিস নেই (যদিও এটি সহজেই কোড নির্গত করবে যা নাল রেফারেন্স উত্পন্ন করবে!)। যেমনটি এটি if()শর্তটি দেখেছে , উপসংহারে পৌঁছেছে যে এটি সত্য হতে পারে না, এবং কেবল পুরো if()বিবৃতিটি ফেলে দেয় । লিংক সময় অপ্টিমাইজেশনের প্রবর্তনের সাথে, শক্তিশালী উপায়ে নাল রেফারেন্সগুলি পরীক্ষা করা সহজ অসম্ভব হয়ে পড়েছে।


টিএল; ডিআর:

নাল রেফারেন্সগুলি কিছুটা ভয়াবহ অস্তিত্ব:

তাদের অস্তিত্ব অসম্ভব বলে মনে হচ্ছে (= মান অনুসারে)
তবে এগুলি বিদ্যমান (= উত্পন্ন মেশিন কোডের সাহায্যে),
তবে তারা উপস্থিত থাকলে আপনি তাদের দেখতে পাবেন না (= আপনার চেষ্টাগুলি অপ্টিমাইজড হবে)
তবে তারা আপনাকে অজান্তেই হত্যা করতে পারে (= আপনার প্রোগ্রামটি অদ্ভুত পয়েন্টগুলিতে ক্রাশ হয় বা আরও খারাপ)।
আপনার একমাত্র আশা হ'ল তাদের অস্তিত্ব নেই (= আপনার প্রোগ্রামটি তৈরি না করে লিখুন)।

আমি আশা করি যে আপনাকে ঘৃণা করতে আসবে না!


4
"পিং হাতি" আসলে কী?
ফারাপ

4
@ ফারাপ আমার কোন ক্লু নেই, এটি কেবল টাইপো ছিল। তবে সি ++ স্ট্যান্ডার্ডটি গোলাপী বা পিং হাতিগুলি যেভাবেই দেখা দেয় তা বিবেচনা করবে না ;-)
সিমস্টার - মনিকা পুনরায় স্থাপন করুন

9

যদি আপনার উদ্দেশ্যটি সিঙ্গেলটন অবজেক্টের একটি গণনার ক্ষেত্রে নালিকে উপস্থাপন করার কোনও উপায় খুঁজে পাওয়া ছিল, তবে এটি (ডি) রেফারেন্স নাল (এটি সি ++ 11, নাল্পটার) খারাপ ধারণা।

ক্ল্যাটিকের মধ্যে নীলের প্রতিনিধিত্ব করে এমন স্ট্যাটিক সিঙ্গলটন অবজেক্টটি কেন ঘোষণা করবেন না এবং নাল্টটারকে ফেরত দেওয়া একটি কাস্ট-টু-পয়েন্টার অপারেটর যুক্ত করবেন?

সম্পাদনা করুন: বেশ কয়েকটি ভুল টাইপ সংশোধন করেছেন এবং কাস্ট-টু-পয়েন্টার অপারেটরটির জন্য কাজ করার জন্য পরীক্ষার জন্য প্রধান () -তে বিবৃতি যুক্ত হয়েছে (যা আমি ভুলে গেছি .. আমার খারাপ) - মার্চ 10 2015 -

// Error.h
class Error {
public:
  static Error& NOT_FOUND;
  static Error& UNKNOWN;
  static Error& NONE; // singleton object that represents null

public:
  static vector<shared_ptr<Error>> _instances;
  static Error& NewInstance(const string& name, bool isNull = false);

private:
  bool _isNull;
  Error(const string& name, bool isNull = false) : _name(name), _isNull(isNull) {};
  Error() {};
  Error(const Error& src) {};
  Error& operator=(const Error& src) {};

public:
  operator Error*() { return _isNull ? nullptr : this; }
};

// Error.cpp
vector<shared_ptr<Error>> Error::_instances;
Error& Error::NewInstance(const string& name, bool isNull = false)
{
  shared_ptr<Error> pNewInst(new Error(name, isNull)).
  Error::_instances.push_back(pNewInst);
  return *pNewInst.get();
}

Error& Error::NOT_FOUND = Error::NewInstance("NOT_FOUND");
//Error& Error::NOT_FOUND = Error::NewInstance("UNKNOWN"); Edit: fixed
//Error& Error::NOT_FOUND = Error::NewInstance("NONE", true); Edit: fixed
Error& Error::UNKNOWN = Error::NewInstance("UNKNOWN");
Error& Error::NONE = Error::NewInstance("NONE");

// Main.cpp
#include "Error.h"

Error& getError() {
  return Error::UNKNOWN;
}

// Edit: To see the overload of "Error*()" in Error.h actually working
Error& getErrorNone() {
  return Error::NONE;
}

int main(void) {
  if(getError() != Error::NONE) {
    return EXIT_FAILURE;
  }

  // Edit: To see the overload of "Error*()" in Error.h actually working
  if(getErrorNone() != nullptr) {
    return EXIT_FAILURE;
  }
}

কারণ এটি ধীর
ভ্যালালেন

6

ঝনঝন ++ 3.5 এমনকি এটি সম্পর্কে সতর্ক করে:

/tmp/a.C:3:7: warning: reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to
      always evaluate to false [-Wtautological-undefined-compare]
if( & nullReference == 0 ) // null reference
      ^~~~~~~~~~~~~    ~
1 warning generated.

0

হ্যাঁ. ( Https://stackoverflow.com/a/42520025/11714860 এর বিপরীতে , এই বাস্তবায়নটি ইনলাইন-প্রুফ এবং কার্যত অপ্টিমাইজার-প্রুফ এবং এর জন্য একটি স্পষ্ট ফাংশন কলের প্রয়োজন হয় না):

#include <iostream>
#include <functional>

struct null_ref_t {
   template <typename T>
   operator T&() {
      union TypeSafetyBreaker {
         T *ptr;

         // see /programming/38691282/use-of-union-with-reference
         std::reference_wrapper<T> ref = ref; 
      };

      TypeSafetyBreaker ptr = {.ptr = nullptr};

      // unwrap the reference
      return ptr.ref.get();
   }
};

null_ref_t nullref;


int main() {
   int &a = nullref;

   // Segmentation fault
   a = 4;
   return 0;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.