มีวิธีมาตรฐานหรือทางเลือกมาตรฐานในการบรรจุ struct ใน c หรือไม่?


13

เมื่อการเขียนโปรแกรมใน CI พบว่ามันมีค่ามากที่จะแพ็ค structs โดยใช้__attribute__((__packed__))แอตทริบิวต์GCCs ดังนั้นฉันสามารถแปลงกลุ่มของหน่วยความจำที่มีโครงสร้างเป็นอาร์เรย์ไบต์ที่จะส่งผ่านบัสได้อย่างง่ายดายบันทึกไปยังหน่วยเก็บหรือนำไปใช้กับบล็อกของรีจิสเตอร์ โครงสร้างแบบแพ็ครับประกันว่าเมื่อได้รับการปฏิบัติเป็นอาร์เรย์ไบต์จะไม่มีการบรรจุใด ๆ ซึ่งทั้งสองสิ้นเปลืองความเสี่ยงด้านความปลอดภัยที่เป็นไปได้และอาจเข้ากันไม่ได้เมื่อเชื่อมต่อกับฮาร์ดแวร์

ไม่มีมาตรฐานสำหรับโครงสร้างการจัดวางที่ทำงานได้ในคอมไพเลอร์ C ทั้งหมดหรือไม่? ถ้าไม่เช่นนั้นฉันคิดว่านี่เป็นคุณสมบัติที่สำคัญอย่างยิ่งสำหรับการเขียนโปรแกรมระบบ? ผู้ใช้ภาษา C ต้นไม่พบความต้องการโครงสร้างการจัดเก็บหรือมีทางเลือกบางอย่างหรือไม่?


การใช้ structs ข้ามโดเมนที่คอมไพล์เป็นความคิดที่แย่มากโดยเฉพาะอย่างยิ่งที่จะชี้ไปที่ฮาร์ดแวร์ (ซึ่งเป็นอีกหนึ่งคอมไพล์โดเมน) โครงสร้างแพ็กเก็ตเป็นเพียงกลลวงเดียวสำหรับการทำเช่นนี้พวกเขามีผลข้างเคียงมากมายดังนั้นจึงมีวิธีแก้ปัญหาอื่น ๆ อีกมากมายสำหรับปัญหาของคุณที่มีผลข้างเคียงน้อยกว่า
old_timer

คำตอบ:


12

ในโครงสร้างสิ่งที่สำคัญคือออฟเซ็ตของสมาชิกแต่ละคนจากที่อยู่ของแต่ละอินสแตนซ์ของโครงสร้าง ไม่มากนักเป็นเรื่องของการบรรจุสิ่งของแน่น ๆ

อย่างไรก็ตามอาร์เรย์มีความสำคัญกับวิธีการ "บรรจุ" กฎใน C คือองค์ประกอบของอาร์เรย์แต่ละตัวมีขนาดเท่ากับ N ไบต์จากก่อนหน้าโดยที่ N คือจำนวนไบต์ที่ใช้ในการจัดเก็บประเภทนั้น

แต่ด้วยโครงสร้างจึงไม่จำเป็นต้องมีความเท่าเทียมกัน

นี่คือตัวอย่างหนึ่งของแผนการบรรจุที่แปลก:

Freescale (ผู้ผลิตไมโครคอนโทรลเลอร์ยานยนต์) สร้างไมโครที่มีหน่วยประมวลผลร่วมหน่วยเวลา (google สำหรับ eTPU หรือ TPU) มันมีสองขนาดข้อมูลพื้นเมือง 8 บิตและ 24 บิตและเกี่ยวข้องเฉพาะกับจำนวนเต็ม

โครงสร้างนี้:

struct a
{
  U24 elementA;
  U24 elementB;
};

จะเห็นแต่ละ U24 เก็บบล็อก 32 บิตของตัวเอง แต่เฉพาะในพื้นที่ที่อยู่สูงสุด

นี้:

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

จะมีสอง U24s เก็บไว้ในที่อยู่ติดกัน 32 บล็อกบิตและ U8 จะถูกเก็บไว้ใน "หลุม" ในด้านหน้าของ U24 elementAแรก

แต่คุณสามารถบอกคอมไพเลอร์ให้เก็บทุกอย่างไว้ในบล็อก 32 บิตของตัวเองถ้าคุณต้องการ มันแพงกว่า RAM แต่ใช้คำแนะนำน้อยกว่าสำหรับการเข้าถึง

"การบรรจุ" ไม่ได้หมายถึง "แพ็คแน่น" - มันหมายถึงรูปแบบบางอย่างสำหรับการจัดองค์ประกอบของ struct WRT ออฟเซ็ต

ไม่มีโครงร่างทั่วไปมันขึ้นอยู่กับคอมไพเลอร์ + สถาปัตยกรรม


