মাল্টি-ডাইমেনশনাল অ্যারে কীভাবে মেমরিতে ফর্ম্যাট করা হয়?


184

সি-তে, আমি জানি যে আমি নীচের কোডটি ব্যবহার করে গাদা হয়ে দ্বি-মাত্রিক অ্যারে বরাদ্দ করতে পারি:

int** someNumbers = malloc(arrayRows*sizeof(int*));

for (i = 0; i < arrayRows; i++) {
    someNumbers[i] = malloc(arrayColumns*sizeof(int));
}

স্পষ্টতই, এটি সংখ্যার পৃথক এক-মাত্রিক অ্যারেগুলির একগুচ্ছ পয়েন্টারগুলির একটি এক-মাত্রিক অ্যারে তৈরি করে এবং "দ্য সিস্টেম" যখন আমি জিজ্ঞাসা করি তখন আমার অর্থ কী তা নির্ধারণ করতে পারে:

someNumbers[4][2];

তবে যখন আমি স্থিরভাবে নিম্নলিখিত লাইনের মতো 2D অ্যারে ঘোষণা করি ...:

int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS];

... স্ট্যাকের উপর কি একই ধরণের কাঠামো তৈরি হয়, বা এটি অন্য কোনও রূপের সম্পূর্ণরূপে? (অর্থাত্ এটি পয়েন্টারগুলির 1D অ্যারে? যদি তা না হয় তবে এটি কী এবং কীভাবে এর উল্লেখগুলি পাওয়া যায়?)

এছাড়াও, যখন আমি "সিস্টেম" বলেছিলাম, আসলে এটি আবিষ্কারের জন্য দায়ী কী? কর্নেল? অথবা সি সংকলক সংকলন করার সময় এটি বাছাই করে?


8
আমি পারলে +1 এরও বেশি দিতাম।
রব লাচলান

1
সতর্কতা : এই কোডটিতে কোনও 2 ডি অ্যারে নেই!
এই সাইটের পক্ষে খুব সৎ

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

উত্তর:


144

একটি স্থিতিশীল দ্বি-মাত্রিক অ্যারে দেখতে অ্যারের মতো লাগে - এটি কেবল স্মৃতিতে স্বচ্ছলভাবে ছড়িয়ে দেওয়া হয়েছে। অ্যারেগুলি পয়েন্টারগুলির মতো একই জিনিস নয় তবে আপনি প্রায়শই এগুলি একে অপরের পরিবর্তে ব্যবহার করতে পারেন কারণ এটি কখনও কখনও বিভ্রান্তি পেতে পারে। সংকলক সঠিকভাবে ট্র্যাক রাখে, যা সবকিছুকে সুন্দরভাবে সাজিয়ে তোলে। আপনি উল্লেখ করেছেন এমন স্ট্যাটিক 2 ডি অ্যারে সম্পর্কে আপনাকে সতর্কতা অবলম্বন করতে হবে, যেহেতু আপনি যদি কোনও int **পরামিতি নিয়ে কোনও ফাংশনে যেতে চেষ্টা করেন তবে খারাপ জিনিস ঘটতে চলেছে। এখানে একটি দ্রুত উদাহরণ:

int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}};

স্মৃতিতে এটি দেখতে দেখতে:

0 1 2 3 4 5

ঠিক যেমন:

int array2[6] = { 0, 1, 2, 3, 4, 5 };

তবে আপনি যদি array1এই ফাংশনে পৌঁছানোর চেষ্টা করেন :

void function1(int **a);

আপনি একটি সতর্কতা পাবেন (এবং অ্যাপটি সঠিকভাবে অ্যারে অ্যাক্সেস করতে ব্যর্থ হবে):

warning: passing argument 1 of function1 from incompatible pointer type

