ขนาดของ (ตัวชี้บางตัว) เท่ากับสี่เสมอหรือไม่


227

ตัวอย่างเช่น sizeof(char*)ผลตอบแทน 4. ไม่int*, long long*ทุกอย่างที่ฉันได้พยายาม มีข้อยกเว้นสำหรับสิ่งนี้หรือไม่?


51
ทำเครื่องหมายลงทำไม เป็นคำถามที่ดีสำหรับมือใหม่
Martin York

2
ฉันสงสัยว่าอีกคำถามหนึ่งกำลังซ่อนอยู่ในคำถามนี้: "อะไรคือขนาดของ" หรืออาจเป็น "ทำไม sizeof <ตัวชี้ใด ๆ > == 4 มีอะไรพิเศษเกี่ยวกับ 4?" ฉันถูกไหม?

2
ขึ้นอยู่กับแพลตฟอร์มของคุณ การใช้งานส่วนใหญ่มีขนาดเท่ากันทุกประเภทของตัวชี้บนแพลตฟอร์มเฉพาะ
phoeagon

คำตอบ:


194

sizeof(char) == 1การรับประกันที่คุณได้รับก็คือ ไม่มีการค้ำประกันอื่น ๆ sizeof(int *) == sizeof(double *)รวมทั้งการรับประกันว่าไม่มี

ในทางปฏิบัติพอยน์เตอร์จะมีขนาด 2 ในระบบ 16 บิต (หากคุณสามารถหาได้) 4 ในระบบ 32 บิตและ 8 ในระบบ 64 บิต แต่ไม่มีสิ่งใดที่จะได้รับจากการพึ่งพา ขนาด.


96
และ 3 ไบต์บนระบบ 24 บิต ใช่ฉันทำงานแล้ว ยินดีต้อนรับสู่โลกของอุปกรณ์ฝังตัว
dwj

30
ฉันได้ทำงานกับระบบ 16 บิตพร้อมกับพอยน์เตอร์ 20 บิตเช่นกัน ฉันควรไปดูว่าขนาดของผลตอบแทน
เท่าไหร่

5
@monjardin: IIRC 8086 เป็นอย่างนั้น มีที่อยู่ 16 บิตและลงทะเบียนเซ็กเมนต์ 4 บิต ฉันเชื่อว่าตัวชี้ "NEAR" ปกติคือ 16 บิตและตัวชี้ที่ประกาศว่า "FAR" นั้นมากกว่า 24 อาจเป็นไปได้ แต่ฉันไม่แน่ใจ
rmeador

18
การรับประกันก็คือ sizeof (char *) == sizeof (void *) เพราะพวกเขาจะต้องมีการรับรองที่เหมือนกัน (วัตถุ [ขนาด] และค่า [ชุดของบิตที่เกี่ยวข้องกับการเป็นตัวแทน])
Johannes Schaub - litb

7
เนื่องจากคำถามจะถามถึงข้อยกเว้นจึงควรสังเกตว่าพอยน์เตอร์ฟังก์ชันสมาชิกคงที่มักจะมีขนาดแตกต่างจากพอยน์เตอร์ปกติและยังแตกต่างกันไปตามแพลตฟอร์มประเภท ฯลฯ นอกเหนือจาก +1
John5342

36

แม้บนแพลตฟอร์ม x86 32 บิตธรรมดาคุณสามารถรับขนาดตัวชี้ที่หลากหลายลองใช้ตัวอย่างนี้:

struct A {};

struct B : virtual public A {};

struct C {};

struct D : public A, public C {};

int main()
{
    cout << "A:" << sizeof(void (A::*)()) << endl;
    cout << "B:" << sizeof(void (B::*)()) << endl;
    cout << "D:" << sizeof(void (D::*)()) << endl;
}

ภายใต้ Visual C ++ 2008 ฉันได้รับ 4, 12 และ 8 สำหรับขนาดของพอยน์เตอร์กับสมาชิกฟังก์ชัน

