নূরের নতুন সংজ্ঞা দেওয়া হচ্ছে


118

আমি এমন একটি সিস্টেমের জন্য সি কোড লিখছি যেখানে 0x0000 ঠিকানাটি বৈধ এবং এতে I / O পোর্ট রয়েছে। অতএব, কোনও NULL পয়েন্টার অ্যাক্সেস করতে পারে এমন কোনও সম্ভাব্য বাগগুলি সনাক্ত করা যাবে না এবং একই সময়ে বিপজ্জনক আচরণের কারণ হবে।

এই কারণে আমি NUL কে অন্য ঠিকানা হিসাবে নতুন সংজ্ঞা দিতে চাই, উদাহরণস্বরূপ এমন ঠিকানা যা বৈধ নয়। যদি আমি দুর্ঘটনাক্রমে এ জাতীয় ঠিকানা অ্যাক্সেস করি তবে আমি একটি হার্ডওয়্যার বাধা পেয়ে যাব যেখানে আমি ত্রুটিটি পরিচালনা করতে পারি। আমার এই সংকলকের জন্য stddef.h এ অ্যাক্সেস পেয়েছি, তাই আমি আসলে স্ট্যান্ডার্ড শিরোনাম পরিবর্তন করতে এবং NULL পুনরায় সংজ্ঞা দিতে পারি।

আমার প্রশ্ন হ'ল: এই সি স্ট্যান্ডার্ডের সাথে কি বিরোধ হবে? যতদূর আমি স্ট্যান্ডার্ডটিতে 7.17 থেকে বলতে পারি, ম্যাক্রো বাস্তবায়ন-সংজ্ঞায়িত। মানদণ্ডের কোথাও এমন কি আছে যেখানে উল্লেখ করা হচ্ছে যে NUL 0 হতে হবে ?

আর একটি সমস্যা হ'ল প্রচুর সংকলকগণ শূন্যে সবকিছু সেট করে স্থিতিশীল সূচনা করে, তথ্যের ধরণের কোনও ব্যাপার না। যদিও স্ট্যান্ডার্ডটি বলে যে সংকলকটির শূন্য এবং NUL এর দিকে পয়েন্টার সেট করা উচিত। আমি যদি আমার সংকলকের জন্য NULL পুনরায় সংজ্ঞা দিই, তবে আমি জানি যে এই জাতীয় স্থিতি প্রবর্তন ব্যর্থ হবে। আমি নির্ভুলভাবে সংকলক শিরোনাম ম্যানুয়ালি পরিবর্তিত করেও কি আমি এটি ভুল সংকলক আচরণ হিসাবে বিবেচনা করতে পারি? কারণ আমি নিশ্চিত জানি যে স্থির সূচনা করার সময় এই নির্দিষ্ট সংকলকটি NULL ম্যাক্রো অ্যাক্সেস করে না।


3
এই সত্যিই একটি ভাল প্রশ্ন। আপনার কাছে আমার কাছে কোনও উত্তর নেই, তবে আমাকে জিজ্ঞাসা করতে হবে: আপনি কি নিশ্চিত যে আপনার বৈধ জিনিস 0x00 দূরে সরিয়ে নেওয়া সম্ভব নয় এবং "সাধারণ" সিস্টেমের মতো নূন্যতম ঠিকানা হতে দেওয়া উচিত? আপনি না হয়, তাহলে শুধুমাত্র নিরাপদে ব্যবহার করা যায় অবৈধ ঠিকানাগুলি বেশী হবে পারেন, তাহলে আপনি হতে পারেন নিশ্চিত আপনি বরাদ্দ করতে পারেন এবং তারপর mprotectসুরক্ষিত করতে। বা, যদি প্ল্যাটফর্মটির কোনও এএসএলআর বা মত না থাকে তবে প্ল্যাটফর্মের শারীরিক মেমরির বাইরে ঠিকানাগুলি। শুভকামনা।
বোরেলিড

8
আপনার কোড ব্যবহার করা থাকলে এটি কীভাবে কাজ করবে if(ptr) { /* do something on ptr*/ }? যদি NUL 0x0 থেকে পৃথক সংজ্ঞায়িত করা হয় তবে এটি কাজ করবে?
জাভেয়ের টি।