কারণ একটি 2 ডি অ্যারে যেমন হয় না int **। একটি বিন্যাসে একটি অ্যারের স্বয়ংক্রিয় ক্ষয় হওয়া কেবল "এক স্তর গভীর" তেমন বলতে যায়। আপনাকে এই ক্রিয়াটি এই হিসাবে ঘোষণা করতে হবে:

void function2(int a[][2]);

অথবা

void function2(int a[3][2]);

সবকিছু খুশি করতে।

এই একই ধারণাটি এন- ডাইমেনশনাল অ্যারে পর্যন্ত প্রসারিত । আপনার অ্যাপ্লিকেশনটিতে এই ধরণের মজাদার ব্যবসায়ের সুবিধা গ্রহণ করা সাধারণত তা বোঝা আরও শক্ত করে তোলে। তাই বাইরে সতর্কতা অবলম্বন করুন।


ব্যাখ্যার জন্য ধন্যবাদ. সুতরাং "শূন্য ফাংশন 2 (ইন্ট আ [] [2]);" স্থিতিশীল এবং গতিশীলভাবে 2D এর ঘোষণা উভয়ই গ্রহণ করবে? এবং আমি অনুমান করি যে অ্যারের দৈর্ঘ্যটি পাস করার জন্য এটি এখনও ভাল অনুশীলন / অপরিহার্য, যদি প্রথম মাত্রাটি [] হিসাবে রেখে যায়?
ক্রিস কুপার

1
@ ক্রিস আমি এমনটি ভাবি না - সি সি সুইজল করে স্ট্যাক- বা বিশ্বব্যাপী বরাদ্দ করা অ্যারেগুলিকে পয়েন্টারগুলির একগুচ্ছ তৈরি করতে আপনার খুব কষ্ট হবে।
কার্ল নরুম

6
@JasonK। - না অ্যারেগুলি পয়েন্টার নয়। কিছু প্রসঙ্গে পয়েন্টারগুলিতে অ্যারে "ক্ষয়" হয় তবে সেগুলি একেবারে এক নয়
কার্ল নরুম 10:25

1
পরিষ্কার হয়ে উঠতে: হ্যাঁ ক্রিস পৃথক প্যারামিটার হিসাবে "অ্যারের দৈর্ঘ্যে উত্তীর্ণ হওয়া এখনও ভাল অনুশীলন", অন্যথায় std :: অ্যারে বা std :: ভেক্টর (যা সি ++ পুরানো সি নয়) ব্যবহার করুন। আমি মনে করি আমরা @ কার্লনরমকে নতুন ব্যবহারকারীদের জন্য এবং বাস্তবিকভাবে কোওরায় অ্যান্ডারস ক্যাসেরগের উদ্ধৃতি দিতে সম্মত হয়েছি: "সি শেখার প্রথম পদক্ষেপটি বুঝতে পারা যে পয়েন্টার এবং অ্যারে একই জিনিস। দ্বিতীয় পদক্ষেপটি বোঝা যাচ্ছে যে পয়েন্টার এবং অ্যারে আলাদা ”"
জেসন কে।

2
@JasonK। "সি শেখার প্রথম পদক্ষেপটি বোঝা যাচ্ছে যে পয়েন্টার এবং অ্যারে একই জিনিস" " - এই উদ্ধৃতিটি খুব ভুল এবং মিসেলিডিং! এটি একই নয় এটি বোঝার পক্ষে এটি সবচেয়ে গুরুত্বপূর্ণ পদক্ষেপ , তবে বেশিরভাগ অপারেটরগুলির জন্য অ্যারেগুলি প্রথম উপাদানটিতে পয়েন্টারে রূপান্তরিত হয় ! (যদি না আপনি বাইটস / সহ একটি প্ল্যাটফর্ম খুঁজে পান তবে এটি ভিন্ন জিনিসsizeof(int[100]) != sizeof(int *)100 * sizeof(int)int
এই সাইটের পক্ষে খুব স্বেচ্ছাসেবী

84

