IntPtr คืออะไรกันแน่


171

ผ่านการใช้ IntelliSense และดูรหัสของคนอื่นฉันได้เจอกับIntPtrประเภทนี้ ทุกครั้งที่จำเป็นต้องใช้ฉันใส่nullหรือIntPtr.Zeroพบฟังก์ชั่นส่วนใหญ่ในการทำงานทุกครั้ง มันคืออะไรกันแน่และเมื่อไร / ทำไมมันถึงใช้?

คำตอบ:


158

มันเป็น "จำนวนเต็มขนาดเนทีฟ (เฉพาะแพลตฟอร์ม)" มันแสดงเป็นภายในvoid*แต่เปิดเผยเป็นจำนวนเต็ม คุณสามารถใช้ทุกครั้งที่คุณต้องการจัดเก็บพอยน์เตอร์ที่ไม่มีการจัดการและไม่ต้องการใช้unsafeรหัส IntPtr.Zeroได้อย่างมีประสิทธิภาพNULL(ตัวชี้โมฆะ)


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

93
โดยทั่วไป (ข้ามภาษาโปรแกรม) ตัวชี้คือตัวเลขที่แสดงตำแหน่งทางกายภาพในหน่วยความจำ nullชี้เป็น (เกือบเสมอ) หนึ่งที่จุด 0 และได้รับการยอมรับอย่างกว้างขวางว่าเป็น "ไม่ได้ชี้ไปอะไร" เนื่องจากระบบมีจำนวนหน่วยความจำที่รองรับแตกต่างกันจึงไม่จำเป็นต้องใช้จำนวนไบต์เดียวกันเพื่อเก็บหมายเลขนั้นเสมอดังนั้นเราจึงเรียก "จำนวนเต็มขนาดดั้งเดิม" ที่สามารถเก็บตัวชี้บนระบบใด ๆ
Sam Harwell

5
+1 สำหรับความคิดเห็นนั้น @ 280Z28 นั่นคือคำอธิบายที่สั้นที่สุดของพอยน์เตอร์ที่ฉันเคยเห็น
BlueRaja - Danny Pflughoeft

9
มันเรียกว่า IntPtr เนื่องจากการใช้งานจากโค้ดเนทีฟที่ไม่มีการจัดการ C / C ++ คุณจะต้องใช้ประเภทอะนาล็อก: intptr_t IntPtr ของ C # จะจับคู่กับ Intptr_t ของ C / C ++ ทุกประการ และอาจมีการนำมาใช้เป็น intptr_t ใน C / C ++ ประเภท intptr_t รับประกันว่าเป็นขนาดเดียวกับประเภทโมฆะ *
ПетърПетров

2
@Trap "ประเภทเฉพาะแพลตฟอร์มที่ใช้แทนตัวชี้หรือจุดจับ" ไม่ใช่ "จำนวนเต็มเฉพาะแพลตฟอร์ม" เป็น "วิธีเฉพาะแพลตฟอร์มเพื่อแสดงตัวชี้หรือหมายเลขอ้างอิง" IntPtrทำให้รู้สึกที่สมบูรณ์แบบเพราะมันเป็นจำนวนเต็ม (เช่นเดียวกับในจำนวนเต็มทางคณิตศาสตร์) และตัวชี้ (เช่นเดียวกับในตัวชี้) Intส่วนหนึ่งมีน้อยจะทำอย่างไรกับชนิด int - มันเป็นแนวคิดที่ไม่ได้ชนิดที่เฉพาะเจาะจง แม้ว่าจะมีความเป็นธรรมสิ่งเดียวที่สเป็ค CLI พูดเกี่ยวกับมันคือว่ามันเป็น "จำนวนเต็มขนาดดั้งเดิม" โอ้ดี :)
Luaan

69

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

คุณสามารถใช้IntPtr.Sizeเพื่อตรวจสอบว่าคุณกำลังทำงานอยู่ในกระบวนการ 32- บิตหรือ 64- บิตเพราะมันจะเป็น 4 หรือ 8 ไบต์ตามลำดับ


16
จุดที่ดีเกี่ยวกับการตรวจสอบขนาดของพื้นที่ที่อยู่สำหรับกระบวนการ
Noldorin