3
সি পয়েন্টারের মেমরি ঠিকানার সাথে কোনও জোরপূর্বক সম্পর্ক নেই। যতক্ষণ পয়েন্টার গাণিতিকের নিয়ম মেনে চলবে ততক্ষণ পয়েন্টার মান কোনও কিছু হতে পারে। বেশিরভাগ বাস্তবায়ন মেমরির ঠিকানাগুলিকে পয়েন্টার মান হিসাবে ব্যবহার করতে পছন্দ করে তবে তারা যতক্ষণ না এটি আইসোমরফিজম হিসাবে কিছু ব্যবহার করতে পারে।
ডেটনওল্ফ

2
@ বিডনলান এটি মিশ্র-সি-তেও (পরামর্শমূলক) বিধি লঙ্ঘন করবে।
লুন্ডিন

2
@ আন্ড্রেস হ্যাঁ এটাই আমার ধারণা। হার্ডওয়্যার লোকেদের এমন সফ্টওয়্যারটি চালিত হওয়া হার্ডওয়্যার ডিজাইনের অনুমতি দেওয়া উচিত নয়! :)
লন্ডিন

উত্তর:


84

সি স্ট্যান্ডার্ডটির জন্য মেশিনের ঠিকানা শূন্যের নাল পয়েন্টার থাকা দরকার না। তবুও, 0একটি পয়েন্টার মানের কাছে ধ্রুবক NULLcastালাইয়ের ফলে অবশ্যই একটি পয়েন্টার (§6.3.2.3 / 3) হওয়া উচিত এবং নুল পয়েন্টারটিকে বুলিয়ান হিসাবে মূল্যায়ন করা অবশ্যই মিথ্যা হতে পারে। এটি কিছুক্ষণ বিশ্রী যদি সত্যিই হতে পারে না একটি শূন্য ঠিকানা চান, এবং NULLশূন্য ঠিকানা নয়।

তবুও, সংকলক এবং স্ট্যান্ডার্ড লাইব্রেরিতে (ভারী) পরিবর্তনগুলি সহ, স্ট্যান্ডার্ড লাইব্রেরিতে NULLএখনও কঠোরভাবে অনুসারী থাকা অবস্থায় বিকল্প বিট প্যাটার্ন দ্বারা প্রতিনিধিত্ব করা অসম্ভব নয় । এটা না কেবল সংজ্ঞা পরিবর্তন করতে যথেষ্ট NULLযেমন তারপর অবশ্য নিজেই NULLসত্যতে মূল্যায়ন করবেন।

বিশেষত, আপনার প্রয়োজন:

  • পয়েন্টারগুলিতে অ্যাসাইনমেন্টে আক্ষরিক শূন্যগুলির জন্য বিন্যাস করুন (বা পয়েন্টারগুলিতে কাস্ট করুন) যেমন কিছু অন্য যাদু মান হিসাবে রূপান্তরিত করতে -1
  • 0পরিবর্তে ম্যাজিক মান পরীক্ষা করতে পয়েন্টার এবং ধ্রুবক পূর্ণসংখ্যার মধ্যে সমতা পরীক্ষার ব্যবস্থা করুন (.5§.§.৯ /))
  • শূন্যের জন্য যাচাই করার পরিবর্তে যাদু মানটির সাম্যতা পরীক্ষা করার জন্য বুলিয়ান হিসাবে একটি পয়েন্টার ধরণের মূল্যায়ন করা হয় এমন সমস্ত প্রসঙ্গে বিন্যাস করুন। এটি সাম্যতা পরীক্ষার শব্দার্থকগুলি থেকে অনুসরণ করে তবে সংকলকটি অভ্যন্তরীণভাবে এটি ভিন্নভাবে প্রয়োগ করতে পারে। §6.5.13 / 3, §6.5.14 / 3, §6.5.15 / 4, §6.5.3.3 / 5, §6.8.4.1 / 2, .86.8.5 / 4 দেখুন
  • ক্যাফে হিসাবে উল্লেখ করা হয়েছে, নতুন নাল পয়েন্টার উপস্থাপনা প্রতিবিম্বিত করতে স্ট্যাটিক অবজেক্টস (§6.7.8 / 10) এবং আংশিক যৌগিক প্রারম্ভক (.76.7.8 / 21) আরম্ভের জন্য শব্দার্থকে আপডেট করুন।
  • সত্য ঠিকানা শূন্য অ্যাক্সেস করার জন্য একটি বিকল্প উপায় তৈরি করুন।

