เหตุใด int จึงใช้เวลา 12 ไบต์ในเครื่องบางเครื่อง


26

ฉันสังเกตเห็นสิ่งที่แปลกหลังจากรวบรวมรหัสนี้ในเครื่องของฉัน:

#include <stdio.h>

int main()
{
    printf("Hello, World!\n");

    int a,b,c,d;

    int e,f,g;

    long int h;

    printf("The addresses are:\n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x",
        &a,&b,&c,&d,&e,&f,&g,&h);

    return 0;
}

ผลที่ได้คือ โปรดสังเกตว่าระหว่างทุก int แอดเดรสจะมีความแตกต่าง 4 ไบต์ อย่างไรก็ตามระหว่าง int สุดท้ายและ int ยาวมีความแตกต่าง 12 ไบต์:

 Hello, World!
 The addresses are:

 da54dcac 
 da54dca8 
 da54dca4 
 da54dca0 
 da54dc9c 
 da54dc98 
 da54dc94 
 da54dc88

3
ใส่อีกintหลังจากhในซอร์สโค้ด hคอมไพเลอร์อาจจะนำมาใส่ในช่องว่างก่อน
ctrl-alt-delor

32
อย่าใช้ความแตกต่างระหว่างที่อยู่หน่วยความจำเพื่อกำหนดขนาด มีsizeofฟังก์ชั่นสำหรับสิ่งนั้น printf("size: %d ", sizeof(long));
Chris Schneider

10
คุณพิมพ์ที่อยู่ต่ำเพียง 4 ไบต์%xเท่านั้น โชคดีสำหรับคุณมันทำงานได้อย่างถูกต้องบนแพลตฟอร์มของคุณเพื่อส่งตัวชี้ที่มีสตริงรูปแบบที่คาดหวังunsigned intแต่พอยน์เตอร์และ ints มีขนาดแตกต่างกันใน ABIs จำนวนมาก ใช้%pเพื่อพิมพ์พอยน์เตอร์ในรหัสพกพา (มันเป็นเรื่องง่ายที่จะจินตนาการถึงระบบที่รหัสของคุณจะพิมพ์บน / ครึ่งล่างของ 4 ตัวชี้เป็นครั้งแรกแทนการครึ่งล่างของทั้งหมด 8)
ปีเตอร์ Cordes

5
@ChrisSchneider พิมพ์ใช้ size_t %zu@yoyo_fun การพิมพ์ที่อยู่การใช้งาน %pการใช้ตัวระบุรูปแบบที่ไม่ถูกต้องจะเรียกใช้พฤติกรรมที่ไม่ได้กำหนด
phuclv

2
@luu ไม่กระจายข้อมูลที่ผิด ไม่มีคอมไพเลอร์ที่เหมาะสมจะใส่ใจกับลำดับการประกาศตัวแปรใน C ถ้ามันสนใจก็ไม่มีเหตุผลว่าทำไมมันจะทำอย่างที่คุณอธิบาย
gnasher729

คำตอบ:


81

มันไม่ได้ใช้เวลา 12 ไบต์ใช้เวลาเพียง 8 แต่การจัดตำแหน่งเริ่มต้นสำหรับ int 8 ไบต์แบบยาวบนแพลตฟอร์มนี้คือ 8 ไบต์ ดังนั้นคอมไพเลอร์จำเป็นต้องย้าย int ที่มีความยาวไปยังที่อยู่ที่หารด้วย 8 ที่อยู่ "ชัดเจน", da54dc8c ไม่สามารถหารด้วย 8 ดังนั้นจึงมีช่องว่าง 12 ไบต์

คุณควรทดสอบสิ่งนี้ได้ หากคุณเพิ่ม int อื่นก่อนหน้าความยาวดังนั้นจึงมี 8 รายการเหล่านั้นคุณควรพบว่า int ที่อยู่ในแนวยาวนั้นจะตกลงกันโดยไม่ต้องย้าย ตอนนี้จะมีเพียง 8 ไบต์จากที่อยู่ก่อนหน้า

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


3
ความแตกต่างไม่ใช่ช่องว่าง
Deduplicator

10
"รวมถึงตัวแปรการสั่งซื้อซ้ำ" หากคอมไพเลอร์ตัดสินใจว่าคุณไม่ได้ใช้ตัวแปรสองตัวพร้อมกันก็สามารถทับซ้อนหรือซ้อนทับบางส่วนได้อย่างอิสระ ...
Roger Lipscombe

