এই কোডের টুকরা কীভাবে আকারের () ব্যবহার না করে অ্যারের আকার নির্ধারণ করবে?


134

কিছু সি সাক্ষাত্কারের প্রশ্নগুলির মধ্য দিয়ে আমি নীচের সমাধান সহ একটি প্রশ্ন পেয়েছি যাতে "সাইজেরফ অপারেটরটি ব্যবহার না করে সিটিতে কোনও অ্যারের আকার কীভাবে পাওয়া যায়?" এটি কাজ করে, তবে কেন বুঝতে পারছি না।

#include <stdio.h>

int main() {
    int a[] = {100, 200, 300, 400, 500};
    int size = 0;

    size = *(&a + 1) - a;
    printf("%d\n", size);

    return 0;
}

প্রত্যাশিত হিসাবে, এটি 5 ফিরে আসে।

সম্পাদনা করুন: লোকেরা এই উত্তরটি দেখিয়েছে , তবে বাক্য গঠনটি কিছুটা পৃথক হয়, অর্থাত্ সূচক পদ্ধতিতে

size = (&arr)[1] - arr;

সুতরাং আমি বিশ্বাস করি যে উভয় প্রশ্নই বৈধ এবং সমস্যাটির দিকে কিছুটা আলাদা পদ্ধতি রয়েছে। অপরিসীম সাহায্য এবং পুরো ব্যাখ্যা জন্য আপনাকে ধন্যবাদ!


13
ভাল, এটি খুঁজে পাচ্ছি না, তবে এটি দৃ strictly়ভাবে বলার মতো দেখাচ্ছে। আনেকেক্স জে .২ স্পষ্টভাবে উল্লেখ করছে: ইউনারি * অপারেটরের অপারেন্ডের একটি অবৈধ মান রয়েছে একটি অপরিজ্ঞাত আচরণ। এখানে &a + 1কোনও বৈধ অবজেক্টের দিকে নির্দেশ করা হচ্ছে না, সুতরাং এটি অবৈধ।
ইউজিন শ।

5
সম্পর্কিত: Is *((*(&array + 1)) - 1)একটি স্বয়ংক্রিয় অ্যারের শেষ উপাদান পেতে ব্যবহার করা নিরাপদ? । tl; ডাঃ *(&a + 1)অনির্ধারিত বেহভৈরকে আহ্বান জানিয়েছে
স্পিক্যাট্রিক্স


@ আলমাডো ভালভাবে বাক্য গঠনটি খানিকটা আলাদা হয়, অর্থাত্ সূচকের অংশটি, সুতরাং আমি বিশ্বাস করি যে এই প্রশ্নটি এখনও তার নিজের পক্ষে বৈধ, তবে আমি ভুল হতে পারি। এটি নির্দেশ করার জন্য আপনাকে ধন্যবাদ!
janojlic

1
@ জজনোজলিজ তারা মূলত একই, কারণ (ptr)[x]হিসাবে একই *((ptr) + x)
এসএস আন

উত্তর:


135

আপনি যখন একটি পয়েন্টারে 1 যুক্ত করেন, ফলাফলটি পয়েন্ট-টু টাইপের (যেমন, একটি অ্যারে) অবজেক্টের ক্রমতে পরবর্তী বস্তুর অবস্থান। যদি pকোনও intবস্তুর দিকে p + 1নির্দেশ করা হয় , তবে পরের intদিকে একটি ক্রমিক হিসাবে নির্দেশ করবে । যদি pএকটি 5-উপাদানের বিন্যাস পয়েন্ট int(এই ক্ষেত্রে, অভিব্যক্তি &a), তারপর p + 1পরবর্তী নির্দেশ করবে 5-উপাদানের বিন্যাসint একটি ক্রম হবে।

দুটি পয়েন্টার বিয়োগ করা (তারা উভয় একই অ্যারে অবজেক্টে বিন্দু সরবরাহ করে, বা একটি অ্যারের শেষ উপাদানটির মধ্য দিয়ে একটি পয়েন্ট করছে) সেই দুটি পয়েন্টারের মধ্যে অবজেক্টের সংখ্যা (অ্যারে উপাদান) দেয়।