কিছু জিনিস আপনার পরিচালনা করতে হবে না । উদাহরণ স্বরূপ:

int x = 0;
void *p = (void*)x;

এর পরে, pনাল পয়েন্টার হওয়ার নিশ্চয়তা নেই। কেবল ধ্রুবক অ্যাসাইনমেন্টগুলি পরিচালনা করা প্রয়োজন (এটি সত্যিকারের শূন্যের অ্যাক্সেসের জন্য একটি ভাল পদ্ধতির)। অনুরূপভাবে:

int x = 0;
assert(x == (void*)0); // CAN BE FALSE

এছাড়াও:

void *p = NULL;
int x = (int)p;

xহতে গ্যারান্টিযুক্ত হয় না 0

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

পার্শ্ব নোট হিসাবে, সংকলক যথাযথ হওয়ার আগে উত্স কোড রূপান্তর পর্যায়ে এই পরিবর্তনগুলি প্রয়োগ করা সম্ভব। এটি হ'ল প্রিপ্রোসেসর -> সংকলক -> এসেইম্বেলার -> লিংকারের স্বাভাবিক প্রবাহের পরিবর্তে আপনি একটি প্রিপ্রসেসর -> নুল ট্রান্সফর্মেশন -> সংকলক -> এস্যাসেবলার -> লিংক যুক্ত করতে পারেন। তারপরে আপনি যেমন রূপান্তর করতে পারেন:

p = 0;
if (p) { ... }
/* becomes */
p = (void*)-1;
if ((void*)(p) != (void*)(-1)) { ... }

এর জন্য একটি সম্পূর্ণ সি পার্সার প্রয়োজন হবে, পাশাপাশি টাইপডেফস এবং ভেরিয়েবলের ঘোষণার বিশ্লেষণ করতে হবে যা চিহ্নিতকারীদের সাথে সম্পর্কিত কিনা তা নির্ধারণ করতে। তবে এটি করে আপনি সংকলকটির কোড জেনারেশনের অংশগুলিতে যথাযথ পরিবর্তন করতে পারবেন না। ঝাঁকুনি এটি বাস্তবায়নের জন্য দরকারী হতে পারে - আমি বুঝতে পারি যে এটি এই জাতীয় রূপান্তরকে সামনে রেখে ডিজাইন করা হয়েছিল। আপনার সম্ভবত অবশ্যই স্ট্যান্ডার্ড লাইব্রেরিতে অবশ্যই পরিবর্তন করা দরকার।


2
ঠিক আছে আমি .3§.৩.২.৩ তে পাঠ্যটি খুঁজে পাইনি, তবে আমি সন্দেহ করেছি যে কোথাও এ জাতীয় বক্তব্য থাকবে :)। আমি আমার প্রশ্নের উত্তরটি অনুমান করি, মান অনুসারে আমাকে NUL পুনরায় সংজ্ঞায়িত করার অনুমতি দেওয়া হয় না যদি না আমি ব্যাক আপ করার জন্য একটি নতুন সি সংকলক লেখার কল্পনা করি :)
লন্ডিন

2
একটি ভাল কৌশলটি হ'ল কম্পাইলারটি হ্যাক করা যাতে পয়েন্টারটি <-> পূর্ণসংখ্যার রূপান্তরগুলি এক্সওর একটি নির্দিষ্ট মান যা একটি অবৈধ পয়েন্টার এবং এখনও যথেষ্ট তুচ্ছ যে লক্ষ্য আর্কিটেকচারটি সস্তায় এটি করতে পারে (সাধারণত, এটি একটি একক বিট সেট সহ একটি মান হতে পারে) যেমন 0x20000000)।
সাইমন রিখটার

2
সংকলকটিতে আপনাকে আর একটি জিনিস বদলাতে হবে যা হ'ল যৌগিক প্রকারের সাথে অবজেক্টের সূচনা - যদি কোনও বস্তু আংশিকভাবে আরম্ভ করা হয় তবে যে কোনও পয়েন্টার যার জন্য সুস্পষ্ট ইনিটালাইজার উপস্থিত নেই তা অবশ্যই আরম্ভ করা উচিত NULL
ক্যাফে

20

