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))
।
Windows पर आप / zp विकल्प के साथ cl.exe कंपियर का उपयोग करते समय एक बाइट के लिए संरेखण सेट कर सकते हैं ।
आमतौर पर सीपीयू के लिए डेटा का उपयोग करना आसान होता है जो कि प्लेटफॉर्म पर और कंपाइलर के आधार पर 4 (या 8) से अधिक है।
तो यह मूल रूप से संरेखण की बात है।
इसे बदलने के लिए आपके पास अच्छे कारण होने चाहिए।
यह बाइट संरेखण और पैडिंग के कारण हो सकता है ताकि संरचना आपके प्लेटफॉर्म पर बाइट्स (या शब्द) की एक समान संख्या के लिए निकले। लिनक्स पर C में उदाहरण के लिए, निम्नलिखित 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 बिट्स) हैं। उपरोक्त कार्यक्रम (लिनक्स पर gcc का उपयोग करके) आकार को 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
यह सभी देखें:
Microsoft Visual C के लिए:
http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs.80%29.aspx
और Microsoft के संकलक के साथ GCC का दावा संगतता:
http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
पिछले उत्तरों के अलावा, कृपया ध्यान दें कि पैकेजिंग की परवाह किए बिना, C ++ में कोई सदस्य-ऑर्डर-गारंटी नहीं है । कम्पाइलर संरचना में वर्चुअल टेबल पॉइंटर और बेस स्ट्रक्चर्स सदस्यों को जोड़ सकते हैं (और निश्चित रूप से)। यहां तक कि मानक (आभासी तंत्र कार्यान्वयन निर्दिष्ट नहीं है) द्वारा वर्चुअल टेबल का अस्तित्व सुनिश्चित नहीं किया जाता है और इसलिए कोई यह निष्कर्ष निकाल सकता है कि ऐसी गारंटी सिर्फ असंभव है।
मुझे पूरा यकीन है कि सदस्य-आदेश की गारंटी सी में है , लेकिन मैं उस पर भरोसा नहीं करूंगा, जब क्रॉस-प्लेटफॉर्म या क्रॉस-कंपाइलर प्रोग्राम लिख रहा हूं।
किसी संरचना का आकार उसके भागों के योग से अधिक होता है क्योंकि जिसे पैकिंग कहा जाता है। एक विशेष प्रोसेसर में एक पसंदीदा डेटा आकार होता है जो इसके साथ काम करता है। यदि 32-बिट्स (4 बाइट्स) में अधिकांश आधुनिक प्रोसेसर का पसंदीदा आकार है। इस तरह की सीमा पर डेटा पहुंचने पर मेमोरी को एक्सेस करना उन चीजों की तुलना में अधिक कुशल होता है जो उस आकार की सीमा को पूरा करती हैं।
उदाहरण के लिए। सरल संरचना पर विचार करें:
struct myStruct
{
int a;
char b;
int c;
} data;
यदि मशीन 32-बिट मशीन है और डेटा 32-बिट सीमा पर संरेखित है, तो हम एक तत्काल समस्या देखते हैं (कोई संरचना संरेखण मानकर)। इस उदाहरण में, हम मान लेते हैं कि संरचना डेटा 1024 (0x400 - ध्यान दें कि सबसे कम 2 बिट शून्य हैं, इसलिए डेटा 32-बिट सीमा से संरेखित है)। Data.a तक पहुंच ठीक काम करेगी क्योंकि यह एक सीमा पर शुरू होता है - 0x400। Data.b तक पहुंच भी ठीक काम करेगी, क्योंकि यह 0x404 पते पर है - एक और 32-बिट सीमा। लेकिन एक अलिखित संरचना 0x405 पते पर data.c लगा देगी। Data.c के 4 बाइट्स 0x405, 0x406, 0x407, 0x408 पर हैं। 32-बिट मशीन पर, सिस्टम एक मेमोरी चक्र के दौरान data.c को पढ़ेगा, लेकिन उसे केवल 4 बाइट्स में से 3 मिलेंगे (4 वीं बाइट अगली सीमा पर है)। तो, सिस्टम को 4 बाइट प्राप्त करने के लिए दूसरी मेमोरी एक्सेस करनी होगी,
अब, यदि पता 0x405 पर data.c लगाने के बजाय, कंपाइलर ने 3 बाइट्स द्वारा संरचना को गद्देदार किया और 0x408 पते पर data.c लगा दिया, तो सिस्टम को डेटा पढ़ने के लिए केवल 1 चक्र की आवश्यकता होगी, उस डेटा तत्व तक पहुंच समय में कटौती 50% से। प्रसंस्करण दक्षता के लिए पैडिंग स्वैप मेमोरी दक्षता। यह देखते हुए कि कंप्यूटर में बड़ी मात्रा में मेमोरी (कई गीगाबाइट) हो सकती है, कंपाइलरों को लगता है कि स्वैप (आकार में गति) एक उचित है।
दुर्भाग्य से, यह समस्या एक हत्यारा बन जाती है जब आप किसी नेटवर्क पर संरचनाएँ भेजने का प्रयास करते हैं या बाइनरी डेटा को बाइनरी फ़ाइल में लिखते हैं। किसी संरचना या वर्ग के तत्वों के बीच डाला गया पैडिंग फ़ाइल या नेटवर्क पर भेजे गए डेटा को बाधित कर सकता है। पोर्टेबल कोड (एक जो कई अलग-अलग कंपाइलरों में जाएगा) लिखने के लिए, आपको उचित "पैकिंग" सुनिश्चित करने के लिए संरचना के प्रत्येक तत्व को अलग से एक्सेस करना होगा।
दूसरी ओर, अलग-अलग कंपाइलरों में डेटा संरचना पैकिंग को प्रबंधित करने की अलग-अलग क्षमताएं हैं। उदाहरण के लिए, दृश्य C / C ++ में कंपाइलर #pragma पैक कमांड का समर्थन करता है। यह आपको डेटा पैकिंग और संरेखण को समायोजित करने की अनुमति देगा।
उदाहरण के लिए:
#pragma pack 1
struct MyStruct
{
int a;
char b;
int c;
short d;
} myData;
I = sizeof(myData);
मुझे अब 11 की लंबाई चाहिए। प्राग्म के बिना, मैं संकलक की डिफ़ॉल्ट पैकिंग के आधार पर 11 से 14 (और कुछ प्रणालियों के लिए, 32 से अधिक) तक कुछ भी हो सकता है।
#pragma pack
। यदि सदस्यों को उनके डिफ़ॉल्ट संरेखण पर आवंटित किया जाता है, तो मैं आमतौर पर कहूंगा कि संरचना पैक नहीं है ।
यदि आप स्पष्ट रूप से या स्पष्ट रूप से संरचना के संरेखण को निर्धारित करते हैं तो यह ऐसा कर सकता है। एक संरचना जो 4 से संरेखित होती है, हमेशा 4 बाइट्स की एक से अधिक होगी, भले ही उसके सदस्यों का आकार ऐसा हो जो 4 बाइट्स का एक से अधिक न हो।
इसके अलावा एक पुस्तकालय 32-बिट ints के साथ x86 के तहत संकलित किया जा सकता है और आप 64-बिट प्रक्रिया पर इसके घटकों की तुलना कर सकते हैं यदि आप हाथ से ऐसा कर रहे थे तो आपको एक अलग परिणाम मिलेगा।
C99 N1256 मानक ड्राफ्ट
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
6.5.3.4 आकार ऑपरेटर :
3 जब एक ऑपरेंड पर लागू किया जाता है जिसमें संरचना या संघ प्रकार होता है, तो परिणाम ऐसी वस्तु में बाइट्स की कुल संख्या है, जिसमें आंतरिक और अनुगामी पैडिंग शामिल है।
6.7.2.1 संरचना और संघ विनिर्देशक :
13 ... एक संरचना वस्तु के भीतर अनाम पैडिंग हो सकती है, लेकिन इसकी शुरुआत में नहीं।
तथा:
15 एक संरचना या संघ के अंत में अनाम पैडिंग हो सकती है।
नई C99 लचीली सरणी सदस्य सुविधा ( struct S {int is[];};
) पैडिंग को भी प्रभावित कर सकती है:
16 एक विशेष मामले के रूप में, एक से अधिक नामित सदस्य के साथ संरचना का अंतिम तत्व एक अपूर्ण सरणी प्रकार हो सकता है; इसे एक लचीली सरणी सदस्य कहा जाता है। ज्यादातर स्थितियों में, लचीले सरणी सदस्य को नजरअंदाज कर दिया जाता है। विशेष रूप से, संरचना का आकार ऐसा है जैसे कि लचीले सरणी सदस्य को छोड़ दिया गया था, सिवाय इसके कि इससे अधिक गद्दी गद्दी होगी जो कि चूक से होगी।
अनुलग्नक जे पोर्टेबिलिटी मुद्दे दोहराते हैं:
निम्नलिखित अनिर्दिष्ट हैं: ...
- संरचनाओं या यूनियनों में मूल्यों को संग्रहीत करते समय पैडिंग बाइट्स का मूल्य (6.2.6.1)
C ++ 11 N3337 मानक ड्राफ्ट
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
5.3.3 आकार :
2 जब किसी वर्ग पर लागू किया जाता है, तो परिणाम उस वर्ग की एक वस्तु में बाइट्स की संख्या होती है, जिसमें किसी प्रकार की वस्तुओं को किसी सरणी में रखने के लिए आवश्यक पैडिंग भी शामिल होती है।
9.2 कक्षा के सदस्य :
मानक-लेआउट संरचना ऑब्जेक्ट के लिए एक पॉइंटर, जिसे एक रीइंटरप्रिट_का उपयोग करके उपयुक्त रूप से परिवर्तित किया गया है, अपने प्रारंभिक सदस्य को इंगित करता है (या यदि वह सदस्य एक बिट-फ़ील्ड है, तो उस इकाई में जिसमें वह रहता है) और इसके विपरीत। [नोट: इसलिए एक मानक-लेआउट संरचना ऑब्जेक्ट के भीतर अनाम पैडिंग हो सकती है, लेकिन इसकी शुरुआत में नहीं, जैसा कि उपयुक्त संरेखण को प्राप्त करने के लिए आवश्यक है। - अंतिम नोट]
मैं केवल नोट को समझने के लिए पर्याप्त C ++ जानता हूं :-)
अन्य उत्तरों के अलावा, एक संरचना (लेकिन आमतौर पर नहीं होती है) में वर्चुअल फ़ंक्शन होते हैं, इस मामले में संरचना के आकार में vtbl के लिए स्थान भी शामिल होगा।
सी भाषा स्मृति में संरचनात्मक तत्वों के स्थान के बारे में कुछ स्वतंत्रता छोड़ देती है:
सी भाषा संरचना में तत्वों के लेआउट के प्रोग्रामर को कुछ आश्वासन प्रदान करती है:
तत्वों के संरेखण से संबंधित समस्याएं:
संरेखण कैसे काम करता है:
ps अधिक विस्तृत जानकारी यहाँ उपलब्ध है: "शमूएल पी। हर्बिसन, गाई एल.सेटेल सीए संदर्भ, (5.6.2-7.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 आर्किटेक्चर हमेशा गलत पते प्राप्त करने में सक्षम रहा है। हालांकि, यह धीमा है और जब मिसलिग्न्मेंट दो अलग-अलग कैश लाइनों को ओवरलैप करता है, तो यह दो कैश लाइनों को दिखाता है जब एक संरेखित एक्सेस केवल एक बेदखल करेगा।
कुछ आर्किटेक्चर को वास्तव में गलत पढ़े-लिखे और लिखने वाले, और एआरएम आर्किटेक्चर के शुरुआती संस्करणों (जो कि आज के सभी मोबाइल सीपीयू में विकसित हुए हैं) पर फंसना पड़ता है ... ठीक है, वे वास्तव में उन लोगों के लिए केवल खराब डेटा लौटाते हैं। (उन्होंने लो-ऑर्डर बिट्स को नजरअंदाज कर दिया।)
अंत में, ध्यान दें कि कैश लाइनें मनमाने ढंग से बड़ी हो सकती हैं, और कंपाइलर उन पर अनुमान लगाने या स्पेस-बनाम-स्पीड ट्रेडऑफ़ बनाने का प्रयास नहीं करता है। इसके बजाय, संरेखण निर्णय एबीआई का हिस्सा हैं और न्यूनतम संरेखण का प्रतिनिधित्व करते हैं जो अंततः समान रूप से कैश लाइन को भर देंगे।
टीएल; डीआर: संरेखण महत्वपूर्ण है।