উত্তর ধারণার উপর ভিত্তি করে তৈরি যে সি সত্যিই নেই আছে 2D অ্যারে - এটা অ্যারে অফ অ্যারে হয়েছে। আপনি যখন এটি ঘোষণা করেন:

int someNumbers[4][2];

আপনি someNumbers4 টি উপাদানের অ্যারে হওয়ার জন্য জিজ্ঞাসা করছেন , যেখানে সেই অ্যারের প্রতিটি উপাদান টাইপযুক্ত int [2](যা নিজেই 2 intএস এর অ্যারে )।

ধাঁধার অপর অংশটি হ'ল অ্যারেগুলি সর্বদা স্মৃতিতে স্বচ্ছন্দভাবে প্রস্তুত থাকে। আপনি যদি জিজ্ঞাসা করেন:

sometype_t array[4];

তাহলে তা সর্বদা এর মতো দেখাবে:

| sometype_t | sometype_t | sometype_t | sometype_t |

(4 টি sometype_tঅবজেক্ট একে অপরের পাশে বিছানো, এর মধ্যে কোনও স্থান নেই)। সুতরাং আপনার someNumbersঅ্যারে অফারে, এটি দেখতে দেখতে এটি দেখতে পাবেন:

| int [2]    | int [2]    | int [2]    | int [2]    |

এবং প্রতিটি int [2]উপাদান নিজেই একটি অ্যারে, যা দেখতে দেখতে এটি দেখতে:

| int        | int        |

সুতরাং সামগ্রিকভাবে, আপনি এটি পান:

| int | int  | int | int  | int | int  | int | int  |

1
চূড়ান্ত বিন্যাসটি দেখে আমার মনে হয় যে কোন একটি [] [] ইনট * হিসাবে প্রবেশ করা যায় ... ঠিক?
নার্সিস ডুডিউ সিয়েও

2
@ ব্যবহারকারী3238855: প্রকারগুলি সামঞ্জস্যপূর্ণ নয় তবে আপনি যদি intঅ্যারের অ্যারেতে প্রথম পয়েন্টার পান (উদাহরণস্বরূপ মূল্যায়ন করে a[0]বা &a[0][0]) তবে হ্যাঁ, আপনি প্রতিটিটি ক্রমান্বয়ে অ্যাক্সেস করার জন্য অফসেট করতে পারেন int)।
ক্যাফে

28
unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}};

স্মৃতিতে সমান:

unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9};

5

আপনারও জবাবে: উভয়ই যদিও সংকলক ভারী উত্তোলন বেশিরভাগ ক্ষেত্রেই করছে।

স্থিতিশীলভাবে বরাদ্দ করা অ্যারের ক্ষেত্রে, "দ্য সিস্টেম" সংকলক হবে। এটি কোনও স্ট্যাক ভেরিয়েবলের মতো স্মৃতিটিকে সংরক্ষণ করবে।

ম্যালোক'ড অ্যারের ক্ষেত্রে, "দ্য সিস্টেম" হ'ল ম্যালোক প্রয়োগকারী (সাধারণত কার্নেল)। সমস্ত সংকলক হ'ল বেস পয়েন্টার।

সংকলক সর্বদা টাইপটি হ'ল যা যা তাদের হিসাবে ঘোষিত হয় তা হ'ল কার্ল যে উদাহরণ দিয়েছিল সেখানে এটি পরিবর্তিতযোগ্য ব্যবহারের সন্ধান করতে পারে except এ কারণেই যদি আপনি একটি [] []] কোনও ফাংশনে প্রবেশ করেন তবে অবশ্যই এটি অবশ্যই স্থিতিযুক্ত বরাদ্দকৃত ফ্ল্যাট, যেখানে ** পয়েন্টারকে পয়েন্টার হিসাবে ধরে নেওয়া হবে।