1
ถ้าคอมไพเลอร์สำหรับการจัดเรียง TPU struct bที่จะย้ายelementCก่อนที่องค์ประกอบอื่น ๆ แล้วมันไม่ได้เป็นไปตามกลไกการเรียบเรียง C ไม่อนุญาตการจัดองค์ประกอบใหม่ใน C
Bart van Ingen Schenau

สิ่งที่น่าสนใจ แต่ U24 ไม่ใช่ประเภท C แบบมาตรฐานen.m.wikipedia.org/wiki/C_data_typesดังนั้นจึงไม่น่าแปลกใจที่ผู้รวบรวมจะถูกบังคับให้จัดการในลักษณะที่ค่อนข้างแปลก
satur9nine

มันแชร์แรมกับซีพียูหลักซึ่งมีขนาดคำ 32 บิต แต่โปรเซสเซอร์นี้มี ALU ซึ่งเกี่ยวข้องกับ 24 บิตหรือ 8 บิตเท่านั้น ดังนั้นจึงมีรูปแบบสำหรับการจัดวางตัวเลข 24 บิตในคำ 32 บิต ไม่ใช่มาตรฐาน แต่เป็นตัวอย่างที่ดีของการบรรจุและการจัดตำแหน่ง ตกลงมันไม่ได้มาตรฐานมาก
RichColours

6

เมื่อการเขียนโปรแกรมใน CI พบว่ามันมีค่ามากที่จะแพ็ค structs โดยใช้ GCCs __attribute__((__packed__))[... ]

เมื่อคุณพูดถึง__attribute__((__packed__))ฉันถือว่าคุณมีความตั้งใจที่จะกำจัดช่องว่างภายในstruct(ทำให้สมาชิกแต่ละคนมีการจัดตำแหน่งแบบ 1 ไบต์)

ไม่มีมาตรฐานสำหรับโครงสร้างการจัดวางที่ทำงานได้ในคอมไพเลอร์ C ทั้งหมดหรือไม่?

... และคำตอบคือ "ไม่" การแพ็ดดิ้งและการจัดเรียงข้อมูลสัมพันธ์กับ struct (และอาร์เรย์ที่ต่อเนื่องกันของ structs ในสแต็กหรือฮีป) มีอยู่ด้วยเหตุผลสำคัญ ในหลาย ๆ เครื่องการเข้าถึงหน่วยความจำที่ไม่ได้จัดแนวอาจทำให้ประสิทธิภาพลดลงอย่างมีนัยสำคัญ (แม้ว่าจะมีฮาร์ดแวร์ใหม่น้อยลง) ในบางกรณีที่เกิดขึ้นได้ยากการเข้าถึงหน่วยความจำที่ไม่ตรงแนวทำให้เกิดข้อผิดพลาดของบัสที่ไม่สามารถกู้คืนได้ (อาจทำให้ระบบปฏิบัติการทั้งหมดล้มเหลว)

เนื่องจากมาตรฐาน C มุ่งเน้นไปที่การพกพาจึงมีความรู้สึกเพียงเล็กน้อยที่จะมีวิธีมาตรฐานในการกำจัดการขยายทั้งหมดในโครงสร้างและเพียงแค่อนุญาตให้มีการวางแนวโดยพลการเนื่องจากมีความเสี่ยงที่จะทำให้รหัส C ไม่ใช่แบบพกพา

วิธีที่ปลอดภัยที่สุดและแบบพกพามากที่สุดเพื่อให้ข้อมูลดังกล่าวส่งออกไปยังแหล่งข้อมูลภายนอกในทางที่จะช่วยลดช่องว่างทั้งหมดคือการเป็นอันดับจาก / 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 (โครงสร้างของอาร์เรย์) เช่นนี้ (สมมติว่าเราต้องการFooอินสแตนซ์8 ตัว):

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

เราทำลายโครงสร้างอย่างมีประสิทธิภาพ ในกรณีนี้การแสดงหน่วยความจำจะเป็นดังนี้:

0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______

... และนี่:

01234567
yyyyyyyy

... ไม่มีช่องว่างภายในและไม่เกี่ยวข้องกับการเข้าถึงหน่วยความจำที่ไม่ตรงแนวเนื่องจากเราไม่ได้เข้าถึงเขตข้อมูลเหล่านี้เป็นออฟเซ็ตของโครงสร้างที่อยู่ แต่แทนที่จะเป็นออฟเซ็ตฐานที่อยู่สำหรับอาร์เรย์ที่มีประสิทธิภาพ

นอกจากนี้ยังนำโบนัสของการเข้าถึงลำดับที่รวดเร็วขึ้นซึ่งเป็นผลมาจากข้อมูลที่น้อยลงในการใช้งาน (ไม่มีช่องว่างที่ไม่เกี่ยวข้องในการผสมเพื่อชะลออัตราการใช้ข้อมูลที่เกี่ยวข้องของเครื่อง) และยังเป็นไปได้ที่คอมไพเลอร์ .