অভিব্যক্তিটির &aঠিকানা পাওয়া যায় aএবং এতে int (*)[5](5-উপাদান অ্যারের পয়েন্টার int) থাকে। এক্সপ্রেশনটি নিম্নলিখিত &a + 15-উপাদান অ্যারেরের ঠিকানা দেয় এবং এতে টাইপও রয়েছে । অভিব্যক্তিটি ফলাফলটির dereferences , যেমন এটি শেষ উপাদানটির প্রথমের ঠিকানা প্রাপ্ত করে এবং টাইপ করে , যা এই প্রসঙ্গে টাইপের একটি অভিব্যক্তিতে "সিদ্ধান্ত নেয়" ।intaint (*)[5]*(&a + 1)&a + 1intaint [5]int *

একইভাবে, অভিব্যক্তি aঅ্যারের প্রথম উপাদান একটি পয়েন্টার থেকে "decays" এবং টাইপ হয়েছে int *

একটি ছবি সাহায্য করতে পারে:

int [5]  int (*)[5]     int      int *

+---+                   +---+
|   | <- &a             |   | <- a
| - |                   +---+
|   |                   |   | <- a + 1
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
+---+                   +---+
|   | <- &a + 1         |   | <- *(&a + 1)
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
+---+                   +---+

এটি একই স্টোরেজের দুটি মতামত - বাম দিকে, আমরা এটিকে 5-উপাদান অ্যারেগুলির ক্রম হিসাবে দেখছি int, যখন ডানদিকে, আমরা এটিকে এর ক্রম হিসাবে দেখছি int। আমি বিভিন্ন এক্সপ্রেশন এবং তাদের প্রকারগুলিও প্রদর্শন করি।

সচেতন থাকুন, এক্সপ্রেশনটি অপরিজ্ঞাত আচরণের*(&a + 1) ফলাফল দেয় :

...
যদি ফলাফলটি অ্যারের অবজেক্টের শেষ উপাদানটির একটিকে চিহ্নিত করে, এটি মূল্যায়ন করা একটি অ্যানারি * অপারেটরের ক্রিয়াকলাপ হিসাবে ব্যবহৃত হবে না।

সি 2011 অনলাইন খসড়া , 6.5.6 / 9


13
এটি "ব্যবহৃত হবে না" পাঠ্যটি সরকারী: সি 2018 6.5.6 8
এরিক পোস্টপিসিল

@ এরিকপোস্টপিসিল: আপনার কি 2018 এর প্রাক-পাব খসড়ার (এন 1570.pdf এর অনুরূপ) লিঙ্ক আছে?
জন বোদে 15'19

1
@ জনবোড: এই উত্তরের ওয়েব্যাক মেশিনের একটি লিঙ্ক রয়েছে । আমি আমার ক্রয়কৃত অনুলিপিটিতে অফিসিয়াল স্ট্যান্ডার্ডটি পরীক্ষা করেছিলাম।
এরিক পোস্টপিসিল

7
যদি কেউ size = (int*)(&a + 1) - a;এই কোডটি লিখে থাকেন তবে তা সম্পূর্ণ বৈধ হবে? : ও
গিজমো

@ গিজমো সম্ভবত তারা সম্ভবত এটি লিখেনি কারণ এইভাবে আপনাকে উপাদান ধরণের নির্দিষ্ট করতে হবে; মূলটি সম্ভবত বিভিন্ন উপাদান ধরণের জেনেরিক ব্যবহারের জন্য ম্যাক্রো হিসাবে সংজ্ঞায়িত হয়ে লেখা হয়েছিল।
লুশেনকো

35

এই লাইনটি সর্বাধিক গুরুত্বপূর্ণ:

size = *(&a + 1) - a;

আপনি দেখতে পাচ্ছেন, এটি প্রথমে ঠিকানা নেয় aএবং এতে একটি যুক্ত করে। তারপরে, এটি সেই পয়েন্টারটিকে অবলম্বন করে এবং এ aথেকে এর মূল মানটি বিয়োগ করে ।