@Noldorin จริง แต่ไม่น่าเชื่อถือเสมอไป ในอดีตที่ผ่านมามีความอุดมสมบูรณ์ของสถาปัตยกรรมที่มีประเภทตัวชี้หลายและบน Windows, IntPtrนอกจากนี้ยังใช้เพื่อเป็นตัวแทนจับซึ่งเป็น 32 บิตโดยไม่คำนึงถึงสถาปัตยกรรม ( แต่Sizeยังคงพูดว่า 8 ในกรณีที่) ข้อมูลจำเพาะของ CLI นั้นให้ข้อสังเกตว่ามันเป็นจำนวนเต็มที่มี "ขนาดดั้งเดิม" แต่นั่นไม่ได้พูดอะไรมาก
Luaan

@ luaan ที่ไม่ได้เปลี่ยนแปลงอะไรจริงๆในคำตอบของฉันมันได้หรือไม่ IntPtr เรียกว่า (และใช้ทั่วทั้งแหล่ง CLR) เป็นค่าขนาดใหญ่พอที่จะเก็บที่อยู่หน่วยความจำ มันสามารถเก็บค่าที่น้อยกว่าแน่นอน สถาปัตยกรรมบางอย่างมีตัวชี้หลายประเภท แต่ต้องมีประเภทที่ใหญ่ที่สุดของชุด
Daniel Earwicker

@DanielEarwicker มันไม่ได้มีปัญหากับการใช้งาน. NET ใด ๆ ในปัจจุบันเท่าที่ฉันรู้ อย่างไรก็ตามปัญหา (ประวัติ) ไม่ได้เกี่ยวกับขนาดเท่านั้นตัวชี้ต่าง ๆ อาจไม่เข้ากันโดยสิ้นเชิง ในตัวอย่างที่ใกล้เคียงกับวันนี้ PAE จะใช้ที่อยู่ 64- บิตแม้ว่า "ขนาดตัวชี้ดั้งเดิม" ยังคงเป็น 32- บิต มันไปตลอดทางกลับไปที่ข้อโต้แย้ง "ระบบ 32 บิต 'หมายถึงอะไรจริง ๆ ? ขณะนี้เรามีการลงทะเบียนตัวเลข 256 บิตและ 512 บิต แต่ยังคงเรียกมันว่า 64- บิต แม้ว่าคุณจะไม่สามารถระบุหน่วยความจำกายภาพแบบ 64- บิตได้ด้วย "พอยน์เตอร์" แบบ 64 บิตของคุณ มันเป็นระเบียบ
Luaan

1
บางส่วนมีความซับซ้อน แต่ IntPtr ยังคงเป็น "ประเภทค่ามีขนาดใหญ่พอที่จะเก็บที่อยู่หน่วยความจำ" การลงทะเบียนฮาร์ดแวร์ที่ใหญ่ที่สุดนั้นไม่ใหญ่พอที่จะเป็นตัวอย่างหนึ่งของคุณ นั่นไม่ใช่สิ่งที่มันมีไว้สำหรับ มันใหญ่พอที่จะแสดงที่อยู่หน่วยความจำ จากนั้นมีบางสิ่งเช่นนี้: stackoverflow.com/questions/12006854/ …ที่เรามีคำว่า "ตัวชี้" ถูกใช้สำหรับสิ่งอื่นนอกเหนือจากที่อยู่หน่วยความจำ - ซึ่งเป็นเหตุผลที่ฉันกล่าวว่า "ที่อยู่หน่วยความจำ" โดยเฉพาะ
Daniel Earwicker

48

นี่คือตัวอย่าง:

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

ดังนั้นเมื่อฉันพร้อมที่จะนำภาพล่าสุดมาไว้ในโปรแกรมเพื่อทำงานร่วมกับไดรเวอร์กล้องให้ฉันด้วย IntPtr ไปยังตำแหน่งที่เก็บภาพไว้ในหน่วยความจำกายภาพดังนั้นฉันจึงไม่ต้องเสียเวลา / ทรัพยากรในการสร้างอีก บล็อกหน่วยความจำเพื่อเก็บภาพที่อยู่ในหน่วยความจำแล้ว IntPtr แสดงให้ฉันเห็นว่าภาพนั้นอยู่ที่ไหน