เรย์มอนด์เฉินพูดคุยกันเกี่ยวกับเรื่องนี้ที่นี่


4
ตัวชี้ไปยังฟังก์ชั่นสมาชิกเป็นความเจ็บปวดที่แท้จริง โชคไม่ดีที่คอมไพเลอร์ทุกคนไม่ชอบคอมไพเลอร์ Digital Mars C ++ ซึ่งจะคืนค่า 4 ในทุกกรณี
dalle

gcc 4.72 พิมพ์ทั้งหมด 8 ... สิ่งนี้ไม่ได้กำหนดไว้ในมาตรฐาน c ++ หรือไม่
Gob00st

2
@ Gob00st: สิ่งเดียวที่กำหนดไว้คือ char คือ 1 ประเภทอื่น ๆ อาจเป็นขนาดใดก็ได้ที่เกี่ยวข้องกับคอมไพเลอร์นั้น ไม่มีข้อกำหนดสำหรับความสอดคล้องระหว่างตัวชี้ประเภทเหล่านี้
Eclipse

โอเคขอบคุณ. ดังนั้นไม่น่าแปลกใจที่ gcc & VC มีการนำไปใช้ที่แตกต่างกัน
Gob00st

5
@Eclipse ใช่มี: ถ่าน <= สั้น <= int <= ยาว <= ยาว
โคลจอห์นสัน

30

เป็นข้อยกเว้นอีกประการสำหรับรายการที่โพสต์แล้ว บนแพลตฟอร์ม 32 บิตพอยน์เตอร์สามารถใช้ 6, ไม่ใช่ 4 , ไบต์:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char far* ptr; // note that this is a far pointer
    printf( "%d\n", sizeof( ptr));
    return EXIT_SUCCESS;
}

หากคุณคอมไพล์โปรแกรมนี้ด้วย Open Watcom และเรียกใช้คุณจะได้รับ 6 เนื่องจากตัวชี้ไกลที่รองรับประกอบด้วยค่าออฟเซ็ต 32 บิตและ 16 บิต


5
ไม่ใช่เซ็กเมนต์ แต่เป็นตัวเลือก แต่มันไม่ได้เป็นส่วนหนึ่งของที่อยู่หน่วยความจำ แต่เป็นรายการดัชนีใน LDT หรือ GDT และมีแฟล็กการเข้าถึงบางอย่าง
Roee Shenberg

1
ทำไมถึงมีเซ็กเมนต์และออฟเซ็ตใน x86 ในขณะที่พื้นที่ที่อยู่แบน
phuclv

@ LưuVĩnhPhúcเพราะมันช่วยประหยัดพื้นที่สำหรับกรณีที่พบบ่อยมากของตัวชี้ใกล้ซึ่งสามารถเข้ารหัสได้สั้นลง
Christopher Creutzig

1
@ChristopherCreutzig ซึ่งหมายถึงกลุ่มที่ใช้ในการขยายพื้นที่ที่อยู่เช่น PAE?
phuclv

@ LưuVĩnhPhúcมันนานมากแล้วที่ฉันได้ประกอบอะไร 32 บิต ส่วนที่ฉันจำได้คือคุณสามารถประหยัดพื้นที่สำหรับพอยน์เตอร์ที่ชี้ใกล้กับรหัสที่คุณมี นอกจากนี้ไม่ใช่สถาปัตยกรรม 32 บิตทั้งหมดซึ่งแน่นอนว่าไม่ใช่ทั้งหมดที่ใช้ x86 - ใช้โมเดลหน่วยความจำแบบแบน ดูเช่นtenouk.com/Bufferoverflowc/Bufferoverflow1a.htmlสำหรับการสนทนาเพิ่มเติมเกี่ยวกับเรื่องนี้แม้ว่าอย่างที่ฉันบอกว่ามันใช้เวลานานและฉันไม่สามารถรับรองอะไร
Christopher Creutzig

24

หากคุณกำลังรวบรวมสำหรับเครื่อง 64 บิตอาจเป็น 8


