क्या सी में एक संरचना को पैक करने के लिए एक मानक तरीका या मानक विकल्प है?


13

जब CI में प्रोग्रामिंग को GCCs __attribute__((__packed__))विशेषता का उपयोग करके स्ट्रक्चर को पैक करने के लिए अमूल्य पाया गया है, तो मैं आसानी से अस्थिर मेमोरी के एक संरचित चंक को बाइट्स के एक सरणी में परिवर्तित कर सकता हूं, जिसे एक बस में प्रेषित किया जा सकता है, स्टोरेज में सहेजा गया या रजिस्टरों के ब्लॉक पर लागू किया जा सकता है। पैक्ड स्ट्रक्चर्स गारंटी देते हैं कि जब बाइट्स के एक सरणी के रूप में व्यवहार किया जाता है, तो इसमें कोई पैडिंग नहीं होगी, जो कि व्यर्थ है, एक संभावित सुरक्षा जोखिम और संभवतः हार्डवेयर के साथ अंतर होने पर असंगत है।

क्या पैकिंग संरचनाओं के लिए कोई मानक नहीं है जो सभी सी कंपाइलरों में काम करता है? यदि नहीं, तो मैं यह सोचकर कि यह सिस्टम प्रोग्रामिंग के लिए एक महत्वपूर्ण विशेषता है, में एक अतिशयोक्ति है? क्या सी भाषा के शुरुआती उपयोगकर्ताओं को पैकिंग संरचनाओं की आवश्यकता नहीं मिली या किसी प्रकार का विकल्प है?


संकलित डोमेन में स्ट्रक्चर्स का उपयोग करना एक बहुत बुरा विचार है, विशेष रूप से हार्डवेयर पर इंगित करने के लिए (जो कि एक अन्य संकलित डोमेन है)। पैकेट संरचनाएं ऐसा करने के लिए केवल एक चाल है, उनके बहुत सारे दुष्प्रभाव हैं, इसलिए कम दुष्प्रभावों के साथ आपकी समस्याओं के कई अन्य समाधान हैं, और वे अधिक पोर्टेबल हैं।
Old_timer

जवाबों:


12

एक संरचना में, प्रत्येक संरचना उदाहरण के पते से प्रत्येक सदस्य की ऑफसेट क्या मायने रखती है। इतनी बात नहीं है कि कैसे चीजें कसकर पैक की जाती हैं।

एक सरणी, हालांकि, मायने रखती है कि यह "पैक" कैसे है। C में नियम यह है कि प्रत्येक ऐरे एलिमेंट पहले से N N बाइट्स है, जहाँ N उस प्रकार को स्टोर करने के लिए उपयोग किए जाने वाले बाइट्स की संख्या है।

लेकिन एक संरचना के साथ, एकरूपता की ऐसी कोई आवश्यकता नहीं है।

यहाँ एक अजीब पैकिंग योजना का एक उदाहरण है:

Freescale (जो ऑटोमोटिव माइक्रोकंट्रोलर बनाते हैं) एक माइक्रो बनाते हैं जिसमें टाइम प्रोसेसिंग यूनिट को-प्रोसेसर (eTPU या TPU के लिए google) होता है। इसमें दो मूल डेटा आकार, 8 बिट्स और 24 बिट्स हैं, और केवल पूर्णांकों के साथ संबंधित है।

यह संरचना:

struct a
{
  U24 elementA;
  U24 elementB;
};

देखेंगे कि प्रत्येक U24 ने अपना 32 बिट ब्लॉक संग्रहीत किया है, लेकिन केवल उच्चतम पता क्षेत्र में।

इस:

struct b
{
  U24 elementA;
  U24 elementB;
  U8  elementC;
};

निकटवर्ती 32 बिट ब्लॉकों में दो U24 संग्रहीत होंगे, और U8 को पहले U24 के सामने "छेद" में संग्रहीत किया जाएगा elementA

लेकिन आप संकलक से कह सकते हैं कि यदि आप चाहें तो सब कुछ अपने 32 बिट ब्लॉक में पैक कर सकते हैं; यह रैम पर अधिक महंगा है, लेकिन एक्सेस के लिए कम निर्देशों का उपयोग करता है।

"पैकिंग" का अर्थ यह नहीं है कि "कसकर पैक करें" - इसका अर्थ है कि एक संरचनात्मक wrt ऑफ़सेट के तत्वों की व्यवस्था के लिए कुछ योजना।

कोई सामान्य योजना नहीं है, यह कंपाइलर + आर्किटेक्चर पर निर्भर है।


1
यदि टीपीयू के लिए संकलक अन्य तत्वों में से किसी से पहले struct bस्थानांतरित करने के लिए पुनर्व्यवस्थित elementCकरता है, तो यह एक अनुरूपण सी संकलक नहीं है। C
बार्ट वैन इनगेन शेनौ

दिलचस्प है, लेकिन U24 एक मानक सी प्रकार नहीं है। en.m.wikipedia.org/wiki/C_data_types इसलिए यह आश्चर्य की बात नहीं है कि कुछ हद तक अजीब तरीके से इसे संभालने के लिए मजबूर किया जाता है।
satur9nine