7
ง่าย IntPtr ช่วยให้คุณใช้ตัวชี้ที่ไม่มีการจัดการ (เช่นที่ใช้ในไดรเวอร์กล้องของคุณ) ในรหัสที่ได้รับการจัดการ?
Callum Rogers

5
ใช่. ในกรณีนี้โปรแกรมควบคุมกล้องส่วนใหญ่ใช้ไดรเวอร์ที่ไม่มีการจัดการภายใต้ประทุน แต่เพื่อให้ทำงานได้อย่างถูกต้องในโลกที่มีการจัดการเท่านั้นมันจึงมี IntPtr ที่ช่วยให้ฉันทำงานกับข้อมูลได้อย่างปลอดภัย
bufferz

3
เหตุใดจึงไม่ให้กระแสข้อมูลกลับมา (ไบต์อาร์เรย์) เหตุใดจึงไม่ปลอดภัยหรือไม่สามารถคืนค่าได้
มัฟฟินชาย

35

การตีความโดยตรง

IntPtrเป็นจำนวนเต็มซึ่งเป็นขนาดเดียวกับตัวชี้

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

ขนาดของ IntPtr เป็นเฉพาะแพลตฟอร์ม แต่รายละเอียดนี้ไม่ค่อยได้รับการพิจารณาเนื่องจากระบบจะใช้ขนาดที่ถูกต้องโดยอัตโนมัติ

ชื่อ "IntPtr" ทำให้เกิดความสับสน - สิ่งที่คล้ายกันHandleอาจเหมาะสมกว่า การเดาเริ่มต้นของฉันคือ "IntPtr" เป็นตัวชี้ไปยังจำนวนเต็ม MSDN เอกสารของ IntPtrไปในรายละเอียดค่อนข้างคลุมเครือโดยไม่เคยให้ความเข้าใจมากเกี่ยวกับความหมายของชื่อ

มุมมองทางเลือก

An IntPtrคือตัวชี้ที่มีข้อ จำกัด สองประการ:

  1. ไม่สามารถยกเลิกการลงทะเบียนโดยตรงได้
  2. ไม่ทราบประเภทของข้อมูลที่ชี้ไป

กล่าวอีกนัยหนึ่งIntPtrคือเหมือนvoid*- แต่ด้วยคุณสมบัติพิเศษที่สามารถใช้ (แต่ไม่ควร) สำหรับการคำนวณทางคณิตศาสตร์ขั้นพื้นฐาน

เพื่อที่จะ dereference IntPtrคุณสามารถทิ้งให้เป็นตัวชี้ความจริง (การทำงานที่สามารถดำเนินการเฉพาะในบริบท "ไม่ปลอดภัย" เป็นพิเศษ) หรือคุณสามารถส่งผ่านไปยังผู้ช่วยประจำเช่นผู้ให้บริการโดยInteropServices.Marshalระดับ การใช้Marshalคลาสทำให้เห็นภาพลวงตาของความปลอดภัยเนื่องจากคุณไม่จำเป็นต้องอยู่ในบริบท "ไม่ปลอดภัย" อย่างชัดเจน อย่างไรก็ตามจะไม่ลบความเสี่ยงของการหยุดทำงานซึ่งมีอยู่ในการใช้ตัวชี้


2
มีแบบอย่างสำหรับชื่อ "intptr" จากมาตรฐาน C99 ของภาษาการเขียนโปรแกรม "C" linux.die.net/man/3/intptr_t
Brent Bradburn

17

ตัวชี้คืออะไร

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

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

ลองนึกภาพว่าหน่วยความจำของโปรแกรมของคุณเหมือนกับอาร์เรย์ขนาดใหญ่ 65535 ไบต์

พอยน์เตอร์ชี้อย่างเชื่อฟัง

พอยน์เตอร์จำที่อยู่หน่วยความจำหนึ่งอันแต่ละอันดังนั้นพวกมันจึงชี้ไปยังที่อยู่เดียวในหน่วยความจำ

ในฐานะที่เป็นกลุ่มพอยน์เตอร์จะจดจำและเรียกคืนที่อยู่หน่วยความจำเชื่อฟังคำสั่งโฆษณาทุกคำสั่งของคุณ

คุณคือราชาของพวกเขา

พอยน์เตอร์ใน C #