2
แม้ว่าโดยปกติจะเป็นกรณีนี้ไม่จำเป็นต้องเป็นจริง ตัวอย่างเช่นหากคุณกำลังรวบรวมบนเครื่อง 64 บิตโดยที่ขนาดของคำคือ 64- บิตดังนั้นขนาดของ (char *) จะเป็น 1 ไม่ต้องพูดถึงประเภทตัวชี้ที่แปลกใหม่ในเครื่องทั่วไปเช่น Eclipse และ dmityugov เขียน
Kaz Dragon

@KazDragon, sizeof(char*)==1? คุณแน่ใจไหม? คุณไม่หมายถึงsize(char)==1เหรอ
Aaron McDaid

3
@AaronMcDaid ฉันหมายถึงขนาดของ size (char *) sizeof (char) อยู่เสมอ 1 แต่ถ้า word ของเครื่องของคุณคือ 64- บิตและสภาพแวดล้อมการพัฒนาของคุณถูกนำไปใช้ในลักษณะที่ CHAR_BITS = 64 ก็เป็นไปได้ที่ตัวชี้จะพอดีกับพื้นที่เดียวกันกับ char และด้วยเหตุนี้ ยังเป็น 1
Kaz Dragon

มันไม่เป็นความจริงในx32-abi sites.google.com/site/x32abi
phuclv

1
@ KazDragon ฉันกำลังสร้าง (ช้ามากเมื่อไม่ผัดวันประกันพรุ่ง) เครื่องที่มีคำ 16 บิตและไม่มีที่อยู่ไบต์ แม้ว่ามันจะไม่สามารถรัน C ได้
user253751

17

ในทางเทคนิคแล้วมาตรฐาน C รับประกันได้เพียงว่า sizeof (char) == 1 และส่วนที่เหลือนั้นขึ้นอยู่กับการใช้งาน แต่สำหรับสถาปัตยกรรม x86 ที่ทันสมัย ​​(เช่นชิป Intel / AMD) สามารถคาดการณ์ได้ค่อนข้าง

คุณอาจเคยได้ยินตัวประมวลผลที่อธิบายว่าเป็น 16 บิต, 32- บิต, 64- บิตเป็นต้นซึ่งมักจะหมายความว่าโปรเซสเซอร์ใช้ N-bits สำหรับจำนวนเต็ม เนื่องจากพอยน์เตอร์เก็บที่อยู่หน่วยความจำและที่อยู่หน่วยความจำเป็นจำนวนเต็มสิ่งนี้จะบอกคุณได้อย่างมีประสิทธิภาพว่าจะใช้บิตจำนวนเท่าใดสำหรับพอยน์เตอร์ sizeof มักจะวัดเป็นไบต์ดังนั้นโค้ดที่คอมไพล์แล้วสำหรับตัวประมวลผลแบบ 32 บิตจะรายงานขนาดตัวชี้เป็น 4 (32 บิต / 8 บิตต่อไบต์) และรหัสสำหรับตัวประมวลผลแบบ 64 บิตจะรายงานขนาดตัวชี้เป็น 8 (64 บิต / 8 บิตต่อไบต์) นี่เป็นที่ที่ข้อ จำกัด ของ RAM 4GB สำหรับโปรเซสเซอร์ 32 บิตมาจาก - หากที่อยู่หน่วยความจำแต่ละอันสอดคล้องกับไบต์เพื่อระบุหน่วยความจำเพิ่มเติมคุณต้องมีจำนวนเต็มมากกว่า 32 บิต


"คุณอาจเคยได้ยินตัวประมวลผลที่อธิบายว่าเป็น 16 บิต, 32- บิต, 64- บิต ฯลฯ ซึ่งมักจะหมายความว่าโปรเซสเซอร์ใช้ N-bits สำหรับจำนวนเต็ม" -> ฉันมีเครื่อง 64 บิต แต่ขนาดของ (int) คือ 4 ไบต์ หากข้อความของคุณเป็นจริงสิ่งนี้จะเป็นไปได้อย่างไร!
Sangeeth Saravanaraj