ข้อเสียคือมันเป็นรหัส PITA นอกจากนี้ยังอาจมีประสิทธิภาพน้อยลงสำหรับการเข้าถึงแบบสุ่มด้วยก้าวที่ใหญ่กว่าในระหว่างเขตข้อมูลซึ่งบ่อยครั้งที่ตัวแทน AoS หรือ AoSoA จะทำได้ดีกว่า แต่นั่นเป็นวิธีมาตรฐานวิธีหนึ่งในการขจัดช่องว่างภายในและจัดเก็บสิ่งต่าง ๆ ให้แน่นที่สุดเท่าที่จะทำได้โดยไม่ต้องขันด้วยการจัดตำแหน่งของทุกสิ่ง


2
ฉันยืนยันว่ามีวิธีการระบุเค้าโครงโครงสร้างอย่างชัดเจนจะช่วยเพิ่มความสะดวกในการพกพาได้อย่างหนาแน่น ในขณะที่บางเลย์เอาต์จะนำไปสู่รหัสที่มีประสิทธิภาพมากในบางเครื่องและรหัสที่ไม่มีประสิทธิภาพมากในรหัสอื่น ๆ รหัสจะทำงานบนเครื่องทั้งหมดและจะมีประสิทธิภาพอย่างน้อยบางส่วน ในทางตรงกันข้ามหากไม่มีคุณสมบัติดังกล่าววิธีเดียวที่จะทำให้การทำงานของรหัสบนเครื่องทั้งหมดน่าจะทำให้มันไม่มีประสิทธิภาพในทุกเครื่องหรือใช้การรวมกันของแมโครและการรวบรวมตามเงื่อนไขเพื่อรวมกันเป็นอุปกรณ์พกพาที่รวดเร็ว โปรแกรมและแบบพกพาช้าในแหล่งเดียวกัน
supercat

แนวคิดใช่ถ้าเราสามารถระบุทุกอย่างลงไปเป็นบิตและไบต์แทนความต้องการการจัดตำแหน่ง endianness ฯลฯ และมีคุณสมบัติที่ช่วยให้การควบคุมที่ชัดเจนดังกล่าวใน C ในขณะที่เลือกที่จะหย่ามันต่อจากสถาปัตยกรรมพื้นฐาน ... แต่ฉันแค่พูดถึง ATM - ในปัจจุบันโซลูชันแบบพกพาที่สุดสำหรับซีเรียลไลเซอร์คือการเขียนในลักษณะที่ไม่ได้ขึ้นอยู่กับการแสดงบิตและไบต์ที่แน่นอนและการจัดตำแหน่งของชนิดข้อมูล น่าเสียดายที่เราไม่มีวิธีการใช้ตู้ ATM ในการทำอย่างอื่นอย่างมีประสิทธิภาพ (ใน C)

5

ไม่มีสถาปัตยกรรมทั้งหมดที่เหมือนกันเพียงเปิดตัวเลือก 32 บิตในหนึ่งโมดูลและดูว่าเกิดอะไรขึ้นเมื่อใช้ซอร์สโค้ดเดียวกันและคอมไพเลอร์เดียวกัน ลำดับไบต์เป็นข้อ จำกัด อื่นที่รู้จักกันดี โยนในการเป็นตัวแทนจุดลอยตัวและปัญหาที่เกิดขึ้นแย่ลง การใช้การบรรจุเพื่อส่งข้อมูลไบนารีนั้นไม่ใช่แบบพกพา เพื่อให้ได้มาตรฐานดังนั้นจึงใช้งานได้จริงคุณจะต้องกำหนดข้อกำหนดภาษา C ใหม่

แม้ว่าทั่วไปแล้วการใช้ Pack เพื่อส่งข้อมูลแบบไบนารีเป็นแนวคิดที่ไม่ดีหากคุณต้องการความปลอดภัยความสะดวกในการพกพาหรืออายุการใช้งานที่ยาวนาน คุณอ่าน binary blob จากแหล่งลงในโปรแกรมของคุณบ่อยแค่ไหน คุณตรวจสอบค่าทั้งหมดว่ามีเหตุผลบ่อยแค่ไหนที่แฮ็กเกอร์หรือการเปลี่ยนแปลงโปรแกรมไม่ได้รับข้อมูล เมื่อถึงเวลาที่คุณได้เขียนรหัสรูทีนการตรวจสอบคุณอาจใช้การนำเข้าและส่งออกตามปกติ


0

ทางเลือกที่ธรรมดามากคือ "padding":

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

สิ่งนี้จะถือว่าโครงสร้างจะไม่ถูกเพิ่มเป็น 8 ไบต์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.