โดยเฉพาะใน C # ตัวชี้เป็นตัวแปรจำนวนเต็มที่เก็บที่อยู่หน่วยความจำระหว่าง 0 ถึง 65534

เฉพาะกับ C # พอยน์เตอร์เป็นประเภท int และดังนั้นจึงได้ลงนาม

คุณไม่สามารถใช้ที่อยู่ที่มีหมายเลขเป็นลบได้และคุณไม่สามารถเข้าถึงที่อยู่ที่อยู่เหนือ 65534 ความพยายามใด ๆ ที่ทำเช่นนั้นจะเป็นการโยน System.AccessViolationException

ตัวชี้ชื่อMyPointerถูกประกาศอย่างเช่น:

int * MyPointer;

ตัวชี้ใน C # เป็น int แต่ที่อยู่หน่วยความจำใน C # เริ่มต้นที่ 0 และขยายได้ถึง 65534

สิ่งที่ควรได้รับการจัดการด้วยความระมัดระวังเป็นพิเศษ

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

พอยน์เตอร์ให้โปรแกรมเมอร์ควบคุมระบบอย่างเข้มงวด ดังนั้นความผิดพลาดที่เกิดขึ้นมีแนวโน้มที่จะเกิดผลกระทบที่ร้ายแรงกว่า

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

ตัวอย่างของบล็อกที่ไม่ปลอดภัย

unsafe
{
    // Place code carefully and responsibly here.

}

วิธีใช้พอยน์เตอร์

เมื่อตัวแปรหรือวัตถุถูกประกาศหรือยกตัวอย่างพวกมันจะถูกเก็บไว้ในหน่วยความจำ

  • ประกาศตัวชี้โดยใช้คำนำหน้าสัญลักษณ์ *

int *MyPointer;

  • ในการรับที่อยู่ของตัวแปรคุณใช้คำนำหน้า & สัญลักษณ์

MyPointer = &MyVariable;

เมื่อกำหนดที่อยู่ให้กับตัวชี้แล้วจะใช้ข้อมูลต่อไปนี้:

  • ไม่มีคำนำหน้า * เพื่ออ้างถึงที่อยู่หน่วยความจำที่ชี้ไปยัง int

MyPointer = &MyVariable; // Set MyPointer to point at MyVariable

  • ด้วยคำนำหน้า * เพื่อรับค่าที่เก็บไว้ในที่อยู่หน่วยความจำที่ถูกชี้ไป

"MyPointer is pointing at " + *MyPointer;

เนื่องจากตัวชี้เป็นตัวแปรที่เก็บที่อยู่หน่วยความจำที่อยู่หน่วยความจำนี้จึงสามารถเก็บไว้ในตัวแปรตัวชี้ได้

ตัวอย่างของพอยน์เตอร์ที่ใช้อย่างระมัดระวังและมีความรับผิดชอบ

    public unsafe void PointerTest()
    {
        int x = 100; // Create a variable named x

        int *MyPointer = &x; // Store the address of variable named x into the pointer named MyPointer

        textBox1.Text = ((int)MyPointer).ToString(); // Displays the memory address stored in pointer named MyPointer

        textBox2.Text = (*MyPointer).ToString(); // Displays the value of the variable named x via the pointer named MyPointer.

    }

ขอให้สังเกตว่าประเภทของตัวชี้เป็น int นี่เป็นเพราะ C # ตีความหน่วยความจำที่อยู่เป็นตัวเลขจำนวนเต็ม (int)

ทำไมถึงเป็น int แทนที่จะเป็น uint

ไม่มีเหตุผลที่ดี

ทำไมต้องใช้พอยน์เตอร์?

พอยน์เตอร์มีความสนุกสนานมากมาย ด้วยคอมพิวเตอร์ส่วนใหญ่ที่ถูกควบคุมโดยหน่วยความจำพอยน์เตอร์จะให้อำนาจโปรแกรมเมอร์ที่มีการควบคุมหน่วยความจำของโปรแกรมมากขึ้น

การตรวจสอบหน่วยความจำ

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

เปลี่ยนค่าเหล่านี้อย่างรับผิดชอบและติดตามว่าการเปลี่ยนแปลงของคุณมีผลกับคอมพิวเตอร์อย่างไร


2
65534ดูเหมือนว่าผิดมากในช่วงตัวชี้ คุณควรให้ข้อมูลอ้างอิง
Brent Bradburn