6
@SangeethSaravanaraj: สำหรับความเข้ากันได้ย้อนหลังกับรหัส 32 บิตพวกเขาตัดสินใจที่จะมี int ต่อไปเป็น 4 ไบต์และต้องการให้คุณเลือกใช้ชนิด 8 ไบต์โดยระบุ 'long' ยาวจริงขนาดดั้งเดิมของคำใน x86-64 วิธีหนึ่งในการดูนี้คือโดยทั่วไปคอมไพเลอร์จะวางโครงสร้างของคุณเพื่อจัดเรียงคำ (แม้ว่าอาจมีสถาปัตยกรรมที่ขนาดของคำและการจัดแนวไม่เกี่ยวข้อง) ดังนั้นหากคุณสร้าง struct ด้วย int (32 บิต) และเรียก sizeof () เมื่อมันกลับมา 8 คุณรู้ว่ามันขยายให้เป็นขนาดคำ 64- บิต
โจเซฟการ์วิน

@SangeethSaravanaraj: โปรดทราบว่าในทางทฤษฎีแล้วขนาดของคำในภาษาซีพียูและสิ่งที่คอมไพเลอร์เลือก 'int' จะแตกต่างกันไปตามอำเภอใจมันเป็นเพียงการประชุมสำหรับ 'int' ที่จะเป็นขนาดของคำก่อนที่ x86-64 จะมา มันใช้เวลานานในการบรรเทาความเข้ากันได้ย้อนหลัง
Joseph Garvin

ขอบคุณสำหรับคำอธิบาย! :)
Sangeeth Saravanaraj

7

ขนาดของตัวชี้โดยทั่วไปขึ้นอยู่กับสถาปัตยกรรมของระบบที่มีการใช้งาน ตัวอย่างเช่นขนาดของตัวชี้ใน 32 บิตคือ 4 ไบต์ (32 บิต) และ 8 ไบต์ (64 บิต) ในเครื่อง 64 บิต บิตประเภทในเครื่องนั้นไม่มีอะไรนอกจากที่อยู่หน่วยความจำที่สามารถมีได้ เครื่อง 32 บิตสามารถมี2^32พื้นที่ที่อยู่และเครื่อง 64 บิตสามารถมี2^64พื้นที่ที่อยู่ได้ไม่เกิน ดังนั้นตัวชี้ (ตัวแปรที่ชี้ไปยังตำแหน่งหน่วยความจำ) ควรจะสามารถชี้ไปยังที่อยู่หน่วยความจำใด ๆ ( 2^32 for 32 bit and 2^64 for 64 bit) ที่เครื่องเก็บ

ด้วยเหตุนี้เราจึงเห็นขนาดของตัวชี้เป็น 4 ไบต์ในเครื่อง 32 บิตและ 8 ไบต์ในเครื่อง 64 บิต


6

นอกเหนือจากความแตกต่าง 16/32/64 บิตแม้กระทั่งสิ่งอื่น ๆ สามารถเกิดขึ้นได้

มีเครื่องที่ sizeof (int *) จะเป็นหนึ่งค่าอาจเป็น 4 แต่ที่ sizeof (char *) มีขนาดใหญ่กว่า เครื่องที่ใช้คำพูดแทนไบต์ต้องมีการเพิ่ม "พอยน์เตอร์อักขระ" เพื่อระบุส่วนของคำที่คุณต้องการเพื่อนำมาตรฐาน C / C ++ มาใช้อย่างถูกต้อง

นี่เป็นสิ่งที่ผิดปกติอย่างมากเนื่องจากผู้ออกแบบฮาร์ดแวร์ได้เรียนรู้คุณค่าของความสามารถในการระบุแอดเดรสไบต์