স্ট্যান্ডার্ডটি উল্লেখ করে যে মান 0 সহ একটি পূর্ণসংখ্যক ধ্রুবক প্রকাশ বা এই void *ধরণের রূপকে ধরণে রূপান্তরিত করে একটি নাল পয়েন্টার ধ্রুবক। এর অর্থ এটি (void *)0সর্বদা নাল পয়েন্টার, তবে দেওয়া int i = 0;, হওয়ার (void *)iদরকার নেই।

সি বাস্তবায়নে এর শিরোনাম সহ সংকলকটি থাকে। আপনি যদি পুনরায় সংজ্ঞায়িত করতে শিরোনামগুলি সংশোধন করেন NULLতবে স্থির সূচনাটি সংশোধন করার জন্য সংকলকটি সংশোধন করেন না, তবে আপনি একটি অ-মাপदार বাস্তবায়ন তৈরি করেছেন। এটি সম্পূর্ণ বাস্তবায়ন একসাথে নেওয়া হয়েছে যা ভুল আচরণ করেছে এবং যদি আপনি এটি ভেঙে দেন তবে সত্যিকার অর্থে আপনার দোষ দেওয়ার কেউ নেই;)

আপনি শুধু স্ট্যাটিক initialisations চেয়ে বেশি ঠিক নয়, অবশ্যই - একটি পয়েন্টার দেওয়া p, if (p)সমতুল্য হয় if (p != NULL), উপরোক্ত নিয়মের কারণে।


8

যদি আপনি সি স্ট্যান্ডের লাইব্রেরি ব্যবহার করেন তবে আপনি এমন ফাংশনগুলির সাথে সমস্যায় পড়বেন যা NULL ফিরিয়ে দিতে পারে। উদাহরণস্বরূপ malloc ডকুমেন্টেশন বলে:

যদি ফাংশনটি অনুরোধকৃত মেমরির ব্লক বরাদ্দ করতে ব্যর্থ হয়, তবে একটি নাল পয়েন্টারটি ফিরে আসবে।

যেহেতু মলোক এবং সম্পর্কিত ফাংশনগুলি একটি নির্দিষ্ট ন্যূনাল মান সহ বাইনারিগুলিতে ইতিমধ্যে সংকলিত হয়েছে, আপনি যদি NULL পুনঃনির্ধারণ করেন, আপনি সি স্টাড লাইবস সহ আপনার পুরো সরঞ্জাম চেইনটি পুনর্নির্মাণ করতে না পারলে আপনি সরাসরি সি স্ট্যান্ড লাইব্রেরি ব্যবহার করতে পারবেন না।

এছাড়াও স্টাড লাইব্রেরির NULL ব্যবহারের কারণে, আপনি যদি std শিরোনাম অন্তর্ভুক্ত করার আগে NULL পুনরায় সংজ্ঞায়িত করেন তবে আপনি শিরোনামে তালিকাভুক্ত NULL সংজ্ঞায়িত করতে পারেন rite অন্তর্নিহিত যে কোনও কিছুই সংকলিত বস্তুগুলি থেকে বেমানান।

পরিবর্তে আমি আপনার নিজের ব্যবহারের জন্য আপনার নিজস্ব NULL, "MYPRODUCT_NULL" সংজ্ঞায়িত করব এবং হয় এফসিডি থেকে সি স্টাড লাইব্রেরিতে / এড়িয়ে বা অনুবাদ করব।


6

NULL একা ছেড়ে আইও কে 0x0000 কে একটি বিশেষ কেস হিসাবে পোর্ট করতে ট্রিট করুন, সম্ভবত এসেম্ব্লারারে লিখিত একটি রুটিন ব্যবহার করে এবং এটি স্ট্যান্ডার্ড সি শব্দার্থবিজ্ঞানের সাপেক্ষে নয়। IOW, NUL পুনরায় সংজ্ঞায়িত করবেন না, পোর্ট 0x00000 পুনরায় সংজ্ঞা দিন।

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


সমস্যাটি তখনই উত্থাপিত হবে যখন দুর্ঘটনাক্রমে NULL অ্যাক্সেস করা হবে, যখন বন্দরটি ইচ্ছাকৃতভাবে অ্যাক্সেস করা হয় না। কেন আমি তার জন্য বন্দর I / O পুনরায় সংজ্ঞায়িত করব? এটি ইতিমধ্যে এটি হিসাবে কাজ করা হয়।
লুন্ডিন

