স্ট্রাক্টে সূচি দেওয়া কি আইনী?


104

কোডটি কীভাবে 'খারাপ' হোক না কেন, এবং ধারনা করা যে সংযোজন / সংকলন / প্ল্যাটফর্মের ক্ষেত্রে প্রান্তিককরণ ইত্যাদি কোনও সমস্যা নয়, তা কি এই অনির্ধারিত বা ভাঙা আচরণ?

আমার যদি এর মতো কাঠামো থাকে: -

struct data
{
    int a, b, c;
};

struct data thing;

এটা কি আইনি অ্যাক্সেস করতে a, bএবং cযেমন (&thing.a)[0], (&thing.a)[1]এবং (&thing.a)[2]?

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

এটি আমার কোড নয়; এটির কোডটি আমার সাথে কাজ করতে হবে, আমি এটির খারাপ কোড বা ভাঙা কোড কিনা সে বিষয়ে আগ্রহী কারণ ভিন্ন ভিন্ন পরিবর্তনগুলিতে এটি আমার অগ্রাধিকারকে প্রভাবিত করে :)

সি এবং সি ++ ট্যাগ করেছেন। আমি বেশিরভাগ সি ++ তেও আগ্রহী, তবে সি এর তুলনায় এটি আলাদা, কেবল আগ্রহের জন্য।


51
না, এটি "আইনী" নয়। এটি অনির্ধারিত আচরণ।
স্যাম বর্ষাচিক

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

7
অতীতটি খনন করা - ইউবি হ'ল নিক নামযুক্ত অনুনাসিক ডেমন
অ্যাড্রিয়ান কলমিচি

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

7
আপনার যদি অ্যারে হিসাবে উপাদানগুলি অ্যাক্সেস করার প্রয়োজন হয় তবে এটিকে অ্যারে হিসাবে সংজ্ঞায়িত করুন। তাদের যদি আলাদা আলাদা নাম প্রয়োজন হয় তবে নামটি ব্যবহার করুন। আপনার কেক রাখতে এবং এটি খাওয়ার চেষ্টা করা অবশেষে বদহজম হতে পারে - সম্ভবত সবচেয়ে অসুবিধাগ্রস্ত কল্পনা করার সময়। (আমি মনে করি সূচক 0 তে সি আইনী; সূচক 1 বা 2 নয় There এমন একটি প্রসঙ্গ রয়েছে যাতে একক উপাদানকে 1 মাপের অ্যারে হিসাবে বিবেচনা করা হয়))
জোনাথন লেফলার

উত্তর:


73

এটি অবৈধ 1 । এটি সি ++ এ একটি অপরিজ্ঞাত আচরণ।

আপনি সদস্যদের একটি অ্যারে ফ্যাশনে নিয়ে যাচ্ছেন, তবে এখানে সি ++ স্ট্যান্ডার্ড যা বলেছে (জোর আমার):

[dcl.array / 1] : ... এরে প্রকারের একটি বস্তু একটি রয়েছে contiguously বরাদ্দ টাইপ টি এন subobjects অ-খালি সেট করুন ...

তবে, সদস্যদের জন্য, এর মতো কোনও স্বতন্ত্র প্রয়োজনীয়তা নেই:

[class.mem / 17] : ...; বাস্তবায়ন প্রান্তিককরণ প্রয়োজনীয়তা দুটি সংলগ্ন সদস্যদের একে অপরের সাথে সাথে বরাদ্দ না করার কারণ হতে পারে ...

উপরোক্ত দুটি উক্তিগুলি ইঙ্গিত করার জন্য যথেষ্ট হতে হবে যে আপনি structসি ++ মানক দ্বারা নির্ধারিত আচরণ নয় বলে সূচিত করার জন্য কেন একটি উদাহরণ বেছে নেওয়া যাক: অভিব্যক্তিটি দেখুন (&thing.a)[2]- সাবস্ক্রিপ্ট অপারেটর সম্পর্কিত:

[expr.post//expr.sub/1] : বর্গক্ষেত্র বন্ধনীর সাথে একটি প্রকাশের পরে একটি পোস্টফিক্স এক্সপ্রেশন একটি পোস্টফিক্স এক্সপ্রেশন। এক্সপ্রেশনগুলির মধ্যে একটি হ'ল "টির অ্যারে" টাইপের একটি গ্লু বা "পয়েন্টার টু টি" টাইপের একটি মূল্য হবে এবং অন্যটি অনির্বাচিত গণনা বা ইন্টিগ্রাল টাইপের মূল্য হবে। ফলাফলটি "টি" টাইপের হয়। "টি" টাইপটি একটি সম্পূর্ণ সংজ্ঞায়িত অবজেক্ট টাইপ হবে be6 এক্সপ্রেশনটি E1[E2]অভিন্ন (সংজ্ঞায়িত) থেকে((E1)+(E2))

উপরের উদ্ধৃতিটির গা bold় পাঠ্যের মধ্যে খনন: একটি পয়েন্টার টাইপের সাথে একটি অবিচ্ছেদ্য প্রকার যোগ করার বিষয়ে (এখানে জোরটি নোট করুন) ..

[expr.add / 4] : যখন অবিচ্ছেদ্য টাইপযুক্ত একটি এক্সপ্রেশনটি একটি পয়েন্টার থেকে যুক্ত বা বিয়োগ করা হয়, তখন ফলাফলটি পয়েন্টার অপারেন্ডের ধরণের হয়। তাহলে অভিব্যক্তিPউপাদানে পয়েন্টx[i]এর একটি অ্যারের বস্তুx এন উপাদান, এক্সপ্রেশন সঙ্গেP + JএবংJ + P(যেখানেJমূল্য আছেj) (সম্ভবত-প্রকল্পিত) উপাদানে বিন্দুx[i + j] যদি0 ≤ i + j ≤ n; অন্যথায় , আচরণটি সংজ্ঞায়িত। ...

যদি ক্লজটির জন্য অ্যারের প্রয়োজনীয়তাটি নোট করুন ; অন্য অন্যথায় উপরে উদ্ধৃতি। অভিব্যক্তি স্পষ্টতই যদি এই ধারাটির জন্য যোগ্যতা অর্জন করে না ; অতএব, অপরিবর্তিত আচরণ।(&thing.a)[2]


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

কিছু সার্থক ওয়ার্কআরউন্ডস (সংজ্ঞায়িত আচরণ সহ) অন্যান্য উত্তর দ্বারা সরবরাহ করা হয়েছে।



মন্তব্যে যথাযথভাবে নির্দেশিত হিসাবে, [বেসিক.লভাল / 8] , যা আমার আগের সম্পাদনায় ছিল তা প্রয়োগ হয় না। ধন্যবাদ @ 2501 এবং @ মিমি

1 : একমাত্র আইনী মামলার জন্য @ ব্যারি এই প্রশ্নের উত্তরটি দেখুন যেখানে আপনি thing.aএই অংশটির মাধ্যমে কাঠামোর সদস্য অ্যাক্সেস করতে পারবেন ।


1
@jcoder এটা সংজ্ঞায়িত করা হয় class.mem । প্রকৃত পাঠ্যের জন্য শেষ অনুচ্ছেদটি দেখুন।
নাথান অলিভার

4
কঠোর এলিজিং এখানে প্রাসঙ্গিক নয়। টাইপ ইন্ট সমষ্টিগত টাইপের মধ্যে অন্তর্ভুক্ত থাকে এবং এই প্রকারটি উপন্যাসের পূর্ববর্তী হতে পারে। - an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
2501

1
@ নীচু নাগরিকগণ, মন্তব্য করতে চান? - এবং উন্নতি করতে বা নির্দেশ করতে কোথায় এই উত্তরটি ভুল?
WhiZTiM

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

1
সম্পন্ন! আমি কঠোর-আলিয়াজিং সম্পর্কে অনুচ্ছেদটি সরিয়েছি।
WhiZTiM

48

নং সি তে, প্যাডিং না থাকলেও এটি অনির্ধারিত আচরণ।

অনির্ধারিত আচরণের কারণ হিসাবে জিনিসটি সীমার অ্যাক্সেস 1 । যখন আপনার কোনও স্কেলার থাকে (কাঠামোর সদস্যগণ এ, বি, সি) এবং পরবর্তী অনুমানক উপাদানটি অ্যাক্সেস করতে অ্যারে 2 হিসাবে এটি ব্যবহার করার চেষ্টা করেন , আপনি একই ধরণের অন্য কোনও বস্তুতে ঘটে গেলেও আপনি অপরিজ্ঞাত আচরণের কারণ হয়ে দাঁড়ান যে ঠিকানা।

তবে আপনি স্ট্রাক্ট অবজেক্টের ঠিকানা ব্যবহার করতে পারেন এবং অফসেটটি নির্দিষ্ট সদস্য হিসাবে গণনা করতে পারেন:

struct data thing = { 0 };
char* p = ( char* )&thing + offsetof( thing , b );
int* b = ( int* )p;
*b = 123;
assert( thing.b == 123 );

এটি প্রতিটি সদস্যের জন্য স্বতন্ত্রভাবে সম্পন্ন করতে হবে তবে এটিকে একটি ফাংশনে রাখা যেতে পারে যা অ্যারে অ্যাক্সেসের অনুরূপ bles


1 (এর থেকে উদ্ধৃত: আইএসও / আইইসি 9899: 201x 6.5.6 অ্যাডিটিভ অপারেটর 8)
ফলাফল যদি অ্যারে অবজেক্টের শেষ উপাদানটির একটিটিকে চিহ্নিত করে , তবে এটি মূল্যায়ন করা একটি অ্যানারি * অপারেটরের ক্রিয়াকলাপ হিসাবে ব্যবহৃত হবে না।

2 (এর থেকে উদ্ধৃত: আইএসও / আইইসি 9899: 201x 6.5.6 অ্যাডিটিভ অপারেটর 7)
এই অপারেটরগুলির উদ্দেশ্যে, কোনও বস্তুর পয়েন্টার যা অ্যারের উপাদান নয়, এটির প্রথম উপাদানটির পয়েন্টার হিসাবে একই আচরণ করে উপাদানটির ধরণ হিসাবে বস্তুর প্রকারের সাথে এক দৈর্ঘ্যের অ্যারে


3
ক্লাসটি একটি মান বিন্যাসের ধরণ হলে কেবল এটি কাজ করে মনে রাখবেন। তা না হলে এখনও ইউবি হয়।
নাথান অলিভার

@ নাথান অলিভার আমার উল্লেখ করা উচিত যে আমার উত্তরটি কেবল সি সম্পাদিত ক্ষেত্রে প্রযোজ্য। এটি দ্বৈত ট্যাগ ভাষার প্রশ্নগুলির অন্যতম সমস্যা।
2501

ধন্যবাদ, এবং সে কারণেই আমি পৃথকভাবে C ++ এবং C এর জন্য আলাদাভাবে জিজ্ঞাসা করেছি কারণ পার্থক্যগুলি জানার জন্য এটি আগ্রহী
jcoder

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

আপনি কি বলবেন যে এটি অপরিবর্তিত char* p = ( char* )&thing.a + offsetof( thing , b );আচরণের দিকে পরিচালিত করে?
এমএম

43

সি ++ এ আপনার যদি সত্যিই এটির প্রয়োজন হয় - অপারেটর তৈরি করুন []:

struct data
{
    int a, b, c;
    int &operator[]( size_t idx ) {
        switch( idx ) {
            case 0 : return a;
            case 1 : return b;
            case 2 : return c;
            default: throw std::runtime_error( "bad index" );
        }
    }
};


data d;
d[0] = 123; // assign 123 to data.a

এটি কেবল কাজ করার গ্যারান্টিযুক্তই নয় তবে ব্যবহারও সহজ, আপনার অপঠনযোগ্য এক্সপ্রেশন লিখতে হবে না (&thing.a)[0]

দ্রষ্টব্য: এই উত্তরটি অনুমান হিসাবে দেওয়া হয়েছে যে আপনার ইতিমধ্যে ক্ষেত্রগুলি নিয়ে একটি কাঠামো রয়েছে এবং আপনাকে সূচকের মাধ্যমে অ্যাক্সেস যুক্ত করতে হবে। গতি যদি সমস্যা হয় এবং আপনি কাঠামো পরিবর্তন করতে পারেন এটি আরও কার্যকর হতে পারে:

struct data 
{
     int array[3];
     int &a = array[0];
     int &b = array[1];
     int &c = array[2];
};

এই সমাধানটি কাঠামোর আকার পরিবর্তন করবে যাতে আপনি পদ্ধতিগুলিও ব্যবহার করতে পারেন:

struct data 
{
     int array[3];
     int &a() { return array[0]; }
     int &b() { return array[1]; }
     int &c() { return array[2]; }
};

1
আমি এর বিচ্ছিন্নতা দেখতে, টাইপ পেনিং ব্যবহার করে একটি সি প্রোগ্রামের ডিসঅাসেপসেস বনাম দেখতে চাই। তবে, তবে ... সি ++ সি হিসাবে দ্রুত ... ঠিক আছে? রাইট?
লুন্ডিন

6
@ লন্ডিন যদি আপনি এই নির্মাণের গতির বিষয়ে চিন্তা করেন তবে ডেটা পৃথক ক্ষেত্র হিসাবে নয় প্রথমে অ্যারে হিসাবে সাজানো উচিত।
স্লভা

2
@ লন্ডিন আপনি উভয়ই অপঠিত এবং অপরিজ্ঞাত আচরণের অর্থ? না ধন্যবাদ.
স্লাভা

1
@ লন্ডিন অপারেটর ওভারলোডিং একটি কম্পাইল-টাইম সিনট্যাকটিক বৈশিষ্ট্য যা সাধারণ ক্রিয়াকলাপের তুলনায় কোনও ওভারহেড প্ররোচিত করে না। গডবোল্ট.আর্গ / জি / ভিকিআরইজে একবার দেখুন , যখন সি ++ এবং সি কোডটি সংকলন করে তখন সংকলক আসলে কী করে। তারা আশ্চর্যজনক যে তারা কী করে এবং কেউ তাদের কাছ থেকে কী প্রত্যাশা করে। আমি ব্যক্তিগতভাবে আরও ভাল টাইপ-সুরক্ষা এবং সি ++ এর চেয়ে বেশি লক্ষ লক্ষ বার সি এর চেয়ে বেশি পছন্দ করি। এবং এটি প্যাডিং সম্পর্কে অনুমানের উপর নির্ভর না করে সর্বদা কাজ করে।
জেনস

2
এই উল্লেখগুলি কমপক্ষে জিনিসটির আকার দ্বিগুণ করবে। শুধু কর thing.a()
টিসি

14

সি ++ এর জন্য: যদি কোনও সদস্যের নাম না জেনে আপনার অ্যাক্সেসের প্রয়োজন হয় তবে আপনি সদস্য ভেরিয়েবলের জন্য একটি পয়েন্টার ব্যবহার করতে পারেন।

struct data {
  int a, b, c;
};

typedef int data::* data_int_ptr;

data_int_ptr arr[] = {&data::a, &data::b, &data::c};

data thing;
thing.*arr[0] = 123;

1
এটি ভাষা সুবিধাগুলি ব্যবহার করছে, এবং ফলস্বরূপ সুস্পষ্টভাবে সংজ্ঞায়িত হয়েছে এবং যেমন আমি অনুমান করি, দক্ষ। সর্বোত্তম উত্তর.
পিটার - মনিকা

2
দক্ষ ধরে? আমি বিপরীত ধরে। দেখুন কোডটি করেন।
জেডুগোস্জ

1
@ জেডিগুগোস, আপনি ঠিক বলেছেন। উত্পন্ন সমাবেশে উঁকিoffsetoff
মারলে

3
আপনি আরআর কনটেক্সারপ তৈরি করে জিনিসগুলিও উন্নত করতে পারেন। এটি ফ্লাইতে তৈরি করার পরিবর্তে ডেটা বিভাগে একটি একক স্থির লুক টেবিল তৈরি করবে।
টিম

10

আইএসও সি 99 / সি 11-এ, ইউনিয়ন ভিত্তিক টাইপ-পানিং আইনী, সুতরাং আপনি এটি অ-অ্যারেগুলিতে পয়েন্টারগুলি সূচির পরিবর্তে ব্যবহার করতে পারেন (অন্যান্য বিভিন্ন উত্তর দেখুন)।

আইএসও সি ++ ইউনিয়ন-ভিত্তিক টাইপ-পেনিংয়ের অনুমতি দেয় না। GNU C ++ একটি এক্সটেনশান হিসাবে করে এবং আমি মনে করি যে সাধারণভাবে GNU এক্সটেনশানগুলিকে সমর্থন করে না এমন কিছু অন্যান্য সংকলক ইউনিয়ন টাইপ-পেনিং সমর্থন করে। তবে এটি আপনাকে কঠোরভাবে পোর্টেবল কোড লিখতে সহায়তা করে না।

জিসিসি এবং ক্ল্যাংয়ের বর্তমান সংস্করণগুলির সাথে, সদস্য switch(idx)নির্বাচন করার জন্য একটি সি ++ সদস্য ফাংশন লিখে সংকলন-সময় ধ্রুবক সূচকগুলির জন্য অপ্টিমাইজ করা হবে, তবে রানটাইম সূচকগুলির জন্য ভয়ঙ্কর শাখা প্রশাখা তৈরি করবে। এর switch()জন্য সহজাত কোনও ভুল নেই ; এটি কেবল বর্তমান সংকলকগুলির মধ্যে একটি মিস-অপ্টিমাইজেশন বাগ। তারা স্লাভা 'সুইচ () ফাংশনটি দক্ষতার সাথে সংকলন করতে পারে।


এর সমাধান / কার্যপ্রণালীটি এটি অন্যভাবে করা: আপনার শ্রেণি / কাঠামোকে একটি অ্যারে সদস্য দিন এবং নির্দিষ্ট উপাদানের সাথে নাম সংযুক্ত করতে এক্সেসর ফাংশন লিখুন।

struct array_data
{
  int arr[3];

  int &operator[]( unsigned idx ) {
      // assert(idx <= 2);
      //idx = (idx > 2) ? 2 : idx;
      return arr[idx];
  }
  int &a(){ return arr[0]; } // TODO: const versions
  int &b(){ return arr[1]; }
  int &c(){ return arr[2]; }
};

গডবোল্ট সংকলক এক্সপ্লোরারটিতে আমরা বিভিন্ন ব্যবহারের ক্ষেত্রে asm আউটপুট দেখতে পারি । এগুলি হ'ল সম্পূর্ণ x86-64 সিস্টেম ভি ফাংশন, এর পিছনে থাকা আরইটি নির্দেশাবলী বাদ দেওয়া হয়েছে যখন তারা ইনলাইন করলে আপনি কী পাবেন show এআরএম / এমআইপিএস / যা কিছু অনুরূপ হবে।

# asm from g++6.2 -O3
int getb(array_data &d) { return d.b(); }
    mov     eax, DWORD PTR [rdi+4]

void setc(array_data &d, int val) { d.c() = val; }
    mov     DWORD PTR [rdi+8], esi

int getidx(array_data &d, int idx) { return d[idx]; }
    mov     esi, esi                   # zero-extend to 64-bit
    mov     eax, DWORD PTR [rdi+rsi*4]

তুলনা করে, @ স্লাভা উত্তর switch()সি ++ এর জন্য ব্যবহার করে রানটাইম-ভেরিয়েবল সূচকটির জন্য এএসএম তৈরি করে। (পূর্ববর্তী গডবোল্ট লিঙ্কে কোড)।

int cpp(data *d, int idx) {
    return (*d)[idx];
}

    # gcc6.2 -O3, using `default: __builtin_unreachable()` to promise the compiler that idx=0..2,
    # avoiding an extra cmov for idx=min(idx,2), or an extra branch to a throw, or whatever
    cmp     esi, 1
    je      .L6
    cmp     esi, 2
    je      .L7
    mov     eax, DWORD PTR [rdi]
    ret
.L6:
    mov     eax, DWORD PTR [rdi+4]
    ret
.L7:
    mov     eax, DWORD PTR [rdi+8]
    ret

এটি সি (বা জিএনইউ সি ++) ইউনিয়ন ভিত্তিক ধরণের শাস্তি সংস্করণের সাথে তুলনা করে স্পষ্টতই ভয়াবহ:

c(type_t*, int):
    movsx   rsi, esi                   # sign-extend this time, since I didn't change idx to unsigned here
    mov     eax, DWORD PTR [rdi+rsi*4]

@ এমএম: ভাল কথা। এটি বিভিন্ন মন্তব্যের উত্তর এবং স্লাভা জবাবের বিকল্প more আমি উদ্বোধনী বিটটি আবার শব্দ করেছি, তাই এটি অন্ততপক্ষে মূল প্রশ্নের উত্তর হিসাবে শুরু হবে। যে ইশারা জন্য ধন্যবাদ।
পিটার কর্ডেস

ব্যবহার করার সময় ইউনিয়ন ভিত্তিক টাইপ punning জিসিসি এবং ঝনঝন কাজ বলে মনে হয় যদিও []একটি ইউপি সদস্য সরাসরি অপারেটর স্ট্যান্ডার্ড সংজ্ঞায়িত array[index]সমতূল্য হিসেবে *((array)+(index)), এবং কেউই জিসিসি কিংবা ঝনঝন নির্ভরযোগ্যভাবে চিনতে করবে একটি অ্যাক্সেস *((someUnion.array)+(index))একটি অ্যাক্সেস হয় someUnion। শুধুমাত্র ব্যাখ্যা আমি দেখতে পারেন যে someUnion.array[index]কিংবা *((someUnion.array)+(index))স্ট্যান্ডার্ড দ্বারা সংজ্ঞায়িত করা হয় না, কিন্তু নিছক একটি জনপ্রিয় এক্সটেনশন, এবং জিসিসি / ঝনঝন আছে দ্বিতীয় সমর্থন করার জন্য না নির্বাচিত আছে কিন্তু এটি প্রথম, অন্তত এখন জন্য সমর্থন বলে মনে হচ্ছে।
সুপারক্যাট

9

সি ++ এ এটি বেশিরভাগই অপরিজ্ঞাত আচরণ (এটি কোন সূচকের উপর নির্ভর করে)।

[Expr.unary.op] থেকে:

পয়েন্টার গাণিতিক (5.7) এবং তুলনা (5.9, 5.10) এর উদ্দেশ্যে, এমন কোনও বস্তু যা কোনও অ্যারে উপাদান নয় যার ঠিকানা এইভাবে নেওয়া হয়েছে তা ধরণের একটি উপাদান সহ একটি অ্যারের সাথে সম্পর্কিত বলে মনে করা হয় T

অভিব্যক্তি &thing.aএইভাবে এক একটি অ্যারের পড়ুন বিবেচিত হয় int

[এক্সফেসার্সব] থেকে:

এক্সপ্রেশনটি E1[E2]অভিন্ন (সংজ্ঞা অনুসারে) থেকে*((E1)+(E2))

এবং [এক্সপ্রেস এডডি] থেকে:

অবিচ্ছেদ্য টাইপযুক্ত একটি এক্সপ্রেশন যখন একটি পয়েন্টার থেকে যুক্ত বা বিয়োগ করা হয়, ফলাফলের মধ্যে পয়েন্টার অপারেন্ডের ধরণ থাকে। অভিব্যক্তি যদি Pউপাদানে পয়েন্ট x[i]একটি অ্যারের বস্তুর xসঙ্গে nউপাদান, এক্সপ্রেশন P + Jএবং J + P(যেখানে Jমূল্য আছে j(সম্ভবত-প্রকল্পিত) উপাদানে) বিন্দু x[i + j]যদি 0 <= i + j <= n; অন্যথায়, আচরণটি সংজ্ঞায়িত।

(&thing.a)[0]পুরোপুরি সুগঠিত কারণ &thing.aআকার 1 এর অ্যারে হিসাবে বিবেচিত হয় এবং আমরা সেই প্রথম সূচকটি নিচ্ছি। এটি গ্রহণযোগ্য একটি সূচক।

(&thing.a)[2]পূর্বশর্ত লঙ্ঘন করে 0 <= i + j <= n, যেহেতু আমরা আছে i == 0, j == 2, n == 1। কেবল পয়েন্টারটি তৈরি &thing.a + 2করা অপরিজ্ঞাত আচরণ।

(&thing.a)[1]আকর্ষণীয় ঘটনা। এটি আসলে [এক্সপিআরডিডি] কোনও কিছু লঙ্ঘন করে না। অ্যারের শেষের আগে আমাদের একটি পয়েন্টার নেওয়ার অনুমতি দেওয়া হয়েছে - যা এটি হবে। এখানে, আমরা [মৌলিক.কম্পাউন্ডে] একটি নোটটিতে ফিরলাম:

কোনও পয়েন্টার ধরণের মান যা কোনও বস্তুর শেষের দিকে বা অতীতকে নির্দেশ করে বস্তু দ্বারা দখলকৃত স্টোরেজ শেষ হওয়ার পরে অবজেক্ট 53 দ্বারা দখল করা মেমোরিতে প্রথম বাইটের অবস্থান (1.7) বা মেমরিতে প্রথম বাইটের প্রতিনিধিত্ব করে যথাক্রমে [দ্রষ্টব্য: কোনও অবজেক্টের শেষের একটি পয়েন্টার (5.7) অবজেক্টের ধরণের কোনও সম্পর্কযুক্ত অবজেক্টের দিকে চিহ্নিত করা হয় না যা সেই ঠিকানায় অবস্থিত হতে পারে।

সুতরাং, পয়েন্টারটি গ্রহণ &thing.a + 1করাকে সংজ্ঞায়িত আচরণ বলে বোঝানো হয় তবে এটি নির্দিষ্ট করে বোঝানো হয় না কারণ এটি কোনও কিছুর প্রতি নির্দেশ দেয় না।


মূল্যায়ন (& জিনিস.এ) + 1 ঠিক আইনী কারণ একটি অ্যারের শেষের দিকের একটি পয়েন্টার আইনী; সেখানে সংরক্ষিত ডেটা পড়া বা লেখাই অপরিবর্তিত আচরণ, </>, <=,> = এর সাথে & জিনিস.বিয়ের সাথে তুলনা করা অপরিজ্ঞাত আচরণ। (এবং জিনিস.এ) + 2 সম্পূর্ণ অবৈধ।
gnasher729

@ gnasher729 হ্যাঁ উত্তরটি আরও কিছু পরিষ্কার করা উচিত।
ব্যারি

(&thing.a + 1)একটি আকর্ষণীয় ক্ষেত্রে আমি আবরণ ব্যর্থ হয়। +1 টি! ... শুধু কৌতূহলী, আপনি কি আইএসও সি ++ কমিটিতে রয়েছেন?
WhiZTiM

এটি একটি খুব গুরুত্বপূর্ণ কেস কারণ অন্যথায় অর্ধ-খোলা ব্যবধান হিসাবে পয়েন্টার ব্যবহার করে প্রতিটি লুপ হবে ইউবি।
জেনস

সর্বশেষ স্ট্যান্ডার্ড উদ্ধৃতি সম্পর্কিত। সি ++ অবশ্যই এখানে সি এর চেয়ে ভাল নির্দিষ্ট করা উচিত।
2501

8

এটি অনির্ধারিত আচরণ।

সি ++ এ প্রচুর নিয়ম রয়েছে যা সংকলকটিকে আপনি কী করছেন তা বোঝার কিছু আশা দেওয়ার চেষ্টা করে, তাই এটি এটি সম্পর্কে যুক্তিযুক্ত এবং এটি অনুকূলিত করতে পারে।

আলিয়াজিং (দুটি পৃথক পয়েন্টার ধরণের মাধ্যমে ডেটা অ্যাক্সেস করা), অ্যারে বাউন্ডস ইত্যাদি সম্পর্কে নিয়ম রয়েছে etc.

আপনার যখন ভেরিয়েবল থাকে x, এটি কোনও অ্যারের সদস্য না হওয়ার অর্থ এই যে সংকলকটি ধরে নিতে পারে যে কোনও []ভিত্তিক অ্যারে অ্যাক্সেস এটি সংশোধন করতে পারে না। সুতরাং আপনি যখনই এটি ব্যবহার করবেন ততবার মেমরি থেকে ডেটা নিয়মিত পুনরায় লোড করতে হবে না; কেবলমাত্র যদি কেউ এর নাম থেকে এটি পরিবর্তন করতে পারে ।

এইভাবে (&thing.a)[1]উল্লেখ না করে সংকলক দ্বারা ধরে নেওয়া যেতে পারে thing.b। এটি এই সত্যটি ব্যবহার করে পড়তে এবং লিখতে পুনরায় অর্ডার করতে ব্যবহার করতে পারে thing.b, আপনি যা করতে চান তা অবৈধ না করে আপনি যা করতে চান তা অবৈধ।

এর একটি ক্লাসিক উদাহরণ কনস্টের বাইরে ফেলে দেওয়া।

const int x = 7;
std::cout << x << '\n';
auto ptr = (int*)&x;
*ptr = 2;
std::cout << *ptr << "!=" << x << '\n';
std::cout << ptr << "==" << &x << '\n';

এখানে আপনি সাধারণত একটি সংকলক পাবেন যা 7 বলে 2! = 7 এবং তারপরে দুটি অভিন্ন পয়েন্টার; যে ptrদিকে ইঙ্গিত করছে সত্ত্বেও x। সংকলকটি xযখন আপনি এর মান জিজ্ঞাসা করেন তখন এটি পড়তে বিরক্ত না করার জন্য ধ্রুবক মান এটি সত্য নেয় x

তবে আপনি যখন ঠিকানাটি গ্রহণ করেন x, আপনি এটিকে উপস্থিত থাকতে বাধ্য করেন। তারপরে আপনি কনস্টকে ফেলে দেন, এবং এটি সংশোধন করেন। সুতরাং মেমরিতে আসল অবস্থান যেখানে xপরিবর্তন করা হয়েছে, সংকলক পড়তে পড়তে আসলে এটি পড়তে মুক্ত x!

সংকলকটি ptrপড়ার পরেও কীভাবে এড়ানো যায় তা নির্ধারণের জন্য যথেষ্ট স্মার্ট হতে পারে *ptrতবে প্রায়শই সেগুলি হয় না। ptr = ptr+argc-1অপ্টিমাইজারটি আপনার চেয়ে বেশি স্মার্ট হয়ে যদি যেতে ও ব্যবহার করতে নির্বিঘ্ন বা কিছুটা বিভ্রান্তি অনুভব করে ।

আপনি একটি কাস্টম সরবরাহ করতে পারেন operator[]যা সঠিক আইটেমটি পায়।

int& operator[](std::size_t);
int const& operator[](std::size_t) const;

উভয় থাকা দরকারী।


"এটি কোনও অ্যারের সদস্য না হওয়ার অর্থ এই যে সংকলকটি ধরে নিতে পারে যে কোনও [] ভিত্তিক অ্যারে অ্যাক্সেস এটিকে পরিবর্তন করতে পারে না" " - সত্য নয়, উদাহরণস্বরূপ (&thing.a)[0]এটিকে সংশোধন করতে পারে
এমএম

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

1
@MM, এটা একটি struct মধ্যে সূচিবদ্ধ একটি উদাহরণ না, কিন্তু এটি একটি এর খুব কিভাবে তার দ্বারা রেফারেন্স কিছু অনির্ধারিত আচরণ ব্যবহারের ভাল চিত্রণ আপাত মেমরি অবস্থান, প্রত্যাশিত সময়ের চেয়ে বেশি বিভিন্ন আউটপুট হতে পারে, কম্পাইলার পারবেন না কারণ অন্য কিছু দিয়ে আপনি এটি চেয়ে চেয়ে ইউবি।
ওয়াইল্ডকার্ড

@ এমএম দুঃখিত, অবজেক্টের কাছে একটি পয়েন্টারের মাধ্যমে তুচ্ছ ব্যতীত আর কোনও অ্যারে অ্যাক্সেস নেই। এবং দ্বিতীয়টি অপরিবর্তিত আচরণের পার্শ্ব প্রতিক্রিয়াগুলি দেখতে সহজ উদাহরণ মাত্র; সংকলকটি পাঠকদের উপযুক্ত করে দেয় xকারণ এটি জানে যে আপনি এটি কোনও সংজ্ঞায়িত উপায়ে পরিবর্তন করতে পারবেন না। অনুরূপ অপ্টিমাইজেশন ঘটতে পারে যখন আপনি এর bমাধ্যমে পরিবর্তন করেন (&blah.a)[1]যদি সংকলক প্রমাণ করতে পারে যে এতে কোনও সংজ্ঞায়িত অ্যাক্সেস নেই bযা এটি পরিবর্তন করতে পারে; সংকলক, আশেপাশের কোড বা যে কোনও কিছুতে আপাতদৃষ্টিতে নিরীহ পরিবর্তনগুলির কারণে এ জাতীয় পরিবর্তন হতে পারে। এমনকি এটি পরীক্ষা করেও যে এটি কাজ করে তা পর্যাপ্ত নয়।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

6

নাম অনুসারে সদস্য অ্যারেতে উপাদানগুলিতে অ্যাক্সেসের জন্য প্রক্সি শ্রেণি ব্যবহারের এক উপায়। এটি খুব সি ++, এবং সিনট্যাকটিক পছন্দ ব্যতীত রেফ-রিটার্নিং অ্যাকসেসর ফাংশনগুলির তুলনায় কোনও সুবিধা নেই। এটি ->সদস্য হিসাবে উপাদানগুলিতে অ্যাক্সেস করতে অপারেটরটিকে ওভারলোড করে , তাই গ্রহণযোগ্য হওয়ার জন্য, উভয়কে অ্যাক্সেসরগুলির সিনট্যাক্স ( d.a() = 5;) অপছন্দ করা দরকার , পাশাপাশি ->একটি পয়েন্টারবিহীন বস্তু ব্যবহার করে সহ্য করা প্রয়োজন । আমি আশা করি এটি কোডের সাথে পরিচিত নয় এমন পাঠককেও বিভ্রান্ত করতে পারে, সুতরাং আপনি উত্পাদন করতে চান এমন কিছু থেকে এটি একটি ঝরঝরে কৌশল more

Dataএই কোডে struct হয় এছাড়াও সাবস্ক্রিপ্ট অপারেটর জন্য overloads, তার ভিতরে প্রবেশ ইন্ডেক্স উপাদান অন্তর্ভুক্ত arঅ্যারের সদস্য, সেইসাথে beginএবং endফাংশন, পুনরাবৃত্তির জন্য। এছাড়াও, এগুলি সমস্তই অবিচ্ছিন্ন এবং কনস্টের সংস্করণগুলির সাথে ওভারলোড হয়েছে, যা আমি সম্পূর্ণতার জন্য অন্তর্ভুক্ত করার প্রয়োজন বলে মনে করেছি।

যখন Data's ->নামে একটি উপাদান অ্যাক্সেস করতে (এই মত: ব্যবহার করা হয় my_data->b = 5;), একটি Proxyবস্তুর ফিরিয়ে দেওয়া হয়। তারপরে, কারণ এই Proxyমূল্যমানটি কোনও পয়েন্টার নয়, এর নিজস্ব ->অপারেটরটি স্বয়ংক্রিয়-চেইন- কলড , যা নিজেই একটি পয়েন্টারটি ফেরত দেয়। এইভাবে, Proxyঅবজেক্টটি তাত্ক্ষণিক হয় এবং প্রাথমিক অভিব্যক্তির মূল্যায়নের সময় বৈধ থাকে।

একটি এর Contruction Proxyবস্তুর তার 3 রেফারেন্স সদস্যদের মান a, bএবং cএকটি পয়েন্টার কন্সট্রাকটর পাস, যা একটি বাফার অন্তত 3 মান যার টাইপ টেমপ্লেট প্যারামিটার হিসাবে দেওয়া হয় ধারণকারী বিন্দু অধিকৃত হয় অনুযায়ী T। সুতরাং Dataক্লাসের সদস্য হওয়া নামকৃত রেফারেন্সগুলি ব্যবহার করার পরিবর্তে, রেফারেন্সগুলি অ্যাক্সেসের পয়েন্টে পপুলেশন করে স্মৃতি সাশ্রয় করে (তবে দুর্ভাগ্যক্রমে, অপারেটরটি ব্যবহার করে ->নয় .)।

সংকলকটির অপ্টিমাইজারটি কতটা ভালভাবে ব্যবহারের মাধ্যমে প্রবর্তিত সমস্ত দিকনির্দেশকে সরিয়ে দেয় তা পরীক্ষা করার জন্য Proxy, নীচের কোডটিতে 2 টি সংস্করণ রয়েছে main()#if 1সংস্করণ ব্যবহার করে ->এবং []অপারেটর এবং #if 0সংস্করণ পদ্ধতি সমতুল্য সেট সঞ্চালিত, কিন্তু শুধুমাত্র সরাসরি অ্যাক্সেস করার মাধ্যমে Data::ar

Nci()ফাংশন অ্যারে উপাদান, যা সরাসরি প্রতিটি মধ্যে ধ্রুবক মান প্লাগিং থেকে অপটিমাইজার বাধা দেয় আরম্ভের জন্য রানটাইম পূর্ণসংখ্যা মান উত্পন্ন std::cout <<কল।

জিসিসি 6.2 এর জন্য, -O3 ব্যবহার করে, উভয় সংস্করণ main()একই সমাবেশ তৈরি করে ( প্রথমটির সাথে তুলনা করার আগে #if 1এবং #if 0আগে টগল main()করুন): https://godbolt.org/g/QqRWZb

#include <iostream>
#include <ctime>

template <typename T>
class Proxy {
public:
    T &a, &b, &c;
    Proxy(T* par) : a(par[0]), b(par[1]), c(par[2]) {}
    Proxy* operator -> () { return this; }
};

struct Data {
    int ar[3];
    template <typename I> int& operator [] (I idx) { return ar[idx]; }
    template <typename I> const int& operator [] (I idx) const { return ar[idx]; }
    Proxy<int>       operator -> ()       { return Proxy<int>(ar); }
    Proxy<const int> operator -> () const { return Proxy<const int>(ar); }
    int* begin()             { return ar; }
    const int* begin() const { return ar; }
    int* end()             { return ar + sizeof(ar)/sizeof(int); }
    const int* end() const { return ar + sizeof(ar)/sizeof(int); }
};

// Nci returns an unpredictible int
inline int Nci() {
    static auto t = std::time(nullptr) / 100 * 100;
    return static_cast<int>(t++ % 1000);
}

#if 1
int main() {
    Data d = {Nci(), Nci(), Nci()};
    for(auto v : d) { std::cout << v << ' '; }
    std::cout << "\n";
    std::cout << d->b << "\n";
    d->b = -5;
    std::cout << d[1] << "\n";
    std::cout << "\n";

    const Data cd = {Nci(), Nci(), Nci()};
    for(auto v : cd) { std::cout << v << ' '; }
    std::cout << "\n";
    std::cout << cd->c << "\n";
    //cd->c = -5;  // error: assignment of read-only location
    std::cout << cd[2] << "\n";
}
#else
int main() {
    Data d = {Nci(), Nci(), Nci()};
    for(auto v : d.ar) { std::cout << v << ' '; }
    std::cout << "\n";
    std::cout << d.ar[1] << "\n";
    d->b = -5;
    std::cout << d.ar[1] << "\n";
    std::cout << "\n";

    const Data cd = {Nci(), Nci(), Nci()};
    for(auto v : cd.ar) { std::cout << v << ' '; }
    std::cout << "\n";
    std::cout << cd.ar[2] << "\n";
    //cd.ar[2] = -5;
    std::cout << cd.ar[2] << "\n";
}
#endif

নিফটি। উত্সাহিত মূলত কারণ আপনি প্রমাণ করেছেন যে এটি অপ্টিমাইজ করে। বিটিডাব্লু, আপনি খুব সাধারণ ফাংশন লিখে main()সময়সুলভ ফাংশন দিয়ে পুরোটা না করে আরও সহজেই তা করতে পারেন ! যেমন int getb(Data *d) { return (*d)->b; }ঠিক mov eax, DWORD PTR [rdi+4]/ ret( Godbolt.org/g/89d3Np ) তে সংকলন করে । (হ্যাঁ, Data &dবাক্য গঠনটি আরও সহজ করে তুলবে, তবে আমি ওভারলোডিংয়ের অদ্ভুততাটি তুলে ধরে রেফের পরিবর্তে একটি পয়েন্টার ব্যবহার করেছি ->।)
পিটার কর্ডেস

যাইহোক, এটি দুর্দান্ত। অন্যান্য মতামতগুলি int tmp[] = { a, b, c}; return tmp[idx];অপ্টিমাইজ না করে, সুতরাং এটি ঝরঝরে।
পিটার কর্ডেস

আর একটি কারণ আমি operator.C ++ 17 এ মিস করছি ।
জেনস

2

যদি মানগুলি পড়া যথেষ্ট, এবং দক্ষতা কোনও উদ্বেগ নয়, বা আপনি যদি বিষয়গুলি ভালভাবে অনুকূল করতে আপনার সংকলককে বিশ্বাস করেন বা স্ট্রাক্টটি কেবলমাত্র 3 বাইট হয় তবে আপনি নিরাপদে এটি করতে পারেন:

char index_data(const struct data *d, size_t index) {
  assert(sizeof(*d) == offsetoff(*d, c)+1);
  assert(index < sizeof(*d));
  char buf[sizeof(*d)];
  memcpy(buf, d, sizeof(*d));
  return buf[index];
}

কেবলমাত্র সি ++ সংস্করণের জন্য, আপনি সম্ভবত স্ট্যান্ডার্ড বিন্যাসটি static_assertযাচাই করতে ব্যবহার করতে চান struct dataএবং এর পরিবর্তে অবৈধ সূচকে ব্যতিক্রম করতে পারেন।


1

এটি অবৈধ, তবে একটি কার্যকারিতা রয়েছে:

struct data {
    union {
        struct {
            int a;
            int b;
            int c;
        };
        int v[3];
    };
};

এখন আপনি ভি সূচক করতে পারেন:


6
অনেক সি ++ প্রকল্পগুলি মনে করে যে সমস্ত জায়গা জুড়ে ডাউনকাস্টিং ঠিক আছে। আমাদের এখনও খারাপ অভ্যাস প্রচার করা উচিত নয়।
গল্পগ্রাহক - আনস্ল্যান্ডার মনিকা

2
ইউনিয়ন উভয় ভাষায় কঠোর আলিয়াজিংয়ের সমস্যা সমাধান করে। তবে ইউনিয়নগুলির মাধ্যমে টাইপ পেনিং কেবল সি-তে ভাল, সি ++ তে নয়।
লন্ডিন

1
তবুও, আমি যদি সমস্ত সি ++ সংকলকগুলির 100% এ কাজ করে তবে আমি অবাক হব না। কি।
সোভেন নীলসন

1
আপনি এটিতে সবচেয়ে আক্রমণাত্মক অপ্টিমাইজার সেটিংস সহ জিসিসিতে চেষ্টা করতে পারেন।
লন্ডিন

1
@ লন্ডিন: জিএনইউ সি ++ তে ইউনিয়ন প্রকারের পাণি দেওয়া আইনী , আইএসও সি ++ এর চেয়ে বেশি extension এটি ম্যানুয়ালটিতে খুব স্পষ্টভাবে বলা হয়েছে বলে মনে হয় না , তবে আমি এটি সম্পর্কে বেশ নিশ্চিত। তবুও, এই উত্তরটি কোথায় তা বৈধ এবং কোথায় তা নয় তা ব্যাখ্যা করা দরকার।
পিটার কর্ডেস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.