4
คอมไพเลอร์ C สำหรับเครื่องเวกเตอร์ Cray เช่น T90 ทำสิ่งที่คล้ายกัน ที่อยู่ของฮาร์ดแวร์คือ 8 ไบต์และชี้ไปที่คำ 8 ไบต์ void*และchar*ได้รับการจัดการในซอฟต์แวร์และมีส่วนเสริมด้วยการชดเชยแบบ 3 บิตภายในคำ - แต่เนื่องจากไม่มีที่อยู่ 64- บิตที่อยู่จริงการชดเชยจะถูกเก็บไว้ในลำดับสูง 3 บิตของ 64 บิต คำ. ดังนั้นchar*และint*มีขนาดเท่ากัน แต่มีการนำเสนอภายในที่แตกต่างกันและรหัสที่ถือว่าพอยน์เตอร์นั้น "จริง" เพียงจำนวนเต็มอาจล้มเหลวได้ไม่ดี
Keith Thompson

5

พอยน์เตอร์ 8 บิตและ 16 บิตถูกใช้ในไมโครคอนโทรลเลอร์รายละเอียดต่ำที่สุด นั่นหมายถึงทุกเครื่องซักผ้าไมโครตู้เย็นทีวีเก่าและแม้แต่รถยนต์

คุณสามารถพูดได้ว่าสิ่งเหล่านี้ไม่มีส่วนเกี่ยวข้องกับการเขียนโปรแกรมในโลกแห่งความเป็นจริง แต่นี่คือตัวอย่างหนึ่งในโลกแห่งความจริง: Arduino ที่มี RAM 1-2-4k (ขึ้นอยู่กับชิป) ที่มีตัวชี้ 2 ไบต์

มันล่าสุดราคาถูกเข้าถึงได้สำหรับทุกคนและคุ้มค่ากับการเข้ารหัส


4

นอกเหนือจากสิ่งที่ผู้คนพูดเกี่ยวกับระบบ 64 บิต (หรืออะไรก็ตาม) แล้วยังมีตัวชี้ชนิดอื่นนอกเหนือจากตัวชี้ไปยังวัตถุ

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


3

จากสิ่งที่ฉันจำได้มันขึ้นอยู่กับขนาดของที่อยู่หน่วยความจำ ดังนั้นในระบบที่มีรูปแบบที่อยู่แบบ 32 บิตขนาดของมันจะคืนค่า 4 เนื่องจากเป็น 4 ไบต์


4
ไม่มีข้อกำหนดดังกล่าว ไม่มีแม้แต่ข้อกำหนดที่ sizeof (int ที่ไม่ได้ลงนาม) == sizeof (ลงนาม int) ขนาดของตัวชี้ไปยัง int จะเป็นตามคำจำกัดความ sizeof (int *) ไปยังขนาดถ่าน (ถ่าน *) ฯลฯ การใช้ข้อสันนิษฐานอื่น ๆ เป็นความคิดที่ไม่ดีสำหรับการพกพา
Mihai Limbășan

อ่าฉันเห็นแล้ว ขอบคุณสำหรับข้อมูล.
จะ Mc

1
ยังสามารถส่งคืน 2 ถ้า CHAR_BIT เป็น 16 sizeof () นับเป็นจำนวนตัวอักษรไม่ใช่ octets
MSalters

5
@Mihai: ใน C ++ sizeof (unsigned int) == sizeof (signed int)ข้อกำหนดนี้พบได้ใน 3.9.1 / 3 "สำหรับแต่ละมาตรฐานลงนามจำนวนเต็มชนิดมีอยู่เหมือนกัน ( แต่แตกต่างกัน) มาตรฐานชนิดจำนวนเต็มไม่ได้ลงนาม: unsigned char, unsigned short int, unsigned int, unsigned long intและunsigned long long int, แต่ละซึ่งตรงกับจำนวนเดียวกันของการจัดเก็บและมีความต้องการการจัดตำแหน่งเช่นเดียวกับที่สอดคล้องกันลงนามจำนวนเต็มชนิด "
Ben Voigt

3

โดยทั่วไป sizeof (อะไรที่สวยมาก) จะเปลี่ยนเมื่อคุณรวบรวมบนแพลตฟอร์มที่แตกต่างกัน บนแพลตฟอร์ม 32 บิตพอยน์เตอร์จะมีขนาดเท่ากันเสมอ บนแพลตฟอร์มอื่น ๆ (64 บิตเป็นตัวอย่างที่ชัดเจน) สิ่งนี้สามารถเปลี่ยนแปลงได้