@ জোন এল। আমি এই কথাটি বলব না যে ম্যালোক কার্নেল দ্বারা প্রয়োগ করা হয়েছে, তবে কার্নেলের আদিম (যেমন ব্রেক) শীর্ষে লিবিসি দ্বারা
ম্যানুয়েল সেলভা

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

2

মনে করুন, আমরা নীচে (সি 99) এর মতো সংজ্ঞায়িত এবং সূচনা করেছি a1এবং করেছি a2:

int a1[2][2] = {{142,143}, {144,145}};
int **a2 = (int* []){ (int []){242,243}, (int []){244,245} };

a1স্মৃতিতে ও অভিব্যক্তিতে প্লেইন অবিচ্ছিন্ন লেআউট সহ একজাতীয় 2D অ্যারে (int*)a1এটির প্রথম উপাদানটির দিকে নির্দেশকের কাছে মূল্যায়ন করা হয়:

a1 --> 142 143 144 145

a2ভিন্ন ভিন্ন 2D অ্যারে থেকে আরম্ভ করা হয় এবং এটি টাইপের একটি মানের পয়েন্টার int*, অর্থাত ডেরিফারেন্স এক্সপ্রেশনটি *a2মূল্যকে মূল্য হিসাবে মূল্যায়ন করে int*, মেমরি বিন্যাসটি অবিচ্ছিন্ন হতে হবে না:

a2 --> p1 p2
       ...
p1 --> 242 243
       ...
p2 --> 244 245

সম্পূর্ণ ভিন্ন মেমরি লেআউট এবং অ্যাক্সেস শব্দার্থক সত্ত্বেও, অ্যারে-অ্যাক্সেস এক্সপ্রেশনগুলির জন্য সি-ভাষার ব্যাকরণ একজাতীয় এবং ভিন্ন ভিন্ন 2D অ্যারে উভয়ের জন্য ঠিক একই দেখাচ্ছে:

  • অভিব্যক্তি অ্যারের বাইরে a1[1][0]মান আনবে144a1
  • অভিব্যক্তি অ্যারের বাইরে a2[1][0]মান আনবে244a2

সংকলক জানেন যে a1প্রকারভেদে অপারেশন করার জন্য অ্যাক্সেস-এক্সপ্রেশন থাকে int[2][2], যখন প্রকারভেদে অ্যাক্সেস-এক্সপ্রেশনটি a2পরিচালনা করে int**। উত্পন্ন সংসদীয় কোডটি একজাতীয় বা ভিন্নজাতীয় অ্যাক্সেস শব্দার্থকে অনুসরণ করবে।

কোডটি রান-টাইমে ক্রাশ হয় যখন টাইপের অ্যারে int[N][M]টাইপ-কাস্ট করা হয় এবং তারপরে টাইপ হিসাবে অ্যাক্সেস করা হয় int**, উদাহরণস্বরূপ:

((int**)a1)[1][0]   //crash on dereference of a value of type 'int'

1

একটি নির্দিষ্ট 2D অ্যারে অ্যাক্সেস করতে নীচে কোড হিসাবে দেখানো হয়েছে এমন অ্যারে ঘোষণার জন্য মেমরি মানচিত্রটি বিবেচনা করুন:

    0  1
a[0]0  1
a[1]2  3

প্রতিটি উপাদান অ্যাক্সেস করতে, ফাংশনটির প্যারামিটার হিসাবে আপনি আগ্রহী কোন অ্যারেটি পাস করার পক্ষে এটি যথেষ্ট। তারপরে প্রতিটি উপাদান পৃথকভাবে অ্যাক্সেস করতে কলামের জন্য অফসেট ব্যবহার করুন।

int a[2][2] ={{0,1},{2,3}};

void f1(int *ptr);

void f1(int *ptr)
{
    int a=0;
    int b=0;
    a=ptr[0];
    b=ptr[1];
    printf("%d\n",a);
    printf("%d\n",b);
}

int main()
{
   f1(a[0]);
   f1(a[1]);
    return 0;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.