यह मुख्य सीपीयू कोर के साथ रैम साझा करता है जिसमें 32 बिट्स का शब्द आकार होता है। लेकिन इस प्रोसेसर में एक ALU है जो केवल 24 बिट्स या 8 बिट्स से संबंधित है। इसलिए इसमें 32 बिट शब्दों में 24 बिट संख्याओं को रखने की योजना है। गैर-मानक, लेकिन पैकिंग और संरेखण का एक बड़ा उदाहरण है। सहमत, यह बहुत गैर-मानक है।
रिचकोलॉर्स

6

जब CI में प्रोग्रामिंग पाया गया है कि यह GCCs का उपयोग करके पैक्स को पैक करने के लिए अमूल्य है __attribute__((__packed__))[...]

चूंकि आप उल्लेख करते हैं __attribute__((__packed__)), मेरा मानना ​​है कि आपका इरादा एक के भीतर सभी पैडिंग को खत्म structकरना है (प्रत्येक सदस्य के पास 1-बाइट संरेखण है)।

क्या पैकिंग संरचनाओं के लिए कोई मानक नहीं है जो सभी सी कंपाइलरों में काम करता है?

... और जवाब नहीं है"। एक संरचना के सापेक्ष पैडिंग और डेटा संरेखण (और स्टैक या ढेर में संरचनाओं के सन्निहित सरणियाँ) एक महत्वपूर्ण कारण के लिए मौजूद हैं। कई मशीनों पर, अनलॉग्ड मेमोरी एक्सेस संभावित रूप से महत्वपूर्ण प्रदर्शन जुर्माना हो सकता है (हालांकि कुछ नए हार्डवेयर पर कम होता जा रहा है)। कुछ दुर्लभ मामलों में, गलत तरीके से मेमोरी एक्सेस एक बस त्रुटि की ओर जाता है जो अपरिवर्तनीय है (पूरे ऑपरेटिंग सिस्टम को क्रैश भी कर सकता है)।

चूंकि सी मानक पोर्टेबिलिटी पर केंद्रित है, इसलिए संरचना में सभी पैडिंग को खत्म करने के लिए एक मानक तरीका होना बहुत कम समझ में आता है और सिर्फ मनमाने क्षेत्रों को गलत तरीके से पेश करने की अनुमति देता है, क्योंकि ऐसा करने से संभावित रूप से सी कोड गैर-पोर्टेबल बनाने का जोखिम होगा।

इस तरह के डेटा को एक बाहरी स्रोत में आउटपुट करने का सबसे सुरक्षित और सबसे पोर्टेबल तरीका है कि सभी पैडिंग को खत्म करने के लिए बाइट स्ट्रीम से / के बजाय केवल आपके की कच्ची मेमोरी सामग्री पर भेजने की कोशिश करना है structs। यह आपके कार्यक्रम को इस क्रमिक संदर्भ के बाहर प्रदर्शन के दंड से पीड़ित होने से भी रोकता है, और आपको structबिना किसी फेंक-बंद किए और संपूर्ण सॉफ़्टवेयर को गड़बड़ किए बिना स्वतंत्र रूप से नए फ़ील्ड जोड़ने की अनुमति भी देगा । यह आपको अंतर्मन और उससे जुड़ी चीजों से निपटने के लिए कुछ जगह देगा, अगर वह कभी चिंता का विषय बन जाए।

संकलक-विशिष्ट निर्देशों तक पहुंचने के बिना सभी पैडिंग को खत्म करने का एक तरीका है, हालांकि यह केवल तभी लागू होता है जब खेतों के बीच सापेक्ष क्रम कोई फर्क नहीं पड़ता। कुछ इस तरह दिया गया:

struct Foo
{
    double x;  // assume 8-byte alignment
    char y;    // assume 1-byte alignment
               // 7 bytes of padding for first field
};

... हमें इन क्षेत्रों से युक्त संरचना के पते के सापेक्ष संरेखित मेमोरी एक्सेस के लिए पैडिंग की आवश्यकता है, जैसे:

0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......

... जहाँ .इंगित करता है गद्दी। प्रत्येक xको प्रदर्शन के लिए 8-बाइट सीमा (और कभी-कभी सही व्यवहार) के लिए भी संरेखित करना चाहिए।

आप एक SoA (सरणी की संरचना) प्रतिनिधित्व का उपयोग करके पोर्टेबल तरीके से पैडिंग को समाप्त कर सकते हैं (मान लें कि हमें 8 शेष राशि की आवश्यकता है Foo):

struct Foos
{
   double x[8];
   char y[8];
};

हमने संरचना को प्रभावी ढंग से ध्वस्त कर दिया है। इस स्थिति में, मेमोरी प्रतिनिधित्व इस प्रकार हो जाता है:

0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______

... और इस:

01234567
yyyyyyyy

