কেন sizeof
অপারেটর কাঠামোর সদস্যদের মোট আকারের চেয়ে কোনও কাঠামোর জন্য আকার বড় করে দেয় ?
কেন sizeof
অপারেটর কাঠামোর সদস্যদের মোট আকারের চেয়ে কোনও কাঠামোর জন্য আকার বড় করে দেয় ?
উত্তর:
প্রান্তিককরণের সীমাবদ্ধতাগুলি পূরণ করতে প্যাডিং যুক্ত হওয়ার কারণে এটি। ডেটা স্ট্রাকচার সারিবদ্ধকরণ কর্মক্ষমতা এবং প্রোগ্রামের যথার্থতা উভয়কেই প্রভাবিত করে:
SIGBUS
)।এখানে একটি x86 প্রসেসরের জন্য আদর্শ সেটিংস ব্যবহারের উদাহরণ রয়েছে (সমস্ত ব্যবহৃত 32 এবং 64 বিট মোড):
struct X
{
short s; /* 2 bytes */
/* 2 padding bytes */
int i; /* 4 bytes */
char c; /* 1 byte */
/* 3 padding bytes */
};
struct Y
{
int i; /* 4 bytes */
char c; /* 1 byte */
/* 1 padding byte */
short s; /* 2 bytes */
};
struct Z
{
int i; /* 4 bytes */
short s; /* 2 bytes */
char c; /* 1 byte */
/* 1 padding byte */
};
const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */
বিন্যাসের মাধ্যমে সদস্যদের বাছাই করে কাঠামোর আকার হ্রাস করা যায় (আকারের অনুসারে বাছাই করা মূল ধরণের ক্ষেত্রে এটি যথেষ্ট) ( Z
উপরের উদাহরণে কাঠামোর মতো )।
গুরুত্বপূর্ণ দ্রষ্টব্য: সি এবং সি ++ উভয় মানের স্ট্যান্ডার্ড সারিবদ্ধকরণ বাস্তবায়ন-সংজ্ঞায়িত বলে উল্লেখ করে। অতএব প্রতিটি সংকলক পৃথক এবং বেমানান ডেটা বিন্যাসের ফলস্বরূপ, ডেটা আলাদাভাবে প্রান্তিককরণ চয়ন করতে পারে। এই কারণে, বিভিন্ন সংকলক দ্বারা ব্যবহৃত গ্রন্থাগারগুলির সাথে ডিল করার সময়, কম্পাইলাররা কীভাবে ডেটা সারিবদ্ধ করে তা বোঝা গুরুত্বপূর্ণ। কিছু সংকলক #pragma
কাঠামোর প্রান্তিককরণ সেটিংস পরিবর্তন করতে কমান্ড-লাইন সেটিংস এবং / অথবা বিশেষ বিবৃতি রয়েছে।
প্যাকিং এবং বাইট প্রান্তিককরণ, এখানে সিএফএকিউতে বর্ণিত :
এটা সারিবদ্ধ করার জন্য। অনেক প্রসেসর 2- এবং 4-বাইট পরিমাণে (যেমন ints এবং দীর্ঘ ints) অ্যাক্সেস করতে পারবেন না যদি তারা প্রতিটি উপায়ে ক্র্যাড হয়।
ধরুন আপনার এই কাঠামো রয়েছে:
struct { char a[3]; short int b; long int c; char d[3]; };
এখন, আপনি ভাবতে পারেন যে এই কাঠামোর স্মৃতিতে এই জাতীয় প্যাক করা সম্ভব হওয়া উচিত:
+-------+-------+-------+-------+ | a | b | +-------+-------+-------+-------+ | b | c | +-------+-------+-------+-------+ | c | d | +-------+-------+-------+-------+
তবে এটি প্রসেসরের উপর আরও অনেক সহজ, যদি সংকলক এটিকে এইভাবে সাজায়:
+-------+-------+-------+ | a | +-------+-------+-------+ | b | +-------+-------+-------+-------+ | c | +-------+-------+-------+-------+ | d | +-------+-------+-------+
বস্তাবন্দী সংস্করণে লক্ষ্য করুন যে আপনার এবং আমার পক্ষে খ এবং সি ক্ষেত্রগুলি কীভাবে মোড়ানো রয়েছে তা দেখতে আপনার পক্ষে কমপক্ষে খানিকটা কঠিন? সংক্ষেপে, এটি প্রসেসরের পক্ষেও শক্ত। অতএব, বেশিরভাগ সংকলকগুলি কাঠামোটি (যেমন অতিরিক্ত, অদৃশ্য ক্ষেত্রগুলি সহ) প্যাড করবে:
+-------+-------+-------+-------+ | a | pad1 | +-------+-------+-------+-------+ | b | pad2 | +-------+-------+-------+-------+ | c | +-------+-------+-------+-------+ | d | pad3 | +-------+-------+-------+-------+
s
তারপরে (উত্তরে দেখানো প্রান্তিককরণ দেওয়া থাকে)। পয়েন্টারটি কেবল তখনই সংরক্ষণ করা হয় যদি অ্যারেগুলির একটি পরিবর্তনশীল আকার থাকে (যেমন, পরিবর্তে ঘোষিত হয়েছিল ) তবে তারপরে উপাদানগুলি অন্য কোথাও সংরক্ষণ করতে হবে। &s.a == &s
&s.d == &s + 12
a
char a[]
char a[3]
আপনি যদি উদাহরণস্বরূপ ব্যবহারের জন্য জিসিসির সাথে কাঠামোর একটি নির্দিষ্ট আকার রাখতে চান __attribute__((packed))
।
উইন্ডোজে আপনি / জেডপি বিকল্পের সাথে ক্লি.এক্স.কম সংযোগকারী ব্যবহার করার সময় এক বাইটে প্রান্তিককরণ সেট করতে পারেন ।
সাধারণত সিপিইউর ডেটা অ্যাক্সেস করা সহজ যা 4 (বা 8) এর একাধিক, প্ল্যাটফর্ম এবং সংকলক নির্ভর করে।
সুতরাং এটি মূলত প্রান্তিককরণের বিষয়।
এটি পরিবর্তন করার জন্য আপনার ভাল কারণ থাকতে হবে।
এটি বাইট সারিবদ্ধকরণ এবং প্যাডিংয়ের কারণে হতে পারে যাতে আপনার প্ল্যাটফর্মের কাঠামোটি এমনকি একাধিক বাইট (বা শব্দ) বেরিয়ে আসে। উদাহরণস্বরূপ লিনাক্সে সি-তে নিম্নলিখিত 3 কাঠামো:
#include "stdio.h"
struct oneInt {
int x;
};
struct twoInts {
int x;
int y;
};
struct someBits {
int x:2;
int y:6;
};
int main (int argc, char** argv) {
printf("oneInt=%zu\n",sizeof(struct oneInt));
printf("twoInts=%zu\n",sizeof(struct twoInts));
printf("someBits=%zu\n",sizeof(struct someBits));
return 0;
}
বাইটস এর মাপের সদস্যদের যথাক্রমে 4 বাইট (32 বিট), 8 বাইট (2x 32 বিট) এবং 1 বাইট (2 + 6 বিট) হয় উপরের প্রোগ্রামটি (জিসিসি ব্যবহার করে লিনাক্সে) 4, 8 এবং 4 হিসাবে আকারগুলি মুদ্রণ করে - যেখানে শেষ কাঠামোটি প্যাডযুক্ত যাতে এটি একটি একক শব্দ (আমার 32 বিট প্ল্যাটফর্মে 4 x 8 বিট বাইট) থাকে।
oneInt=4
twoInts=8
someBits=4
:2
এবং :6
আসলে 2 এবং 6 বিট নির্দিষ্ট করছে, এক্ষেত্রে 32 বিট পূর্ণ পূর্ণসংখ্যার নয়। someBits.x, মাত্র 2 বিট হ'ল কেবল 4 সম্ভাব্য মান সংরক্ষণ করতে পারে: 00, 01, 10 এবং 11 (1, 2, 3 এবং 4)। এটা কোনো কিছু হলো? বৈশিষ্ট্য সম্পর্কে এখানে একটি নিবন্ধটি দেওয়া হয়েছে: geeksforgeeks.org/bit-fields-c
আরো দেখুন:
মাইক্রোসফ্ট ভিজ্যুয়াল সি এর জন্য:
http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs.80%29.aspx
এবং জিসিসি মাইক্রোসফ্টের সংকলকটির সাথে সামঞ্জস্যতার দাবি করেছে:
http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
পূর্ববর্তী উত্তরগুলি ছাড়াও, দয়া করে নোট করুন যে প্যাকেজিং নির্বিশেষে, সি ++ তে কোনও সদস্যের অর্ডার-গ্যারান্টি নেই । সংকলকগণ কাঠামোটিতে ভার্চুয়াল টেবিল পয়েন্টার এবং বেস স্ট্রাকচারের সদস্যদের যুক্ত করতে পারে (এবং অবশ্যই করবে)। এমনকি ভার্চুয়াল টেবিলের অস্তিত্বও মান দ্বারা নিশ্চিত করা হয় না (ভার্চুয়াল প্রক্রিয়া বাস্তবায়ন নির্দিষ্ট করা হয়নি) এবং সুতরাং যে কেউ এই সিদ্ধান্তটি গ্রহণ করতে পারে যে এই ধরনের গ্যারান্টি কেবল অসম্ভব।
আমি বেশ নিশ্চিত সদস্য-অর্ডার করা হয় সি নিশ্চিত , কিন্তু আমি এটি গণনা না, যখন একটি ক্রস-প্ল্যাটফর্ম বা ক্রস কম্পাইলার প্রোগ্রাম লেখা।
কোনও কাঠামোর আকার তার অংশগুলির যোগফলের চেয়ে বড় যা প্যাকিং বলে। একটি নির্দিষ্ট প্রসেসরের একটি পছন্দসই ডেটা আকার থাকে যা এটি দিয়ে কাজ করে। 32-বিট (4 বাইট) যদি সর্বাধিক আধুনিক প্রসেসরের পছন্দসই আকার। যখন এই ধরণের সীমানায় ডেটা থাকে তখন মেমোরি অ্যাক্সেস করা সেই আকারের সীমানাকে অবিচ্ছিন্ন করে তোলে এমন জিনিসগুলির চেয়ে বেশি দক্ষ।
উদাহরণ স্বরূপ. সাধারণ কাঠামো বিবেচনা করুন:
struct myStruct
{
int a;
char b;
int c;
} data;
যদি মেশিনটি 32-বিট মেশিন হয় এবং ডেটা 32-বিট সীমানায় সংযুক্ত থাকে, আমরা একটি তাত্ক্ষণিক সমস্যা দেখতে পাই (কোনও কাঠামোর সারিবদ্ধতা ধরে না রেখে)। এই উদাহরণে, আসুন আমরা ধরে নিই যে কাঠামোর ডেটা 1024 ঠিকানায় শুরু হয় (0x400 - নোট করুন যে সর্বনিম্ন 2 বিট শূন্য, সুতরাং ডেটাটি 32-বিট সীমানায় সংযুক্ত করা হয়)। ডেটা.এ অ্যাক্সেস সূক্ষ্মভাবে কাজ করবে কারণ এটি একটি সীমানা - 0x400 এ শুরু হয়। ডেটা.বিতে অ্যাক্সেসও ঠিকঠাক কাজ করবে, কারণ এটি 0x404 ঠিকানায় রয়েছে - অন্য 32-বিট সীমানা। তবে একটি স্বাক্ষরবিহীন কাঠামো 0x405 ঠিকানায় ডেটা। ডেটা। এর 4 বাইট 0x405, 0x406, 0x407, 0x408 এ রয়েছে। একটি 32-বিট মেশিনে, সিস্টেমটি একটি মেমরি চক্র চলাকালীন ডেটা.c পড়ত তবে 4 টি বাইটের মধ্যে কেবল 3 পায় (চতুর্থ বাইটটি পরবর্তী সীমানায় থাকে)। সুতরাং, 4 র্থ বাইট পেতে সিস্টেমকে একটি দ্বিতীয় মেমরি অ্যাক্সেস করতে হবে,
এখন, যদি 0x405 ঠিকানায় ডেটা। কোড স্থাপনের পরিবর্তে, সংকলকটি কাঠামোটি 3 বাইট দ্বারা প্যাড করে এবং ডাটা কোড.এক্স 0x408 ঠিকানায় রাখে, তবে তথ্যটি পড়ার জন্য সিস্টেমটি কেবলমাত্র 1 টি চক্রের প্রয়োজন হবে, সেই তথ্য উপাদানটিতে অ্যাক্সেসের সময় কাটাতে হবে 50% দ্বারা প্যাডিং প্রক্রিয়াজাতকরণ দক্ষতার জন্য মেমরি দক্ষতা অদলবদল করে। কম্পিউটারগুলিতে প্রচুর পরিমাণে মেমরি থাকতে পারে (বহু গিগাবাইট), সংকলকরা মনে করেন যে অদলবদল (গতির চেয়ে বেশি গতি) একটি যুক্তিসঙ্গত।
দুর্ভাগ্যক্রমে, আপনি যখন কোনও নেটওয়ার্কের উপর কাঠামো প্রেরণ করার চেষ্টা করেন বা বাইনারি ফাইলে বাইনারি ডেটা লেখেন তখন দুর্ভাগ্যক্রমে, এই সমস্যা হত্যাকারী হয়ে ওঠে। কাঠামো বা শ্রেণীর উপাদানগুলির মধ্যে Theোকানো প্যাডিং ফাইল বা নেটওয়ার্কে প্রেরিত ডেটা ব্যাহত করতে পারে। পোর্টেবল কোড লেখার জন্য (এটি বিভিন্ন ধরণের বিভিন্ন সংকলককে যাবে) সঠিক "প্যাকিং" নিশ্চিত করতে আপনাকে সম্ভবত কাঠামোর প্রতিটি উপাদান পৃথকভাবে অ্যাক্সেস করতে হবে।
অন্যদিকে, বিভিন্ন সংকলকগুলির ডেটা স্ট্রাকচার প্যাকিং পরিচালনা করার জন্য বিভিন্ন ক্ষমতা রয়েছে। উদাহরণস্বরূপ, ভিজ্যুয়াল সি / সি ++ এ সংকলক # প্রগমা প্যাক কমান্ড সমর্থন করে। এটি আপনাকে ডেটা প্যাকিং এবং প্রান্তিককরণ সামঞ্জস্য করতে অনুমতি দেবে।
উদাহরণ স্বরূপ:
#pragma pack 1
struct MyStruct
{
int a;
char b;
int c;
short d;
} myData;
I = sizeof(myData);
আমার এখন ১১ এর দৈর্ঘ্য হওয়া উচিত the প্রগমা ব্যতীত, আমি সংকলকের ডিফল্ট প্যাকিংয়ের উপর নির্ভর করে 11 থেকে 14 (এবং কিছু সিস্টেমে, তত 32 হিসাবে) হতে পারি।
#pragma pack
। সদস্যদের তাদের ডিফল্ট প্রান্তিককরণ বরাদ্দ হয়, আমি সাধারণত বলতে চাই কাঠামো না বস্তাবন্দী।
যদি আপনি স্পষ্টভাবে বা স্পষ্টভাবে স্ট্রাক্টের সারিবদ্ধতা সেট করে থাকেন তবে এটি এটি করতে পারে। ৪ টি সারিবদ্ধ একটি স্ট্রাক্ট সর্বদা 4 বাইটের একাধিক হবে এমনকি তার সদস্যদের আকার এমন কিছু হবে যা 4 বাইটের একাধিক নয়।
এছাড়াও একটি লাইব্রেরি x86 এর অধীনে 32-বিট ইনট সহ সংকলিত হতে পারে এবং আপনি যদি 64-বিট প্রসেসের সাথে এর উপাদানগুলির তুলনা করছেন তবে আপনি যদি হাতের সাহায্যে এটি করছিলেন তবে আপনাকে আলাদা ফলাফল দেবে।
C99 N1256 স্ট্যান্ডার্ড খসড়া
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
6.5.3.4 আকার অপারেটর :
3 যখন কাঠামো বা ইউনিয়নের ধরণের কোনও অপরেন্ডে প্রয়োগ করা হয়, ফলাফলটি অভ্যন্তরীণ এবং ট্রেলিং প্যাডিং সহ এমন কোনও সামগ্রীর মোট বাইটের সংখ্যা।
7.7.২.১ কাঠামো এবং ইউনিয়ন নির্দিষ্টকরণকারী :
13 ... স্ট্রাকচার অবজেক্টের মধ্যে নামবিহীন প্যাডিং থাকতে পারে, তবে এটির শুরুতে নয়।
এবং:
15 কোনও কাঠামো বা ইউনিয়নের শেষে নামবিহীন প্যাডিং থাকতে পারে।
নতুন C99 নমনীয় অ্যারে সদস্য বৈশিষ্ট্য ( struct S {int is[];};
) প্যাডিংগুলিকেও প্রভাবিত করতে পারে:
16 একটি বিশেষ কেস হিসাবে, একাধিক নামযুক্ত সদস্যের সাথে কাঠামোর শেষ উপাদানটির অসম্পূর্ণ অ্যারে প্রকার থাকতে পারে; এটিকে নমনীয় অ্যারে সদস্য বলা হয়। বেশিরভাগ পরিস্থিতিতে, নমনীয় অ্যারে সদস্যকে উপেক্ষা করা হয়। বিশেষত, কাঠামোর আকারটি হ'ল যেন নমনীয় অ্যারে সদস্য বাদ দেওয়া হয় তবে এটি বাদ দেওয়ার অর্থের চেয়ে আরও বেশি ট্র্যাডিং প্যাডিং থাকতে পারে।
সংযুক্ত জে বহনযোগ্য বিষয়গুলির পুনরাবৃত্তি:
নিম্নলিখিতগুলি অনির্ধারিত: ...
- কাঠামো বা ইউনিয়নে মান সংরক্ষণ করার সময় প্যাডিং বাইটের মান (.2.২..1.১)
সি ++ 11 এন 3337 স্ট্যান্ডার্ড খসড়া
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
5.3.3 মাপ :
2 যখন কোনও ক্লাসে প্রয়োগ করা হয়, ফলাফল হ'ল শ্রেণীর কোনও অবজেক্টে বাইটের সংখ্যা সহ সেই ধরণের অবজেক্টগুলিকে অ্যারে রাখার জন্য প্রয়োজনীয় কোনও প্যাডিং।
৯.২ শ্রেণির সদস্য :
একটি স্ট্যান্ডার্ড-লেআউট স্ট্রাক্ট অবজেক্টের একটি পয়েন্টার, একটি পুনরায় ব্যাখ্যা-কাস্টম ব্যবহার করে উপযুক্ত রূপান্তরিত করে, এর প্রাথমিক সদস্যকে নির্দেশ করে (অথবা যদি সেই সদস্যটি বিট-ফিল্ড হয়, তবে এটি যে ইউনিটে থাকে সেখানে) এবং তদ্বিপরীত। [দ্রষ্টব্য: সুতরাং একটি মান-লেআউট স্ট্রাক্ট অবজেক্টের মধ্যে নামবিহীন প্যাডিং থাকতে পারে তবে উপযুক্ত প্রান্তিককরণ অর্জনের জন্য প্রয়োজনীয় হিসাবে এটি শুরুতে নয়। - শেষ নোট]
আমি কেবল নোটটি বুঝতে যথেষ্ট সি ++ জানি :-)
অন্যান্য উত্তরের পাশাপাশি, একটি স্ট্রাক্টের ভার্চুয়াল ফাংশন থাকতে পারে (তবে সাধারণত থাকে না), সেক্ষেত্রে স্ট্রাক্টের আকারটিও ভিটিবিএলের স্থান অন্তর্ভুক্ত করবে।
সি ভাষা স্মৃতিতে কাঠামোগত উপাদানগুলির অবস্থান সম্পর্কে কিছু স্বাধীনতা সংকলন করে:
সি ভাষা কাঠামোর উপাদানগুলির বিন্যাসের প্রোগ্রামারকে কিছু নিশ্চয়তা প্রদান করে:
উপাদানগুলির সারিবদ্ধকরণ সম্পর্কিত সমস্যাগুলি:
সারিবদ্ধকরণ কীভাবে কাজ করে:
পিএস আরও বিশদ তথ্য এখানে পাওয়া যায়: "স্যামুয়েল পি। হার্বিসন, গাই এল। স্টিল সিএ রেফারেন্স, (5.6.2 - 5.6.7)"
ধারণাটি হ'ল গতি এবং ক্যাশে বিবেচনার জন্য অপারেশনগুলি তাদের প্রাকৃতিক আকারের সাথে সংযুক্ত ঠিকানাগুলি থেকে পড়া উচিত। এটি ঘটতে, সংকলক প্যাডগুলি কাঠামোর সদস্যগণ যাতে নীচের সদস্য বা নীচের কাঠামোটি সারিবদ্ধ করা হবে।
struct pixel {
unsigned char red; // 0
unsigned char green; // 1
unsigned int alpha; // 4 (gotta skip to an aligned offset)
unsigned char blue; // 8 (then skip 9 10 11)
};
// next offset: 12
X86 আর্কিটেকচার সর্বদা বিভ্রান্ত ঠিকানাগুলি আনতে সক্ষম হয়েছে। যাইহোক, এটি ধীরে ধীরে এবং যখন মিস্যালাইনমেন্ট দুটি পৃথক ক্যাশে লাইনকে ওভারল্যাপ করে, তখন এটি দুটি ক্যাশে লাইন সরিয়ে দেয় যখন একটি সারিবদ্ধ অ্যাক্সেস কেবল একটিকে উচ্ছেদ করে।
কিছু আর্কিটেকচারকে প্রকৃতপক্ষে বিভ্রান্তিযুক্ত পড়া এবং লেখাগুলি এবং এআরএম আর্কিটেকচারের প্রাথমিক সংস্করণগুলি (যা আজকের সমস্ত মোবাইল সিপিইউতে বিবর্তিত হয়েছিল) আটকে আছে ... ভাল, তারা আসলে সেইগুলির জন্য খারাপ ডেটা ফিরিয়ে দিয়েছে। (তারা নিম্ন-অর্ডার বিট উপেক্ষা করেছে।)
শেষ অবধি, লক্ষ্য করুন যে ক্যাশে লাইনগুলি নির্বিচারে বড় হতে পারে এবং সংকলক সেগুলি অনুমান করার বা কোনও স্থান-বনাম-গতির ট্রেড অফ করার চেষ্টা করে না। পরিবর্তে, প্রান্তিককরণ সিদ্ধান্তগুলি এবিআইয়ের অংশ এবং ন্যূনতম প্রান্তিককরণের প্রতিনিধিত্ব করে যা শেষ পর্যন্ত সমানভাবে একটি ক্যাশে লাইন পূরণ করবে।
টিএল; ডিআর: প্রান্তিককরণ গুরুত্বপূর্ণ।