โปรดใส่ตัวอย่างพร้อมคำอธิบาย
int *p;จะกำหนดตัวชี้ไปยังจำนวนเต็มและ*pจะอ่านค่าตัวชี้นั้นหมายความว่ามันจะดึงข้อมูลที่ p ชี้ไปจริง ๆ
                โปรดใส่ตัวอย่างพร้อมคำอธิบาย
int *p;จะกำหนดตัวชี้ไปยังจำนวนเต็มและ*pจะอ่านค่าตัวชี้นั้นหมายความว่ามันจะดึงข้อมูลที่ p ชี้ไปจริง ๆ
                คำตอบ:
มันมักจะดีพอ - ถ้าคุณกำลังเขียนโปรแกรมการชุมนุม - เพื่อจ้องชี้ที่มีอยู่หน่วยความจำตัวเลข 1 หมายถึงไบต์ที่สองในความทรงจำของกระบวนการ 2 ที่สาม 3 ที่สี่และอื่น ๆ ....
เมื่อคุณต้องการเข้าถึงข้อมูล / ค่าในหน่วยความจำที่ตัวชี้ชี้ไปที่ - เนื้อหาของที่อยู่ด้วยดัชนีตัวเลขนั้น - จากนั้นคุณยกเลิกการอ้างอิงตัวชี้
ภาษาคอมพิวเตอร์ที่แตกต่างกันมีสัญลักษณ์ที่แตกต่างกันเพื่อบอกคอมไพเลอร์หรือล่ามว่าตอนนี้คุณสนใจในค่า (ปัจจุบัน) ของวัตถุชี้ไปที่ - ฉันมุ่งเน้นด้านล่างของ C และ C ++
พิจารณาใน C ให้ตัวชี้เช่นpด้านล่าง ...
const char* p = "abc";... สี่ไบต์ด้วยค่าตัวเลขที่ใช้ในการเข้ารหัสตัวอักษร 'a', 'b', 'c' และ 0 ไบต์เพื่อแสดงถึงจุดสิ้นสุดของข้อมูลที่เป็นข้อความจะถูกเก็บไว้ในหน่วยความจำและที่อยู่ตัวเลขของสิ่งนั้น pข้อมูลจะถูกเก็บไว้ใน วิธีนี้ C ถอดรหัสข้อความในหน่วยความจำที่เป็นที่รู้จักกันASCIIZ
ตัวอย่างเช่นหากสตริงตัวอักษรเกิดขึ้นที่ที่อยู่ 0x1000 และpตัวชี้ 32 บิตที่ 0x2000 เนื้อหาหน่วยความจำจะเป็น:
Memory Address (hex)    Variable name    Contents
1000                                     'a' == 97 (ASCII)
1001                                     'b' == 98
1002                                     'c' == 99
1003                                     0
...
2000-2003               p                1000 hexหมายเหตุว่าไม่มีตัวแปรชื่อ / ระบุสำหรับที่อยู่ 0x1000 pแต่เราทางอ้อมสามารถอ้างถึงสตริงตัวอักษรโดยใช้ตัวชี้การจัดเก็บที่อยู่:
ในการอ้างถึงตัวละครpที่เราอ้างอิงถึงเราpใช้เครื่องหมายเหล่านี้ (อีกครั้งสำหรับ C):
assert(*p == 'a');  // The first character at address p will be 'a'
assert(p[1] == 'b'); // p[1] actually dereferences a pointer created by adding
                     // p and 1 times the size of the things to which p points:
                     // In this case they're char which are 1 byte in C...