সিতে পয়েন্টার গাণিতিক কারণে এটি অ্যারের উপাদানগুলির সংখ্যা, বা 5। একটি যোগ &aকরা এবং পরবর্তী 5 intটির পরবর্তী অ্যারেতে পয়েন্টার a। তারপরে, এই কোডটি ফলাফলের পয়েন্টারটিকে অবৈধ করে এবং এটি থেকে বিয়োগ করে a(একটি বিন্যাসে ক্ষয় হয়ে গেছে এমন একটি অ্যারে টাইপ), অ্যারেতে উপাদানগুলির সংখ্যা দেয়।

কীভাবে পয়েন্টার গাণিতিক কাজ করে তার বিশদ:

বলুন আপনার কাছে এমন একটি পয়েন্টার রয়েছে xyzযা কোনও প্রকারকে নির্দেশ করে intএবং এতে মান থাকে (int *)160। আপনি xyzযে কোনও নম্বর থেকে বিয়োগ করলে , সি নির্দিষ্ট করে যে প্রকৃত পরিমাণ থেকে বিয়োগ করা হয়েছে xyzএটি যে ধরণের দিকে ইঙ্গিত করে তার চেয়ে সেই পরিমাণের চেয়ে বহুগুণ বেশি। উদাহরণস্বরূপ, যদি আপনি বিয়োগ 5থেকে xyzমান xyzফলে হবে xyz - (sizeof(*xyz) * 5)যদি পয়েন্টার এরিথমেটিক প্রয়োগ করেননি।

ধরণের aঅ্যারে হিসাবে 5 int, ফলস্বরূপ মানটি 5 হবে তবে যাইহোক, এটি কোনও পয়েন্টারের সাথে কাজ করবে না, কেবল একটি অ্যারে দিয়ে। যদি আপনি এটি একটি পয়েন্টার দিয়ে চেষ্টা করেন, ফলাফল সর্বদা হবে 1

এখানে একটি ছোট উদাহরণ যা ঠিকানাগুলি দেখায় এবং এটি কীভাবে সংজ্ঞায়িত। বাম দিকের ঠিকানাগুলি দেখায়:

a + 0 | [a[0]] | &a points to this
a + 1 | [a[1]]
a + 2 | [a[2]]
a + 3 | [a[3]]
a + 4 | [a[4]] | end of array
a + 5 | [a[5]] | &a+1 points to this; accessing past array when dereferenced

এর অর্থ হল কোডটি (বা ) aথেকে বিয়োগ করছে , দিচ্ছে ।&a[5]a+55

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


27

হুঁ, আমি সন্দেহ করি এটি সি এর প্রথম দিনগুলিতে ফিরে কাজ করবে না এটি যদিও চালাক।

একবারে পদক্ষেপ নেওয়া:

  • &a টাইপ ইন্টের অবজেক্টের পয়েন্টার পায় [5]
  • +1 পরের ধরণের জিনিসটি ধরে নিয়ে অনুমান করে যে তাদের মধ্যে একটি অ্যারে রয়েছে
  • * কার্যকরভাবে সেই ঠিকানাটি টাইপ পয়েন্টারকে ইনটে রূপান্তর করে
  • -a দুটি ইন্ট পয়েন্টারকে বিয়োগ করে, তাদের মধ্যে অন্তরের উদাহরণগুলির গণনা ফিরিয়ে দেয়।

আমি নিশ্চিত না যে এটি সম্পূর্ণ আইনী (এটির অর্থ ভাষা-আইনজীবী আইনী - এটি বাস্তবে কার্যকর হবে না), কিছু ধরণের অপারেশন চলছে given উদাহরণস্বরূপ, যখন তারা একই অ্যারেতে উপাদানগুলিকে নির্দেশ করে তখন আপনাকে কেবল দুটি পয়েন্টার বিয়োগ করতে "অনুমতি দেওয়া" হয়। *(&a+1)অভিভাবক অ্যারে হলেও অন্য অ্যারে অ্যাক্সেস করে সংশ্লেষিত হয়েছিল, সুতরাং আসলে একই অ্যারেতে পয়েন্টার নয় a। এছাড়াও, আপনাকে যখন অ্যারের শেষ উপাদানটি দিয়ে একটি পয়েন্টার সংশ্লেষ করার অনুমতি দেওয়া হয় এবং আপনি যে কোনও বস্তুকে 1 টি উপাদান হিসাবে অ্যারে হিসাবে বিবেচনা করতে পারেন *, তবুও এই সংশ্লেষিত পয়েন্টারে ডেরিফারেন্সিং ( ) এর ক্রিয়াকলাপ "অনুমোদিত নয়", যদিও এটি এক্ষেত্রে কোন আচরণ নেই!