1
ฉันโหวตสิ่งนี้เพราะมันเป็นบทความที่ดีที่อธิบายตัวชี้ ฉันต้องการดูข้อมูลอ้างอิงบางส่วนเกี่ยวกับข้อมูลจำเพาะที่คุณกล่าวถึง (ตามความเห็นด้านบน) อย่างไรก็ตาม; คำตอบนี้ไม่เกี่ยวข้องกับคำถาม System.IntPtrคำถามคือเกี่ยวกับ ฉันอยากจะเห็นคำตอบที่ได้รับการปรับปรุงในตอนท้ายอธิบายว่าอะไรบ้างSystem.IntPtrและเกี่ยวข้องกับพอยน์เตอร์ที่ไม่ปลอดภัยใน C # ได้ไหม
Michael Puckett II

7

MSDN บอกเรา:

ประเภท IntPtr ได้รับการออกแบบให้เป็นจำนวนเต็มที่มีขนาดเฉพาะแพลตฟอร์ม นั่นคืออินสแตนซ์ของประเภทนี้คาดว่าจะเป็น 32- บิตบนฮาร์ดแวร์และระบบปฏิบัติการ 32- บิตและ 64- บิตบนฮาร์ดแวร์และระบบปฏิบัติการ 64- ​​บิต

ประเภท IntPtr สามารถใช้งานได้โดยภาษาที่สนับสนุนพอยน์เตอร์และเป็นวิธีการทั่วไปในการอ้างถึงข้อมูลระหว่างภาษาที่ไม่สนับสนุนพอยน์เตอร์

วัตถุ IntPtr ยังสามารถใช้เพื่อจับที่จับ ตัวอย่างเช่นอินสแตนซ์ของ IntPtr มีการใช้อย่างกว้างขวางในคลาส System.IO.FileStream เพื่อจัดการไฟล์

ประเภท IntPtr เป็นไปตาม CLS ในขณะที่ประเภท UIntPtr ไม่ได้เป็น เฉพาะชนิด IntPtr ที่ใช้ในรันไทม์ภาษาทั่วไป ประเภท UIntPtr นั้นส่วนใหญ่มีไว้เพื่อรักษาความสมมาตรทางสถาปัตยกรรมด้วยประเภท IntPtr

http://msdn.microsoft.com/en-us/library/system.intptr(VS.71).aspx


5

อย่างนี้เป็นหน้า MSDNIntPtrที่เกี่ยวข้องกับ

บรรทัดแรกอ่าน:

ชนิดเฉพาะแพลตฟอร์มที่ใช้เพื่อแทนตัวชี้หรือหมายเลขอ้างอิง

สำหรับสิ่งที่ตัวชี้หรือหมายเลขอ้างอิงเป็นหน้าไปสู่สถานะ:

ประเภท IntPtr สามารถใช้งานได้โดยภาษาที่สนับสนุนพอยน์เตอร์และเป็นวิธีการทั่วไปในการอ้างถึงข้อมูลระหว่างภาษาที่ไม่สนับสนุนพอยน์เตอร์

วัตถุ IntPtr ยังสามารถใช้เพื่อจับที่จับ ตัวอย่างเช่นอินสแตนซ์ของ IntPtr มีการใช้อย่างกว้างขวางในคลาส System.IO.FileStream เพื่อจัดการไฟล์

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

หมายเลขอ้างอิงสามารถเป็นตัวระบุสำหรับวัตถุและส่งผ่านระหว่างวิธี / คลาสเมื่อทั้งสองฝ่ายจำเป็นต้องเข้าถึงวัตถุนั้น


2

An IntPtrเป็นประเภทค่าที่ใช้เพื่อเก็บที่อยู่หน่วยความจำหรือที่จับเป็นหลัก ชี้ที่อยู่ในหน่วยความจำ ตัวชี้สามารถพิมพ์ (เช่นint*) หรือ untyped (เช่นvoid*) ของ Windows จับเป็นค่าที่มักจะมีขนาดเท่ากัน (หรือขนาดเล็กกว่า) ที่อยู่หน่วยความจำและแสดงให้เห็นถึงระบบทรัพยากร (เช่นไฟล์หรือหน้าต่าง)

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