“ การอ้างถึง” ตัวชี้หมายถึงอะไร


540

โปรดใส่ตัวอย่างพร้อมคำอธิบาย


สิ่งนี้สามารถช่วยคุณได้: stackoverflow.com/questions/2795575/…
Harry Joy


24
int *p;จะกำหนดตัวชี้ไปยังจำนวนเต็มและ*pจะอ่านค่าตัวชี้นั้นหมายความว่ามันจะดึงข้อมูลที่ p ชี้ไปจริง ๆ
Peyman

4
Fun's Pointer Fun ( cslibrary.stanford.edu/104 ) เป็นวิดีโอที่ยอดเยี่ยมเกี่ยวกับพอยน์เตอร์ที่อาจอธิบายสิ่งต่าง ๆ @ Erik- คุณพร้อมที่จะวางลิงก์ Stanford CS Library มีสินค้ามากมายอยู่ที่นั่น ...
templatetypedef

6
คำตอบของแฮร์รี่ตรงข้ามกับความช่วยเหลือที่นี่
Jim Balter

คำตอบ:


731

ทบทวนคำศัพท์พื้นฐาน

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

  • เกิดอะไรขึ้นกับ 0 และไบต์แรก? ทีนี้เราจะมาดูทีหลัง - ดูพอยน์เตอร์พอยน์เตอร์ด้านล่าง
  • สำหรับคำจำกัดความที่แม่นยำยิ่งขึ้นของพอยน์เตอร์ที่จัดเก็บและความเกี่ยวข้องของหน่วยความจำและที่อยู่ดู"เพิ่มเติมเกี่ยวกับที่อยู่หน่วยความจำและสาเหตุที่คุณอาจไม่จำเป็นต้องรู้"ในตอนท้ายของคำตอบนี้

เมื่อคุณต้องการเข้าถึงข้อมูล / ค่าในหน่วยความจำที่ตัวชี้ชี้ไปที่ - เนื้อหาของที่อยู่ด้วยดัชนีตัวเลขนั้น - จากนั้นคุณยกเลิกการอ้างอิงตัวชี้

ภาษาคอมพิวเตอร์ที่แตกต่างกันมีสัญลักษณ์ที่แตกต่างกันเพื่อบอกคอมไพเลอร์หรือล่ามว่าตอนนี้คุณสนใจในค่า (ปัจจุบัน) ของวัตถุชี้ไปที่ - ฉันมุ่งเน้นด้านล่างของ 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 ++ เป็นวิธีปฏิบัติที่ดีที่สุดในการใช้วัตถุตัวชี้สมาร์ทในการจัดเก็บและจัดการพอยน์เตอร์โดยยกเลิกการจัดสรรโดยอัตโนมัติเมื่อตัวทำลายสมาร์ทพอยน์เตอร์ทำงาน ตั้งแต่ 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

ตัวชี้ Null

ใน 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)สร้างคำสั่งเดียวกันหรือไม่
Pacerier

2
@Pacerier: จาก 6.5.2.1/2 ในร่างมาตรฐาน N1570 C (ตอนแรกที่ฉันพบออนไลน์) "คำจำกัดความของโอเปอเรเตอร์ห้อย [] คือ E1 [E2] เหมือนกับ (* ((E1) + E2)) )." - ฉันนึกภาพไม่ออกเลยว่าทำไมคอมไพเลอร์จะไม่แปลงพวกมันให้เป็นตัวแทนเหมือนกันในช่วงแรก ๆ ของการคอมไพล์โดยใช้ออพติไมซ์เดียวกันหลังจากนั้น แต่ฉันไม่เห็นว่าใครจะพิสูจน์ได้ว่า โดยไม่ต้องสำรวจคอมไพเลอร์ทุกคนที่เคยเขียน
Tony Delroy