3

ขนาดของตัวชี้และ int คือ 2 ไบต์ในคอมไพเลอร์ Turbo C บนเครื่อง windows 32 บิต

ขนาดของพอยเตอร์จึงเป็นคอมไพเลอร์เฉพาะ แต่โดยทั่วไปคอมไพเลอร์ส่วนใหญ่จะถูกนำไปใช้เพื่อสนับสนุนตัวแปรตัวชี้ 4 ไบต์ใน 32 บิตและตัวแปรตัวชี้ 8 ไบต์ในเครื่อง 64 บิต)

ขนาดของตัวชี้จึงไม่เหมือนกันในทุกเครื่อง


2

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


2

ในWin64 (Cygwin GCC 5.4)เรามาดูตัวอย่างด้านล่าง:

ก่อนอื่นให้ทดสอบโครงสร้างต่อไปนี้:

struct list_node{
    int a;
    list_node* prev;
    list_node* next;
};

struct test_struc{
    char a, b;
};

รหัสทดสอบด้านล่าง:

std::cout<<"sizeof(int):            "<<sizeof(int)<<std::endl;
std::cout<<"sizeof(int*):           "<<sizeof(int*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(double):         "<<sizeof(double)<<std::endl;
std::cout<<"sizeof(double*):        "<<sizeof(double*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(list_node):      "<<sizeof(list_node)<<std::endl;
std::cout<<"sizeof(list_node*):     "<<sizeof(list_node*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(test_struc):     "<<sizeof(test_struc)<<std::endl;
std::cout<<"sizeof(test_struc*):    "<<sizeof(test_struc*)<<std::endl;    

เอาท์พุทอยู่ด้านล่าง:

sizeof(int):            4
sizeof(int*):           8

sizeof(double):         8
sizeof(double*):        8

sizeof(list_node):      24
sizeof(list_node*):     8

sizeof(test_struc):     2
sizeof(test_struc*):    8

คุณจะเห็นว่าใน 64 บิตเป็นsizeof(pointer)8


1

ตัวชี้เป็นเพียงคอนเทนเนอร์สำหรับที่อยู่ บนเครื่อง 32 บิตช่วงที่อยู่ของคุณคือ 32 บิตดังนั้นตัวชี้จะเป็น 4 ไบต์เสมอ บนเครื่อง 64 บิตคุณมีช่วงที่อยู่ 64 บิตตัวชี้จะเป็น 8 ไบต์


1
บนเครื่อง 32- บิตที่มี 32- บิตขนาดของ (ถ่าน *) อาจเป็น 1
Robert Gamble

"... พร้อมไบต์ 32 บิต" ฉันไม่รู้ว่ามีสิ่งเหล่านี้อยู่จริง
Ed S.

1
บนเป็ดขนาด 32 บิตขนาดของ (ถ่าน *) ส่งคืน PI
Adriano Varoli Piazza

0

เพื่อความสมบูรณ์และความสนใจทางประวัติศาสตร์ในโลก 64 บิตมีการจัดทำแพลตฟอร์มที่แตกต่างกันตามขนาดของประเภทยาวและยาวชื่อ LLP64 และ LP64 ส่วนใหญ่อยู่ระหว่างระบบ Unix-type และ Windows มาตรฐานเก่าที่ชื่อว่า ILP64 ได้สร้าง int = 64-bit

Microsoft ดูแลรักษา LLP64 โดยที่ longlong = 64 bit wide แต่ long อยู่ที่ 32 เพื่อการพอร์ตที่ง่ายขึ้น

Type           ILP64   LP64   LLP64
char              8      8       8
short            16     16      16
int              64     32      32
long             64     64      32
long long        64     64      64
pointer          64     64      64

ที่มา: https://stackoverflow.com/a/384672/48026

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