... कोई अधिक पैडिंग ओवरहेड, और बिना गलत मेमोरी मेमोरी को शामिल किए बिना, क्योंकि हम इन डेटा फ़ील्ड्स को संरचना पते की ऑफसेट के रूप में एक्सेस नहीं कर रहे हैं, बल्कि इसके बजाय आधार पते की एक ऑफसेट के रूप में प्रभावी रूप से एक सरणी है।

यह उपभोग करने के लिए दोनों कम डेटा के परिणामस्वरूप अनुक्रमिक पहुंच के लिए तेजी से बोनस का वहन करता है (मशीन की प्रासंगिक डेटा खपत दर को धीमा करने के लिए मिश्रण में कोई अधिक अप्रासंगिक गद्दी नहीं है) और संकलक के लिए संभावित रूप से प्रसंस्करण के लिए एक क्षमता बहुत तुच्छ है। ।

नकारात्मक पक्ष यह है कि यह कोड के लिए एक PITA है। यह खेतों के बीच बड़ी स्ट्राइड के साथ यादृच्छिक अभिगम के लिए संभावित रूप से कम कुशल है, जहां अक्सर AoS या AoSoA प्रतिनिधि बेहतर प्रदर्शन करेंगे। लेकिन यह एक मानक तरीका है पैडिंग को खत्म करने और चीजों को कसकर पैक करने के लिए जितना संभव हो सब कुछ संरेखण के साथ पेंच किए बिना।


2
मेरा तर्क है कि स्पष्ट रूप से बड़े पैमाने पर पोर्टेबिलिटी को बढ़ाने के लिए संरचना लेआउट को निर्दिष्ट करने का एक साधन होगा । जबकि कुछ लेआउट में कुछ मशीनों पर बहुत ही कुशल कोड होता है और दूसरों पर बहुत अक्षम कोड होता है, कोड सभी मशीनों पर काम करेगा और कम से कम कुछ पर कुशल होगा। इसके विपरीत, इस तरह की सुविधा के अभाव में, सभी मशीनों पर कोड का काम करने का एकमात्र तरीका सभी मशीनों पर या तो इसे अयोग्य बनाने की संभावना है या फिर तेजी से गैर-पोर्टेबल को संयोजित करने के लिए मैक्रोज़ और सशर्त संकलन का एक गुच्छा का उपयोग करें कार्यक्रम और एक ही स्रोत में एक धीमी पोर्टेबल।
सुपरकैट

वैचारिक रूप से हां, अगर हम बिट्स और बाइट्स प्रतिनिधित्व, संरेखण आवश्यकताओं, धीरज, आदि के लिए सब कुछ निर्दिष्ट कर सकते हैं और इसमें एक विशेषता है जो सी में इस तरह के स्पष्ट नियंत्रण की अनुमति देता है जबकि वैकल्पिक रूप से इसे अंतर्निहित वास्तुकला से आगे तलाक देता है ... लेकिन मैं बस के बारे में था। एटीएम - वर्तमान में एक धारावाहिक के लिए सबसे पोर्टेबल समाधान है इसे इस तरह से लिखना है कि यह सटीक बिट्स और बाइट्स के प्रतिनिधित्व और डेटा प्रकारों के संरेखण पर निर्भर नहीं करता है। दुर्भाग्य से हमारे पास अन्यथा (प्रभावी रूप से) सी करने के लिए एटीएम का अभाव है।

5

सभी आर्किटेक्चर समान नहीं हैं, बस एक मॉड्यूल पर 32 बिट विकल्प चालू करें, और देखें कि समान स्रोत कोड और समान कंपाइलर का उपयोग करते समय क्या होता है। बाइट ऑर्डर एक और प्रसिद्ध सीमा है। फ्लोटिंग पॉइंट प्रतिनिधित्व में फेंको और समस्याएं बदतर हो जाती हैं। बाइनरी डेटा भेजने के लिए पैकिंग का उपयोग करना गैर-पोर्टेबल है। इसे मानकीकृत करने के लिए इसलिए यह व्यावहारिक रूप से प्रयोग करने योग्य था, आपको सी भाषा विनिर्देश को फिर से परिभाषित करना होगा।

यद्यपि आम है, बाइनरी डेटा भेजने के लिए पैक का उपयोग करना एक बुरा विचार है यदि आप डेटा की सुरक्षा, पोर्टेबिलिटी या दीर्घायु चाहते हैं। कितनी बार आप अपने कार्यक्रम में एक स्रोत से एक बाइनरी बूँद पढ़ते हैं। आप कितनी बार सभी मानों की जांच कर रहे हैं कि डेटा के लिए एक हैकर या प्रोग्राम में बदलाव नहीं हुआ है? जब तक आप एक चेक रूटीन को कोडित करते हैं, तब तक आप आयात और निर्यात दिनचर्या का उपयोग कर सकते हैं।


0

एक बहुत ही सामान्य विकल्प है "नाम पैडिंग":

struct s {
  short s1;
  char  c2;
  char  reserved; // Padding
};

यह मान लेता है कि संरचना को 8 बाइट्स के लिए गद्देदार नहीं किया जाएगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.