3
@Honey: ค่า 1000 hex มีขนาดใหญ่เกินกว่าที่จะเข้ารหัสในหน่วยความจำไบต์เดียว (8 บิต): คุณสามารถเก็บหมายเลขที่ไม่ได้ลงนามตั้งแต่ 0 ถึง 255 ในหนึ่งไบต์ ดังนั้นคุณไม่สามารถเก็บเลขฐานสิบหก 1000 ที่ "เพียง" ที่อยู่ 2000 แทนระบบ 32 บิตจะใช้ 32 บิต - ซึ่งเป็นสี่ไบต์ - ด้วยที่อยู่จาก 2000 ถึง 2003 ระบบ 64 บิตจะใช้ 64 บิต - 8 ไบต์ - จาก 2000 ถึง 2007 ทั้งสองวิธีที่อยู่พื้นฐานของpเป็นเพียง 2000: หากคุณมีตัวชี้อื่นpจะต้องเก็บ 2000 ในสี่หรือแปดไบต์ หวังว่าจะช่วย! ไชโย
Tony Delroy

1
@TonyDelroy: หากสหภาพuมีอาร์เรย์arrทั้ง gcc และ clang จะรับรู้ว่า lvalue u.arr[i]อาจเข้าถึงที่เก็บข้อมูลเดียวกันกับสมาชิกสหภาพรายอื่น แต่จะไม่รับรู้ว่า lvalue *(u.arr+i)อาจทำเช่นนั้น ฉันไม่แน่ใจว่าผู้เขียนคอมไพเลอร์เหล่านั้นคิดว่าหลังเรียกใช้ UB หรือที่อดีตเรียกใช้ UB แต่พวกเขาควรประมวลผลอย่างเป็นประโยชน์อยู่แล้ว แต่พวกเขาเห็นว่าสองนิพจน์แตกต่างกันอย่างชัดเจน
supercat

3
ฉันไม่ค่อยเห็นพอยน์เตอร์และการใช้งานภายใน C / C ++ ดังนั้นจึงสรุปและอธิบายได้ง่าย
kayleeFrye_onDeck

102

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

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.

15
ตัวชี้ไม่ได้ชี้ไปที่คุ้มค่าก็ชี้ไปยังวัตถุ
Keith Thompson

51
@ KeithThompson ตัวชี้ไม่ได้ชี้ไปที่วัตถุมันชี้ไปยังที่อยู่หน่วยความจำที่วัตถุ (อาจเป็นแบบดั้งเดิม) ตั้งอยู่
mg30rg

4
@ mg30rg: ฉันไม่แน่ใจว่าคุณสร้างความแตกต่างอะไร ค่าตัวชี้คือที่อยู่ วัตถุตามคำนิยามคือ "ภูมิภาคของการจัดเก็บข้อมูลในสภาพแวดล้อมการดำเนินการเนื้อหาที่สามารถเป็นตัวแทนของค่า" และคุณหมายถึงอะไร "ดั้งเดิม"? มาตรฐาน C ไม่ได้ใช้คำนั้น
Keith Thompson

6
@ KeithThompson ฉันแทบจะไม่ได้ชี้ให้เห็นว่าคุณไม่ได้เพิ่มคุณค่าให้กับคำตอบคุณเป็นเพียง nitpicking เกี่ยวกับคำศัพท์ (และก็ทำเช่นนั้นด้วย) ค่าตัวชี้เป็นที่อยู่แน่นอนว่าเป็น "ชี้" ไปยังที่อยู่หน่วยความจำ คำว่า "วัตถุ" ในโลก OOPdriven ของเราอาจทำให้เข้าใจผิดเพราะสามารถตีความได้ว่า "คลาสอินสแตนซ์" (ใช่ฉันไม่ทราบว่าคำถามนี้มีป้ายกำกับ [C] ไม่ใช่ [C ++]) และฉันใช้คำนั้น "primitive" ตรงข้ามกับ "copmlex" (โครงสร้างข้อมูลเช่น struct หรือคลาส)
mg30rg