আমি সন্দেহ করি যে সি (কে অ্যান্ডআর সিনট্যাক্স, যে কেউ?) এর প্রথম দিনগুলিতে একটি অ্যারে আরও দ্রুত পয়েন্টারে ক্ষয় হয়ে যায়, তাই সম্ভবত *(&a+1)পরবর্তী টাইপটির ইন্টি * পয়েন্টারের ঠিকানাটিই ফিরে আসতে পারে। আধুনিক সি ++ এর আরও কঠোর সংজ্ঞাগুলি অবশ্যই পয়েন্টারটিকে অ্যারে প্রকারের উপস্থিতি এবং অ্যারের আকার জানতে দেয় এবং সম্ভবত সি স্ট্যান্ডার্ডগুলি অনুসরণ করে। সমস্ত সি ফাংশন কোডটি কেবলমাত্র আর্গুমেন্ট হিসাবে পয়েন্টার নেয়, তাই প্রযুক্তিগত দৃশ্যমান পার্থক্যটি ন্যূনতম। তবে আমি এখানে কেবল অনুমান করছি।

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

আর একটি সম্ভাব্য দুর্বলতা হতে পারে যে সি সংকলক বাইরের অ্যারে সারিবদ্ধ করতে পারে। কল্পনা করুন যে এটি 5 টি চর ( char arr[5]) এর একটি অ্যারে ছিল , যখন প্রোগ্রামটি সঞ্চালন করে &a+1এটি "অ্যারের অ্যারে" আচরণের জন্য প্রার্থনা করে। সংকলকটি সিদ্ধান্ত নিতে পারে যে 5 টি (4 char arr[][5]) অ্যারের অ্যারে আসলে 8 টি চর ( char arr[][8]) এর অ্যারের হিসাবে উত্পন্ন হয় , যাতে বাহ্যিক অ্যারেটি সুন্দরভাবে সারিবদ্ধ হয়। আমরা যে কোডটি নিয়ে আলোচনা করছি সেগুলি এখন অ্যারের আকারকে 8 হিসাবে নয়, 5 হিসাবে প্রতিবেদন করবে I'm আমি কোনও বিশেষ সংকলক স্পষ্টভাবে এটি করবে তা বলছি না, তবে এটি হতে পারে।


যথেষ্ট ফর্সা। তবে ব্যাখ্যা করার জন্য কঠিন কারণে, সবাই মাপের () / আকারের () ব্যবহার করে?
রত্ন টেলর

5
বেশিরভাগ মানুষই করেন। উদাহরণস্বরূপ, sizeof(array)/sizeof(array[0])একটি অ্যারেতে উপাদানগুলির সংখ্যা দেয়।
এসএস অ্যান

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

1
পয়েন্টারগুলিকে বিয়োগ করা একই অ্যারেটিতে কেবল দুটি পয়েন্টারের মধ্যেই সীমাবদ্ধ নয় — পয়েন্টারগুলিও অ্যারের শেষের শেষের এক হতে পারে। &a+1সংজ্ঞায়িত করা. জন বলিঞ্জার নোট হিসাবে, *(&a+1)এটি না, যেহেতু এটি কোনও অস্তিত্বের অস্তিত্বকে অবজ্ঞা করার চেষ্টা করে।
এরিক পোস্টপিসিল

5
একটি সংকলক একটি char [][5]হিসাবে বাস্তবায়ন করতে পারে না char arr[][8]। একটি অ্যারে হ'ল এটিতে পুনরাবৃত্তি হওয়া অবজেক্টগুলি; কোন প্যাডিং নেই। অতিরিক্ত, এটি সি 2018 6.5.3.4 7 এর (অ-আদর্শিক) উদাহরণটি ভেঙে দেবে, যা আমাদেরকে বলে যে আমরা একটি অ্যারের সাথে উপাদানগুলির সংখ্যা গণনা করতে পারি sizeof array / sizeof array[0]
এরিক পোস্টপিসিল
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.