8
หรือแน่นอนเก็บไว้ในการลงทะเบียนแทนในกองซ้อน
หยุดทำร้ายโมนิก้า

11
@ OrangeDog ฉันไม่คิดว่าจะเกิดขึ้นหากที่อยู่ถูกนำมาใช้ในกรณีนี้ แต่โดยทั่วไปแล้วคุณถูกต้องแน่นอน
อเล็กซ์

5
@Alex: คุณสามารถได้รับสิ่งที่ตลกกับหน่วยความจำและลงทะเบียนเมื่อรับที่อยู่ การใช้ที่อยู่หมายความว่าจะต้องให้ที่ตั้งหน่วยความจำ แต่ไม่ได้หมายความว่าต้องใช้งานจริง หากคุณใช้ที่อยู่กำหนด 3 ให้กับมันและส่งไปยังฟังก์ชั่นอื่นมันอาจจะเขียน 3 ลงใน RDI และการโทรโดยไม่ต้องเขียนลงในหน่วยความจำ แปลกใจในตัวดีบัก
Zan Lynx

9

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

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


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

4
@yoyo_fun และหากคุณ จริงๆต้องการที่จะเข้าใจหน่วยความจำแล้วมีกระดาษที่มีชื่อเสียงในเรื่องfuturetech.blinkenlights.nl/misc/cpumemory.pdf
อเล็กซ์

1
@yoyo_fun มันค่อนข้างง่าย ตัวควบคุมหน่วยความจำบางตัวสามารถเข้าถึงความกว้างบิตของโปรเซสเซอร์ได้หลายตัวเท่านั้น (เช่นตัวประมวลผลแบบ 32 บิตสามารถร้องขอที่อยู่ 0-3, 4-7, 8-11 โดยตรงเท่านั้น) หากคุณขอที่อยู่ที่ไม่สอดคล้องกันโปรเซสเซอร์จะต้องทำการร้องขอหน่วยความจำสองครั้งจากนั้นนำข้อมูลเข้าสู่การลงทะเบียน ดังนั้นกลับไปที่ 32- บิตหากคุณต้องการค่าที่เก็บไว้ที่ที่อยู่ 1 ตัวประมวลผลต้องถามที่อยู่ 0-3, 4-7 จากนั้นรับไบต์จาก 1, 2, 3 และ 4 ไบต์ของ หน่วยความจำอ่านเสีย
phyrfox

2
จุดเล็ก ๆ น้อย ๆ แต่การเข้าถึงหน่วยความจำที่ไม่ตรงแนวอาจเป็นความผิดพลาดที่ไม่สามารถกู้คืนได้แทนที่จะเป็นปัญหาด้านประสิทธิภาพ ขึ้นอยู่กับสถาปัตยกรรม
Jon Chesterfield

1
@JonChesterfield - ใช่ นั่นเป็นเหตุผลที่ฉันแสดงความคิดเห็นว่าคำอธิบายที่ฉันให้ใช้กับสถาปัตยกรรมสมัยใหม่ส่วนใหญ่ (ซึ่งฉันหมายถึง x86 และ ARM) มีคนอื่นที่ทำงานในรูปแบบที่แตกต่างกัน แต่มีน้อยกว่าคนทั่วไป (น่าสนใจ: ARM เคยเป็นหนึ่งในสถาปัตยกรรมที่จำเป็นต้องมีการเข้าถึงที่สอดคล้อง แต่พวกเขาได้เพิ่มการจัดการการเข้าถึงที่ไม่ได้จัดแนวโดยอัตโนมัติในการแก้ไขในภายหลัง)
Jules

2

การทดสอบของคุณไม่จำเป็นต้องทดสอบสิ่งที่คุณคิดว่าเป็นเพราะไม่มีความต้องการของภาษาที่เกี่ยวข้องกับที่อยู่ของตัวแปรท้องถิ่นเหล่านี้กับแต่ละอื่น ๆ

คุณจะต้องใส่สิ่งเหล่านี้เป็นเขตข้อมูลในโครงสร้างเพื่อให้สามารถอนุมานบางอย่างเกี่ยวกับการจัดสรรพื้นที่จัดเก็บ

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

ในทางตรงกันข้ามมันจะไม่ได้รับอนุญาตให้แทรกตัวแปรชั่วคราวลงใน struct ดังนั้นหากคุณพิมพ์ที่อยู่ของฟิลด์ struct แทนคุณจะเปรียบเทียบรายการที่ได้รับการจัดสรรจากหน่วยความจำลอจิคัลเดียวกัน (โครงสร้าง)

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