ตัวแปรสแต็กถูกจัดแนวโดย GCC __attribute __ ((aligned (x))) หรือไม่


88

ฉันมีรหัสต่อไปนี้:

และฉันมีผลลัพธ์ต่อไปนี้:

ทำไมที่อยู่ของ a[0]ไม่ได้เป็นหลาย0x1000?

อะไรกันแน่ __attribute__((aligned(x))) ? ฉันเข้าใจคำอธิบายนี้ผิด?

ฉันใช้ gcc 4.1.2

คำตอบ:


98

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

C11 / C ++ 11 alignas(64) float a[4];ใช้ได้กับพลังของการจัดตำแหน่ง 2 แบบ
GNU C ก็เช่นกัน__attribute__((aligned(x)))เช่นเดียวกับที่คุณใช้

(ใน C11 #include <stdalign.h>สำหรับ#define alignas _Alignas: cppref )


แต่ในกรณีของคุณที่มีการจัดแนวขนาดใหญ่มากจนถึงขอบเขตหน้า 4k คุณอาจไม่ต้องการให้มันอยู่บนสแต็ก

เนื่องจากตัวชี้สแต็กอาจเป็นอะไรก็ได้เมื่อฟังก์ชันเริ่มทำงานจึงไม่มีวิธีใดที่จะจัดแนวอาร์เรย์ได้โดยไม่ต้องจัดสรรมากกว่าที่คุณต้องการและปรับเปลี่ยน (คอมไพเลอร์จะand rsp, -4096หรือเทียบเท่าและไม่ใช้ 0 ถึง 4088 ไบต์ใด ๆ ที่จัดสรรการแยกสาขาว่าพื้นที่นั้นมีขนาดใหญ่พอหรือไม่จะเป็นไปได้ แต่ไม่ได้ทำเนื่องจากการจัดตำแหน่งขนาดใหญ่ใหญ่กว่าขนาดของอาร์เรย์หรือพื้นที่อื่น ๆ ไม่ใช่กรณีปกติ)

หากคุณย้ายอาร์เรย์ออกจากฟังก์ชันและไปยังตัวแปรส่วนกลางก็ควรใช้งานได้ สิ่งอื่นที่คุณทำได้คือเก็บไว้เป็นตัวแปรท้องถิ่น (ซึ่งเป็นสิ่งที่ดีมาก) แต่จงทำให้มันstaticเป็น วิธีนี้จะป้องกันไม่ให้จัดเก็บในสแตก ระวังว่าทั้งสองวิธีนี้ไม่ปลอดภัยต่อเธรดหรือการเรียกซ้ำ - ปลอดภัยเนื่องจากอาร์เรย์จะมีสำเนาเพียงชุดเดียว

ด้วยรหัสนี้:

ฉันได้รับสิ่งนี้:

ซึ่งเป็นสิ่งที่คาดหวัง ด้วยรหัสเดิมของคุณฉันจะได้รับค่าแบบสุ่มเหมือนที่คุณทำ


11
+1 คำตอบที่ถูกต้อง ทางเลือกอื่นคือการทำให้อาร์เรย์ภายในเป็นแบบคงที่ การจัดแนวบนสแต็กเป็นปัญหาเสมอและควรหลีกเลี่ยงให้เป็นนิสัย
Dan Olson

โอ้ใช่ฉันไม่ได้คิดจะทำให้มันนิ่ง นั่นเป็นความคิดที่ดีเนื่องจากป้องกันการชนชื่อ ฉันจะแก้ไขคำตอบของฉัน
Zifre

3
โปรดทราบว่าการทำให้เป็นแบบคงที่ยังทำให้ไม่กลับเข้าที่และไม่ปลอดภัยต่อด้าย
ArchaeaSoftware

3
นอกจากนี้ gcc 4.6+ ยังจัดการสิ่งนี้ได้อย่างถูกต้องแม้ในสแต็ก
texthell

1
คำตอบนี้เคยถูกต้อง แต่ตอนนี้ไม่ใช่แล้ว gcc ที่เก่าถึง 4.6 อาจเก่ากว่ารู้วิธีจัดตำแหน่งตัวชี้สแต็กเพื่อใช้งาน C11 / C ++ 11 อย่างถูกต้องalignas(64)หรืออะไรก็ตามบนวัตถุที่มีการจัดเก็บอัตโนมัติ และแน่นอน GNU C__attribute((aligned((64)))
Peter Cordes

41

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

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660

ฉันได้ลองใช้รหัสของคุณด้านบนด้วย gcc สองเวอร์ชัน: 4.1.2 จากกล่อง RedHat 5.7 และล้มเหลวในทำนองเดียวกันกับปัญหาของคุณ (อาร์เรย์ในเครื่องไม่ได้อยู่ในแนวเดียวกับขอบเขต 0x1000 ไบต์) จากนั้นฉันลองใช้รหัสของคุณกับ gcc 4.4.6 บน RedHat 6.3 และทำงานได้อย่างไม่มีที่ติ (อาร์เรย์ในเครื่องถูกจัดแนว) ชาว Myth TV มีปัญหาคล้ายกัน (ซึ่งแพทช์ gcc ด้านบนดูเหมือนจะแก้ไขได้):

http://code.mythtv.org/trac/ticket/6535

อย่างไรก็ตามดูเหมือนว่าคุณจะพบข้อบกพร่องใน gcc ซึ่งดูเหมือนจะได้รับการแก้ไขในเวอร์ชันที่ใหม่กว่า


3
ตามข้อผิดพลาดที่เชื่อมโยง gcc 4.6 เป็นรุ่นแรกที่มีปัญหานี้ได้รับการแก้ไขอย่างสมบูรณ์สำหรับสถาปัตยกรรมทั้งหมด
texthell

นอกจากนั้นรหัสแอสเซมบลีที่สร้างโดย gcc เพื่อสร้างตัวแปรจัดแนวบนสแต็กนั้นแย่มากและไม่ได้รับการเพิ่มประสิทธิภาพ ดังนั้นการจัดสรรตัวแปรจัดแนวบนสแต็กจึงเหมาะสมmemalign()หรือไม่?
Jérôme Pouiller

13

GCC ล่าสุด (ทดสอบด้วย 4.5.2-8ubuntu4) ดูเหมือนว่าจะทำงานได้ตามที่คาดไว้โดยจัดเรียงอาร์เรย์อย่างถูกต้อง

ฉันเข้าใจ:


นี่เป็นเรื่องที่น่าแปลกใจเล็กน้อยเมื่อพิจารณาถึงการจัดสรรอาร์เรย์ในสแต็กหมายความว่าตอนนี้สแต็กเต็มไปด้วยรูหรือไม่?
ysap

หรือกองซ้อนของเขาอยู่ในแนว 16 ไบต์
user7116

9

การจัดตำแหน่งไม่ได้มีผลกับทุกประเภท คุณควรพิจารณาใช้โครงสร้างเพื่อดูแอตทริบิวต์ในการดำเนินการ:

จากนั้นคุณจะอ่าน:

ซึ่งเป็นสิ่งที่คุณคาดหวัง

แก้ไข: ผลัก @yzap และต่อไปนี้ @Caleb กรณีแสดงความคิดเห็นที่เป็นปัญหาแรกคือเนื่องจากรุ่น GCC เท่านั้น ฉันได้ตรวจสอบ GCC 3.4.6 เทียบกับ GCC 4.4.1 ด้วยซอร์สโค้ดของผู้ร้องขอ:

ตอนนี้เห็นได้ชัดว่า GCC เวอร์ชันเก่า (ก่อน 4.4.1) แสดงพยาธิสภาพของการจัดตำแหน่ง

หมายเหตุ 1: รหัสที่เสนอของฉันไม่ตอบคำถามซึ่งฉันเข้าใจว่าเป็น "การจัดตำแหน่งแต่ละฟิลด์ของอาร์เรย์"

หมายเหตุ 2: การนำ a [] ที่ไม่คงที่ภายใน main () และการคอมไพล์ด้วย GCC 3.4.6 จะทำลายคำสั่งการจัดตำแหน่งของอาร์เรย์ของโครงสร้าง แต่รักษาระยะห่าง 0x1000 ระหว่างโครงสร้าง ... ยังแย่อยู่! (ดูคำตอบ @zifre สำหรับวิธีแก้ปัญหา)


2
ตามคำตอบของ zifre ไม่ใช่ประเภท แต่เป็นความจริงที่ว่าคุณทำให้มันคงที่ในเวอร์ชันของคุณ
ysap

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