assert(*(p + 1) == 'b');  // Another notation for p[1]คุณยังสามารถย้ายพอยน์เตอร์ไปยังข้อมูลที่ชี้ไปที่, ทำการอ้างอิงตามที่คุณไป:
++p;  // Increment p so it's now 0x1001
assert(*p == 'b');  // p == 0x1001 which is where the 'b' is...หากคุณมีข้อมูลที่สามารถเขียนได้คุณสามารถทำสิ่งนี้ได้:
int x = 2;
int* p_x = &x;  // Put the address of the x variable into the pointer p_x
*p_x = 4;       // Change the memory at the address in p_x to be 4
assert(x == 4); // Check x is now 4ข้างต้นคุณต้องรู้ที่รวบรวมเวลาที่คุณจะต้องเรียกว่าตัวแปรและรหัสถามคอมไพเลอร์ที่จะจัดที่มันควรจะเก็บไว้เพื่อให้มั่นใจอยู่จะสามารถใช้ได้ผ่านx&x
ใน C หากคุณมีตัวแปรที่เป็นตัวชี้ไปยังโครงสร้างที่มีสมาชิกข้อมูลคุณสามารถเข้าถึงสมาชิกเหล่านั้นได้โดยใช้->โอเปอเรเตอร์ dereferencing:
typedef struct X { int i_; double d_; } X;
X x;
X* p = &x;
p->d_ = 3.14159;  // Dereference and access data member x.d_
(*p).d_ *= -1;    // Another equivalent notation for accessing x.d_ในการใช้ตัวชี้โปรแกรมคอมพิวเตอร์ยังต้องการข้อมูลเชิงลึกเกี่ยวกับประเภทของข้อมูลที่ชี้ไปที่ - ถ้าชนิดข้อมูลนั้นต้องการมากกว่าหนึ่งไบต์เพื่อเป็นตัวแทนจากนั้นตัวชี้จะชี้ไปยังไบต์ที่มีตัวเลขต่ำสุดในข้อมูล
ดังนั้นดูตัวอย่างที่ซับซ้อนกว่านี้เล็กน้อย:
double sizes[] = { 10.3, 13.4, 11.2, 19.4 };
double* p = sizes;
assert(p[0] == 10.3);  // Knows to look at all the bytes in the first double value
assert(p[1] == 13.4);  // Actually looks at bytes from address p + 1 * sizeof(double)
                       // (sizeof(double) is almost always eight bytes)
++p;                   // Advance p by sizeof(double)
assert(*p == 13.4);    // The double at memory beginning at address p has value 13.4
*(p + 2) = 29.8;       // Change sizes[3] from 19.4 to 29.8
                       // Note earlier ++p and + 2 here => sizes[3]บางครั้งคุณไม่ทราบว่าหน่วยความจำมากคุณจะต้องจนกว่าโปรแกรมของคุณกำลังทำงานและเห็นสิ่งที่ข้อมูลจะถูกโยนที่มัน ... mallocแล้วคุณแบบไดนามิกสามารถจัดสรรหน่วยความจำที่ใช้ มันเป็นเรื่องธรรมดาที่จะเก็บที่อยู่ในตัวชี้ ...
int* p = (int*)malloc(sizeof(int)); // Get some memory somewhere...
*p = 10;            // Dereference the pointer to the memory, then write a value in
fn(*p);             // Call a function, passing it the value at address p
(*p) += 3;          // Change the value, adding 3 to it
free(p);            // Release the memory back to the heap allocation libraryใน C ++ การจัดสรรหน่วยความจำโดยปกติจะทำกับnewโอเปอเรเตอร์และการจัดสรรคืนด้วยdelete:
int* p = new int(10); // Memory for one int with initial value 10
delete p;
p = new int[10];      // Memory for ten ints with unspecified initial value
delete[] p;
p = new int[10]();    // Memory for ten ints that are value initialised (to 0)
delete[] p;ดูตัวชี้สมาร์ท C ++ด้านล่าง
บ่อยครั้งที่ตัวชี้อาจเป็นเพียงตัวบ่งชี้ว่ามีข้อมูลหรือบัฟเฟอร์บางอย่างอยู่ในหน่วยความจำ หากจำเป็นต้องใช้ข้อมูล / บัฟเฟอร์อย่างต่อเนื่องหรือความสามารถในการโทรfree()หรือdeleteเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำโปรแกรมเมอร์จะต้องทำงานบนสำเนาของตัวชี้ ...
const char* p = asprintf("name: %s", name);  // Common but non-Standard printf-on-heap
// Replace non-printable characters with underscores....
for (const char* q = p; *q; ++q)
    if (!isprint(*q))
        *q = '_';