2
@ লন্ডিন দুর্ঘটনাক্রমে বা না, NUL কেবলমাত্র একটি সি প্রোগ্রাম ব্যবহার করেই ব্যবহার করা যেতে পারে *p, p[]বা p(), তাই সংকলকটির কেবল IO পোর্ট 0x0000 রক্ষা করার জন্য তাদের যত্ন নেওয়া দরকার।
অপালালা

@ লন্ডিন আপনার প্রশ্নের দ্বিতীয় অংশ: একবার আপনি সি এর মধ্যে থেকে শূন্যের প্রবেশাধিকারকে সীমাবদ্ধ করে ফেললে আপনার 0x0000 বন্দর পৌঁছানোর জন্য অন্য উপায় প্রয়োজন। এসেম্বলারের লিখিত একটি ফাংশন এটি করতে পারে। সি এর মধ্যে থেকে বন্দরটি 0xFFFF বা যাই হোক না কেন ম্যাপ করা যেতে পারে তবে কোনও ফাংশন ব্যবহার করা এবং পোর্ট নম্বরটি ভুলে যাওয়া ভাল।
অপালালা

3

অন্যদের দ্বারা উল্লিখিত হিসাবে NUL পুনঃনির্ধারণের ক্ষেত্রে চূড়ান্ত অসুবিধা বিবেচনা করে, সুপরিচিত হার্ডওয়্যার অ্যাড্রেসগুলির জন্য ডিফেরেন্সিং পুনরায় সংজ্ঞায়িত করা আরও সহজ । কোনও ঠিকানা তৈরি করার সময়, প্রতিটি সুপরিচিত ঠিকানাতে 1 টি যুক্ত করুন, যাতে আপনার সুপরিচিত IO বন্দরটি হবে:

  #define CREATE_HW_ADDR(x)(x+1)
  #define DEREFERENCE_HW_ADDR(x)(*(x-1))

  int* wellKnownIoPort = CREATE_HW_ADDR(0x00000000);

  printf("IoPortIs" DEREFERENCE_HW_ADDR(wellKnownIoPort));

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

  if (pointer)
  {
     ...
  }

এখনও কাজ

ক্রেজি আমি জানি, তবে কেবল ভাবলাম আমি এই ধারণাটি বাইরে ফেলে দেব :)।


সমস্যাটি তখনই উত্থাপিত হবে যখন দুর্ঘটনাক্রমে NULL অ্যাক্সেস করা হবে, যখন বন্দরটি ইচ্ছাকৃতভাবে অ্যাক্সেস করা হয় না। কেন আমি তার জন্য বন্দর I / O পুনরায় সংজ্ঞায়িত করব? এটি ইতিমধ্যে এটি হিসাবে কাজ করা হয়।
লুন্ডিন

@ লন্ডইন আমি অনুমান করি যে আপনাকে আরও বেদনাদায়ক চয়ন করতে হবে, সম্পূর্ণ সরঞ্জামচেনটি পুনর্নির্মাণ করা টুইটারে করা বা আপনার কোডের এই একটি অংশ পরিবর্তন করা।
ডগ টি।

2

নাল পয়েন্টারটির জন্য বিট প্যাটার্নটি পূর্ণসংখ্যার 0 এর বিট প্যাটার্নের মতো নাও হতে পারে But তবে ন্যূনল ম্যাক্রোর সম্প্রসারণ অবশ্যই নাল পয়েন্টার ধ্রুবক হতে হবে, এটি মান 0 এর একটি ধ্রুবক পূর্ণসংখ্যা যা কাস্ট করা যেতে পারে (অকার্যকর) *)।

সঙ্গতিপূর্ণ থাকার সময় আপনি যে ফলাফলটি চান তা অর্জন করতে আপনাকে আপনার সরঞ্জাম চেনটি পরিবর্তন করতে হবে (বা সম্ভবত কনফিগার করতে হবে) তবে এটি অর্জনযোগ্য achie


1

আপনি ঝামেলা চাইছেন। NULLএকটি নন-নাল মানটির পুনরায় সংজ্ঞা দেওয়া এই কোডটি ভেঙে দেবে:

   যদি (মাইপয়েন্টার)
   {
      // মাইপয়েন্টার নাল নয়
      ...
   }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.