3
ให้ฉันเพิ่มคำตอบนี้ว่าตัวดำเนินการตัวห้อยของอาร์เรย์[]ยังระบุตัวชี้อีกครั้ง ( a[b]ถูกกำหนดเป็นค่าเฉลี่ย*(a + b))
cmaster - คืนสถานะโมนิกา

20

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

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.. 

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


18

ในคำง่าย ๆ dereferencing หมายถึงการเข้าถึงค่าจากตำแหน่งหน่วยความจำบางอย่างที่ตัวชี้นั้นชี้


7

รหัสและคำอธิบายจากตัวชี้พื้นฐาน :

การดำเนินการ 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   
}

คุณต้องจัดสรรหน่วยความจำสำหรับที่ x ควรชี้ไปที่ ตัวอย่างของคุณมีพฤติกรรมที่ไม่ได้กำหนด
Peyman

3

ฉันคิดว่าคำตอบก่อนหน้านี้ทั้งหมดผิดเพราะพวกเขาระบุว่าการลงทะเบียนหมายถึงการเข้าถึงคุณค่าที่แท้จริง Wikipedia ให้คำจำกัดความที่ถูกต้องแทน: https://en.wikipedia.org/wiki/Dereference_operator

มันทำงานกับตัวแปรตัวชี้และส่งกลับค่า l- เท่ากับค่าที่อยู่ตัวชี้ สิ่งนี้เรียกว่า "การลงทะเบียน" ตัวชี้

ที่กล่าวว่าเราสามารถอ่านค่าตัวชี้ได้โดยไม่ต้องเข้าถึงค่าที่ชี้ไป ตัวอย่างเช่น:

char *p = NULL;
*p;

เรายกเลิกการอ้างอิงตัวชี้ NULL โดยไม่ต้องเข้าถึงค่าของมัน หรือเราสามารถทำ:

p1 = &(*p);
sz = sizeof(*p);

อีกครั้งพิจารณา แต่ไม่เคยเข้าถึงค่า รหัสดังกล่าวจะไม่ผิดพลาด: เกิดข้อผิดพลาดเมื่อคุณเข้าถึงข้อมูลโดยตัวชี้ที่ไม่ถูกต้อง อย่างไรก็ตามโชคไม่ดีที่ตามมาตรฐานการยกเลิกการอ้างอิงตัวชี้ที่ไม่ถูกต้องนั้นเป็นพฤติกรรมที่ไม่ได้กำหนด (มีข้อยกเว้นเล็กน้อย) แม้ว่าคุณจะไม่ได้พยายามสัมผัสข้อมูลจริงก็ตาม

ดังนั้นในระยะสั้น: การยกเลิกการลงทะเบียนตัวชี้หมายถึงการใช้ตัวดำเนินการการยกเลิกการลงทะเบียนกับมัน ตัวดำเนินการนั้นเพียงส่งคืนค่า l สำหรับการใช้ในอนาคตของคุณ


ดีคุณตรวจสอบตัวชี้ NULL ที่จะนำไปสู่การแบ่งส่วนความผิด
arjun gaur

ด้านบนของที่คุณค้นหา 'ผู้ประกอบการ dereferencing' และไม่ 'dereferencing ตัวชี้' ซึ่งจริง ๆ แล้วหมายถึงการรับค่า / การเข้าถึงค่าที่ตำแหน่งหน่วยความจำที่ชี้โดยตัวชี้
arjun gaur

คุณเคยลองไหม ฉันทำ. ต่อไปนี้ไม่ผิดพลาด: `#include <stdlib.h> int main () {char * p = NULL; * p; กลับ 0 } `
stsp

1
@stsp ทำเพราะรหัสไม่ผิดพลาดในขณะนี้ไม่ได้หมายความว่าจะไม่เกิดขึ้นในอนาคตหรือในระบบอื่น ๆ

1
*p;ทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด แม้ว่าคุณจะถูกต้องที่ dereferencing ไม่สามารถเข้าถึงค่าต่อ seรหัส*p; จะเข้าถึงค่า
MM
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.