printf("%s\n", p); // Only q was modified
free(p);... หรือประสานการกลับรายการการเปลี่ยนแปลงอย่างระมัดระวัง ...
const size_t n = ...;
p += n;
...
p -= n;  // Restore earlier value...
free(p);ใน C ++ เป็นวิธีปฏิบัติที่ดีที่สุดในการใช้วัตถุตัวชี้สมาร์ทในการจัดเก็บและจัดการพอยน์เตอร์โดยยกเลิกการจัดสรรโดยอัตโนมัติเมื่อตัวทำลายสมาร์ทพอยน์เตอร์ทำงาน ตั้งแต่ C ++ 11 ไลบรารีมาตรฐานจะให้สองunique_ptrสำหรับเมื่อมีเจ้าของเดียวสำหรับวัตถุที่จัดสรร ...
{
    std::unique_ptr<T> p{new T(42, "meaning")};
    call_a_function(p);
    // The function above might throw, so delete here is unreliable, but...
} // p's destructor's guaranteed to run "here", calling delete... และshared_ptrสำหรับการเป็นเจ้าของหุ้น (โดยใช้การนับการอ้างอิง ) ...
{
    auto p = std::make_shared<T>(3.14, "pi");
    number_storage1.may_add(p); // Might copy p into its container
    number_storage2.may_add(p); // Might copy p into its container    } // p's destructor will only delete the T if neither may_add copied itใน C NULLและ0- และนอกจากนี้ใน C ++ nullptr- สามารถใช้เพื่อระบุว่าตัวชี้ไม่ได้เก็บที่อยู่หน่วยความจำของตัวแปรในขณะนี้และไม่ควรถูกอ้างถึงหรือใช้ในการคำนวณทางคณิตศาสตร์ ตัวอย่างเช่น:
const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
int c;
while ((c = getopt(argc, argv, "f:")) != -1)
    switch (c) {
      case f: p_filename = optarg; break;
    }
if (p_filename)  // Only NULL converts to false
    ...   // Only get here if -f flag specifiedใน C และ C ++, เช่นเดียวกับชนิดของตัวเลข inbuilt ไม่จำเป็นต้องเริ่มต้นที่จะ0ไม่boolsไปชี้ยังไม่ได้ตั้งเสมอfalse NULLทั้งหมดเหล่านี้ถูกตั้งค่าเป็น 0 / false / NULL เมื่อพวกเขาเป็นstaticตัวแปรหรือ (C ++ เท่านั้น) ตัวแปรสมาชิกโดยตรงหรือโดยอ้อมของวัตถุคงที่หรือฐานของพวกเขาหรือผ่านการเริ่มต้นเป็นศูนย์ศูนย์ (เช่นnew T();และnew T(x, y, z);ดำเนินการศูนย์ initialisation new T;ไม่).
นอกจากนี้เมื่อคุณกำหนด0, NULLและnullptrตัวชี้บิตในตัวชี้ไม่จำเป็นต้องมีการตั้งค่าทั้งหมด: ตัวชี้อาจไม่ประกอบด้วย "0" ในระดับฮาร์ดแวร์หรือดูอยู่ 0 ในพื้นที่ที่อยู่เสมือนของคุณ คอมไพเลอร์ที่ได้รับอนุญาตในการจัดเก็บอย่างอื่นมีถ้ามันมีเหตุผลที่จะ แต่สิ่งที่มันไม่ - ถ้าคุณมาพร้อมและเปรียบเทียบตัวชี้ไป0, NULL, nullptrหรือตัวชี้อื่นที่ได้รับมอบหมายใด ๆ ของผู้ที่ต้องทำงานเปรียบเทียบตามที่คาดไว้ ดังนั้นด้านล่างซอร์สโค้ดในระดับคอมไพเลอร์ "NULL" อาจเป็นบิต "เสก" ในภาษา C และ C ++ ...
พอยน์เตอร์ที่เริ่มต้นอย่างเข้มงวดยิ่งขึ้นจะเก็บรูปแบบบิตเพื่อระบุที่อยู่หน่วยความจำNULL(หรือเสมือน )
กรณีง่าย ๆ คือกรณีนี้เป็นการชดเชยเชิงตัวเลขในพื้นที่ที่อยู่เสมือนทั้งหมดของกระบวนการ ในกรณีที่ซับซ้อนมากขึ้นตัวชี้อาจสัมพันธ์กับพื้นที่หน่วยความจำเฉพาะซึ่ง CPU อาจเลือกตามการลงทะเบียนของ "ส่วน" ของ CPU หรือลักษณะของรหัสส่วนที่เข้ารหัสในรูปแบบบิตและ / หรือค้นหาในสถานที่ที่แตกต่างกันขึ้นอยู่กับ คำแนะนำเกี่ยวกับรหัสเครื่องโดยใช้ที่อยู่
ตัวอย่างเช่นการกำหนดint*ค่าเริ่มต้นอย่างถูกต้องให้ชี้ไปที่intตัวแปรอาจ - หลังจากการร่ายไปยังfloat*- การเข้าถึงหน่วยความจำในหน่วยความจำ "GPU" ค่อนข้างแตกต่างจากหน่วยความจำที่intตัวแปรอยู่นั้นจากนั้นเมื่อใช้งานแล้ว หน่วยความจำที่แตกต่างกันสำหรับการถือครองเครื่อง opcodes สำหรับโปรแกรม (ด้วยค่าตัวเลขของint*ตัวชี้แบบสุ่มและไม่ถูกต้องได้อย่างมีประสิทธิภาพภายในขอบเขตหน่วยความจำอื่น ๆ เหล่านี้)
ภาษาโปรแกรม 3GL เช่น C และ C ++ มักจะซ่อนความซับซ้อนนี้ไว้เช่น:
หากคอมไพเลอร์ให้ตัวชี้ไปยังตัวแปรหรือฟังก์ชั่นคุณสามารถยกเลิกการลงทะเบียนได้อย่างอิสระ (ตราบใดที่ตัวแปรไม่ถูกทำลาย / deallocated ในขณะเดียวกัน) และเป็นปัญหาของคอมไพเลอร์ไม่ว่าจะต้องมีการเรียกคืน คำสั่งรหัสเครื่องที่แตกต่างกันใช้
หากคุณได้รับตัวชี้ไปยังองค์ประกอบในอาร์เรย์คุณสามารถใช้เลขคณิตของตัวชี้เพื่อย้ายไปที่อื่นในอาร์เรย์หรือแม้กระทั่งสร้างที่อยู่แบบหนึ่งต่อท้ายของอาร์เรย์ที่ถูกกฎหมายเพื่อเปรียบเทียบกับตัวชี้อื่น ๆ กับองค์ประกอบ ในอาเรย์ (หรือที่ถูกย้ายโดยเลขคณิตตัวชี้ไปยังค่าเดียวกันที่ผ่านมาแบบเดียวกัน); อีกครั้งใน C และ C ++ มันขึ้นอยู่กับคอมไพเลอร์เพื่อให้แน่ใจว่า "ใช้งานได้"
ฟังก์ชั่นระบบปฏิบัติการเฉพาะเช่นการแมปหน่วยความจำที่ใช้ร่วมกันอาจให้คำแนะนำแก่คุณและพวกเขาจะ "ทำงานได้" ภายในช่วงของที่อยู่ที่เหมาะสมสำหรับพวกเขา
ความพยายามในการย้ายพอยน์เตอร์ทางกฎหมายที่เกินขอบเขตเหล่านี้หรือเพื่อส่งตัวเลขโดยพลการไปยังพอยน์เตอร์หรือใช้พอยน์เตอร์ที่พาดพิงไปยังประเภทที่ไม่เกี่ยวข้องโดยทั่วไปจะมีพฤติกรรมที่ไม่กำหนดดังนั้นควรหลีกเลี่ยงไลบรารีและแอพพลิเคชั่น อาจจำเป็นต้องพึ่งพาพฤติกรรมที่ไม่ได้กำหนดไว้โดยมาตรฐาน C หรือ C ++ ซึ่งได้กำหนดไว้อย่างดีจากการใช้งานหรือฮาร์ดแวร์เฉพาะของพวกเขา
p[1] และ*(p + 1) เหมือนกันหรือไม่ นั่นคือทำp[1] และ*(p + 1)สร้างคำสั่งเดียวกันหรือไม่
                    pเป็นเพียง 2000: หากคุณมีตัวชี้อื่นpจะต้องเก็บ 2000 ในสี่หรือแปดไบต์ หวังว่าจะช่วย! ไชโย
                    uมีอาร์เรย์arrทั้ง gcc และ clang จะรับรู้ว่า lvalue u.arr[i]อาจเข้าถึงที่เก็บข้อมูลเดียวกันกับสมาชิกสหภาพรายอื่น แต่จะไม่รับรู้ว่า lvalue *(u.arr+i)อาจทำเช่นนั้น ฉันไม่แน่ใจว่าผู้เขียนคอมไพเลอร์เหล่านั้นคิดว่าหลังเรียกใช้ UB หรือที่อดีตเรียกใช้ UB แต่พวกเขาควรประมวลผลอย่างเป็นประโยชน์อยู่แล้ว แต่พวกเขาเห็นว่าสองนิพจน์แตกต่างกันอย่างชัดเจน
                    การยกเลิกการลงทะเบียนตัวชี้หมายถึงการรับค่าที่เก็บไว้ในตำแหน่งหน่วยความจำที่ชี้โดยตัวชี้ ตัวดำเนินการ * ใช้เพื่อทำสิ่งนี้และเรียกว่าตัวดำเนินการการยกเลิกการประชุม
int a = 10;
int* ptr = &a;
printf("%d", *ptr); // With *ptr I'm dereferencing the pointer. 
                    // Which means, I am asking the value pointed at by the pointer.
                    // ptr is pointing to the location in memory of the variable a.
                    // In a's location, we have 10. So, dereferencing gives this value.
// Since we have indirect control over a's location, we can modify its content using the pointer. This is an indirect way to access a.
 *ptr = 20;         // Now a's content is no longer 10, and has been modified to 20.[]ยังระบุตัวชี้อีกครั้ง ( a[b]ถูกกำหนดเป็นค่าเฉลี่ย*(a + b))
                    ตัวชี้คือ "การอ้างอิง" ไปยังค่า .. เช่นเดียวกับหมายเลขการโทรห้องสมุดเป็นการอ้างอิงไปยังหนังสือ "การยกเลิกการลงทะเบียน" หมายเลขการโทรกำลังดำเนินการผ่านทางกายภาพและดึงข้อมูลหนังสือเล่มนั้น
int a=4 ;
int *pA = &a ;
printf( "The REFERENCE/call number for the variable `a` is %p\n", pA ) ;
// The * causes pA to DEREFERENCE...  `a` via "callnumber" `pA`.
printf( "%d\n", *pA ) ; // prints 4.. หากหนังสือไม่อยู่ที่นั่นบรรณารักษ์จะเริ่มตะโกนปิดห้องสมุดและมีคนสองคนตั้งค่าให้ตรวจสอบสาเหตุของคนที่จะหาหนังสือที่ไม่ได้อยู่ที่นั่น
ในคำง่าย ๆ dereferencing หมายถึงการเข้าถึงค่าจากตำแหน่งหน่วยความจำบางอย่างที่ตัวชี้นั้นชี้
รหัสและคำอธิบายจากตัวชี้พื้นฐาน :
การดำเนินการ dereference เริ่มต้นที่ตัวชี้และตามลูกศรไปเพื่อเข้าถึง pointee ของมัน เป้าหมายอาจจะดูที่สถานะปวงหรือเปลี่ยนสถานะปวง การดำเนินการยกเลิกการลงรายการบัญชีบนตัวชี้ใช้งานได้เฉพาะเมื่อตัวชี้มีจุด - จุดที่ต้องถูกจัดสรรและตัวชี้จะต้องตั้งค่าให้ชี้ไปที่มัน ข้อผิดพลาดที่พบบ่อยที่สุดในรหัสตัวชี้กำลังลืมที่จะตั้งค่า Pointee ข้อผิดพลาดรันไทม์ที่พบบ่อยที่สุดเนื่องจากข้อผิดพลาดนั้นในรหัสคือการดำเนินการไม่ทำงาน ใน Java การตรวจสอบที่ไม่ถูกต้องจะถูกแฟล็กอย่างสุภาพโดยระบบรันไทม์ ในภาษาที่คอมไพล์เช่น C, C ++ และ Pascal ความไม่ถูกต้องที่ผิดพลาดบางครั้งจะผิดพลาดและบางครั้งหน่วยความจำเสียหายในบางวิธีการสุ่ม
void main() {   
    int*    x;  // Allocate the pointer x
    x = malloc(sizeof(int));    // Allocate an int pointee,
                            // and set x to point to it
    *x = 42;    // Dereference x to store 42 in its pointee   
}ฉันคิดว่าคำตอบก่อนหน้านี้ทั้งหมดผิดเพราะพวกเขาระบุว่าการลงทะเบียนหมายถึงการเข้าถึงคุณค่าที่แท้จริง Wikipedia ให้คำจำกัดความที่ถูกต้องแทน: https://en.wikipedia.org/wiki/Dereference_operator
มันทำงานกับตัวแปรตัวชี้และส่งกลับค่า l- เท่ากับค่าที่อยู่ตัวชี้ สิ่งนี้เรียกว่า "การลงทะเบียน" ตัวชี้
ที่กล่าวว่าเราสามารถอ่านค่าตัวชี้ได้โดยไม่ต้องเข้าถึงค่าที่ชี้ไป ตัวอย่างเช่น:
char *p = NULL;
*p;เรายกเลิกการอ้างอิงตัวชี้ NULL โดยไม่ต้องเข้าถึงค่าของมัน หรือเราสามารถทำ:
p1 = &(*p);
sz = sizeof(*p);อีกครั้งพิจารณา แต่ไม่เคยเข้าถึงค่า รหัสดังกล่าวจะไม่ผิดพลาด: เกิดข้อผิดพลาดเมื่อคุณเข้าถึงข้อมูลโดยตัวชี้ที่ไม่ถูกต้อง อย่างไรก็ตามโชคไม่ดีที่ตามมาตรฐานการยกเลิกการอ้างอิงตัวชี้ที่ไม่ถูกต้องนั้นเป็นพฤติกรรมที่ไม่ได้กำหนด (มีข้อยกเว้นเล็กน้อย) แม้ว่าคุณจะไม่ได้พยายามสัมผัสข้อมูลจริงก็ตาม
ดังนั้นในระยะสั้น: การยกเลิกการลงทะเบียนตัวชี้หมายถึงการใช้ตัวดำเนินการการยกเลิกการลงทะเบียนกับมัน ตัวดำเนินการนั้นเพียงส่งคืนค่า l สำหรับการใช้ในอนาคตของคุณ
*p;ทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด แม้ว่าคุณจะถูกต้องที่ dereferencing ไม่สามารถเข้าถึงค่าต่อ seรหัส*p; จะเข้าถึงค่า