ผู้คนพบอะไรยากเกี่ยวกับพอยน์เตอร์ C? [ปิด]


173

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

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

ดังนั้นหากคุณกำลังดิ้นรนกับพอยน์เตอร์หรือคุณเพิ่งจะทันใดนั้น "เข้าใจ" พอยน์เตอร์ที่ทำให้คุณมีปัญหาคืออะไร


54
Snarky, คุยโว, โต้เถียงอย่างไม่มีความจำเป็น, ตอบด้วยความจริง:พวกเขาพิการ - ใจพิการ, ฉันบอกว่า 'ยา ---' โดยการรับหนึ่งในภาษาระดับสูง "แสดงออก" ก่อน พวกเขาควรเริ่มต้นด้วยการเขียนโปรแกรมบนโลหะเปลือยเช่นเดียวกับพระเจ้าที่ Daniel Boone ตั้งใจ!
dmckee --- ผู้ดูแลอดีตลูกแมว

3
... และโปรแกรมเมอร์คงจะดีกว่าเพราะมันจะพัฒนาเป็นการสนทนาแม้ว่าคุณจะพยายามอย่างเต็มที่ก็ตาม
dmckee --- ผู้ดูแลอดีตลูกแมว

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

2
@ Sam Saffron: ในขณะที่ฉันมักจะยอมรับว่านี่เป็นคำถามของโปรแกรมเมอร์มากกว่า SE แต่จริงๆแล้วมันจะไม่เลวเลยถ้าผู้คนยินดีที่จะทำเครื่องหมายว่า "ฉันคิดว่ามันง่าย" และ "ฉันเกลียดการเห็นพอยน์เตอร์" เป็นสแปม พวกเขาเป็น.
jkerian

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

คำตอบ:


86

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

เมื่อฉันสอนฉันพบช่องโหว่ต่อไปนี้ในความเข้าใจของนักเรียนว่าเป็นสาเหตุของปัญหาที่พบบ่อยที่สุด:

  1. ฮีป vs พื้นที่จัดเก็บสแต็ค มันน่าอัศจรรย์เพียงไม่กี่คนที่ไม่เข้าใจสิ่งนี้แม้ในความหมายทั่วไป
  2. เฟรมสแต็ก เพียงแค่แนวคิดทั่วไปของส่วนเฉพาะของสแต็กสำหรับตัวแปรท้องถิ่นพร้อมกับเหตุผลที่มันเป็น 'สแต็ค' ... รายละเอียดเช่นการเก็บตำแหน่งส่งคืนรายละเอียดตัวจัดการข้อยกเว้นและการลงทะเบียนก่อนหน้านี้สามารถถูกทิ้งไว้อย่างปลอดภัย สร้างคอมไพเลอร์
  3. "หน่วยความจำคือหน่วยความจำคือหน่วยความจำ" แคสต์เพียงแค่เปลี่ยนเวอร์ชั่นของผู้ปฏิบัติงานหรือจำนวนห้องที่คอมไพเลอร์ให้หน่วยความจำก้อนหนึ่ง คุณจะรู้ว่าคุณจัดการกับปัญหานี้เมื่อมีคนพูดถึง "สิ่งที่ (ดั้งเดิม) ตัวแปร X จริงๆคือ"

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

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


13
IMHO การเข้าใจ stack และ heap นั้นไม่จำเป็นเท่ารายละเอียด CPU ระดับต่ำ สแต็คและฮีปมีรายละเอียดการใช้งาน ข้อมูลจำเพาะ ISO C ไม่ได้มีการกล่าวถึงคำว่า "stack" เพียงอย่างเดียวและ K&R ก็ไม่เหมือนกัน
sigjuice

4
@sigjuice: การคัดค้านของคุณพลาดทั้งคำถามและคำตอบ A) K&R C เป็นความผิดสมัย B) ISO C ไม่ใช่ภาษาเดียวที่มีพอยน์เตอร์คะแนนของฉัน 1 และ 2 ได้รับการพัฒนาเทียบกับภาษาที่ไม่ใช่ C-based C) 95% ของสถาปัตยกรรม (ไม่ใช่ภาษา) ออกใช้ฮีป / stack system มันเป็นเรื่องธรรมดามากพอที่จะอธิบายข้อยกเว้นที่สัมพันธ์กับมัน D) ประเด็นของคำถามคือ "ทำไมคนไม่เข้าใจตัวชี้" ไม่ใช่ "ฉันจะอธิบาย ISO C ได้อย่างไร"
jkerian

9
@John Marchetti: ยิ่งกว่านั้น ... เนื่องจากคำถามคือ "ปัญหารากเหง้าของคนที่มีปัญหาเกี่ยวกับพอยน์เตอร์" คืออะไรฉันไม่คิดว่าคนที่ถามคำถามที่เกี่ยวข้องกับตัวชี้จะประทับใจอย่างมากกับ "You don ' ไม่จำเป็นต้องรู้ "เป็นคำตอบ เห็นได้ชัดว่าพวกเขาไม่เห็นด้วย :)
jkerian

3
@ jkerian มันอาจจะล้าสมัย แต่หน้าสามหรือสี่หน้าของ K&R ที่อธิบายตัวชี้ทำได้โดยไม่จำเป็นต้องมีรายละเอียดการใช้งาน ความรู้เกี่ยวกับรายละเอียดการนำไปใช้นั้นมีประโยชน์ด้วยเหตุผลหลายประการ แต่ IMHO ไม่ควรเป็นข้อกำหนดเบื้องต้นสำหรับการทำความเข้าใจโครงสร้างหลักของภาษา
sigjuice

3
"โดยทั่วไปแล้วการให้ที่อยู่สมมติอย่างชัดเจนให้กับสถานที่ต่างๆช่วยด้วย" -> +1
fredoverflow

146

เมื่อฉันเริ่มทำงานกับพวกเขาปัญหาที่ใหญ่ที่สุดที่ฉันมีคือไวยากรณ์

int* ip;
int * ip;
int *ip;

เหมือนกันหมด

แต่:

int* ip1, ip2;  //second one isn't a pointer!
int *ip1, *ip2;

ทำไม? เนื่องจากส่วน "ตัวชี้" ของการประกาศเป็นของตัวแปรไม่ใช่ชนิด

และจากนั้นการลดทอนสิ่งที่ใช้สัญกรณ์ที่คล้ายกันมาก:

*ip = 4;  //sets the value of the thing pointed to by ip to '4'
x = ip;   //hey, that's not '4'!
x = *ip;  //ahh... there's that '4'

ยกเว้นเมื่อคุณต้องการรับพอยน์เตอร์ ... จากนั้นคุณใช้เครื่องหมายและ!

int *ip = &x;

ไชโยเพื่อความมั่นคง!

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

void foo(****ipppArr);

เพื่อเรียกสิ่งนี้ฉันต้องการที่อยู่ของอาเรย์ของพอยน์เตอร์เพื่อพอยน์เตอร์ไปยังพอยน์เตอร์ของ ints:

foo(&(***ipppArr));

ในหกเดือนเมื่อฉันต้องรักษารหัสนี้ฉันจะใช้เวลามากขึ้นในการพยายามคิดออกว่าทั้งหมดนี้หมายถึงอะไรมากกว่าการเขียนใหม่จากพื้นดินขึ้น (ใช่อาจมีความผิดพลาดทางไวยากรณ์ - มันเป็นเวลานานแล้วที่ฉันได้ทำอะไรในซีฉันก็คิดถึงมัน แต่หลังจากนั้นฉันก็เป็นนักหมอหมอ)


21
ความคิดเห็นของคุณเกี่ยวกับคนแรก >> * ip = 4; // ตั้งค่า ip เป็น '4' << ผิด >> ควรตั้งค่าของสิ่งที่ชี้ไปที่ '4'
aaaa bbbb

8
การวางซ้อนกันหลายประเภทมากเกินไปเป็นความคิดที่ไม่ดีในภาษาใด ๆ คุณอาจพบว่าการเขียน "foo (& (*** ipppArr))"; แปลกใน C แต่เขียนอะไรบางอย่างเช่น "std :: map <std :: pair <int, int>, std :: pair <std :: vector <int>, std :: tuple <int, double, std :: list <int> >>> "ใน C ++ นั้นซับซ้อนมากเช่นกัน นี่ไม่ได้หมายความว่าพอยน์เตอร์ใน C หรือ STL container ใน C ++ นั้นซับซ้อน หมายความว่าคุณต้องใช้คำจำกัดความประเภทที่ดีกว่าเพื่อให้ผู้อ่านรหัสของคุณเข้าใจได้
แพทริค

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

4
แม้แต่การอ่านคำตอบนี้ฉันก็ยังอยากได้กระดาษแผ่นหนึ่งและวาดรูป ใน C ฉันวาดรูปเสมอ
Michael Easter

19
@ Jason มีวิธีการวัดความยากลำบากอะไรนอกเหนือจากสิ่งที่คนส่วนใหญ่พบว่ายาก?
Rupert Madden-Abbott

52

ความเข้าใจที่ถูกต้องของตัวชี้ต้องใช้ความรู้เกี่ยวกับสถาปัตยกรรมของเครื่องต้นแบบ

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


18
@dmckee: ฉันผิดใช่ไหม? โปรแกรมเมอร์ Java กี่คนที่สามารถจัดการกับ segfault ได้?
Robert Harvey

5
segfaults มีบางอย่างเกี่ยวกับ stick shift หรือไม่? - โปรแกรมเมอร์ Java
Tom Anderson

6
@ Robert: มันมีความหมายว่าเป็นของแท้ เรื่องนี้เป็นเรื่องยากที่จะพูดคุยโดยไม่ทำร้ายความรู้สึกของผู้คน และฉันเกรงว่าความคิดเห็นของฉันจะทำให้เกิดความขัดแย้งมากฉันคิดว่าคุณควรหลีกเลี่ยง แม่ cupla
dmckee --- ผู้ดูแลอดีตลูกแมว

30
ฉันไม่เห็นด้วย; คุณไม่จำเป็นต้องเข้าใจสถาปัตยกรรมพื้นฐานเพื่อรับพอยน์เตอร์ (เป็นสิ่งที่เป็นนามธรรมอยู่แล้ว)
สัน

11
@ Jason: ใน C ตัวชี้จะเป็นที่อยู่หน่วยความจำ การทำงานกับพวกเขาอย่างปลอดภัยนั้นเป็นไปไม่ได้หากไม่เข้าใจโครงสร้างของเครื่อง ดูen.wikipedia.org/wiki/Pointer_(computing)และboredzo.org/pointers/#definition
Robert Harvey

42

เมื่อต้องรับมือกับพอยน์เตอร์คนที่สับสนจะอยู่ในค่ายหนึ่งในสองแห่ง ฉันเคยเป็นทั้งคู่หรือเปล่า

array[]ฝูงชน

นี่คือฝูงชนที่ตรงขึ้นไปไม่รู้ว่าจะแปลจากตัวชี้ไปยังสัญกรณ์อาร์เรย์ (หรือไม่รู้ด้วยซ้ำว่ามันเกี่ยวข้องกัน) นี่คือสี่วิธีในการเข้าถึงองค์ประกอบของอาร์เรย์:

  1. สัญลักษณ์อาร์เรย์ (การจัดทำดัชนี) ที่มีชื่ออาร์เรย์
  2. สัญลักษณ์อาร์เรย์ (การจัดทำดัชนี) ที่มีชื่อตัวชี้
  3. สัญลักษณ์ตัวชี้ (*) พร้อมชื่อตัวชี้
  4. สัญลักษณ์ตัวชี้ (*) พร้อมชื่ออาร์เรย์

 

int vals[5] = {10, 20, 30, 40, 50};
int *ptr;
ptr = vals;

array       element            pointer
notation    number     vals    notation

vals[0]     0          10      *(ptr + 0)
ptr[0]                         *(vals + 0)

vals[1]     1          20      *(ptr + 1)
ptr[1]                         *(vals + 1)

vals[2]     2          30      *(ptr + 2)
ptr[2]                         *(vals + 2)

vals[3]     3          40      *(ptr + 3)
ptr[3]                         *(vals + 3)

vals[4]     4          50      *(ptr + 4)
ptr[4]                         *(vals + 4)

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

reference to a pointerและpointer to a pointerฝูงชน

นี่เป็นบทความที่ยอดเยี่ยมที่อธิบายความแตกต่างซึ่งฉันจะอ้างและขโมยรหัสจาก :)

เป็นตัวอย่างเล็ก ๆ อาจเป็นเรื่องยากมากที่จะเห็นสิ่งที่ผู้เขียนต้องการทำถ้าคุณเจอสิ่งนี้:

//function prototype
void func(int*& rpInt); // I mean, seriously, int*& ??

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(pvar);
  ....
  return 0;
}

หรือในระดับที่น้อยกว่าบางสิ่งเช่นนี้:

//function prototype
void func(int** ppInt);

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(&pvar);
  ....
  return 0;
}

ดังนั้นในตอนท้ายของวันเราจะแก้ไขอะไรกับความหมายซึ่งพูดไม่ได้ทั้งหมด? ไม่มีอะไร

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

ความซับซ้อนและความสามารถในการแลกเปลี่ยน ( ดูเป็นตัวหนา) ที่มีการอ้างอิงซึ่งมักจะเป็นคำเตือนของพอยน์เตอร์และข้อผิดพลาดของผู้มาใหม่ทำให้เข้าใจพอยน์เตอร์ได้ยาก สิ่งสำคัญคือต้องเข้าใจเพื่อประโยชน์ของตัวชี้ว่าการอ้างอิงนั้นผิดกฎหมายใน C และ C ++ ด้วยเหตุผลที่ทำให้คุณสับสนlvalue- rvalueความหมาย

ตามคำตอบก่อนหน้านี้ตั้งข้อสังเกตหลายครั้งคุณจะมีโปรแกรมเมอร์ hotshot เหล่านี้ที่คิดว่าพวกเขาฉลาดโดยใช้******awesome_var->lol_im_so_clever()และเราส่วนใหญ่มีความผิดในการเขียนความโหดร้ายดังกล่าวในบางครั้ง แต่มันก็ไม่ได้รหัสที่ดีและแน่นอนว่า .

คำตอบนี้กลายเป็นนานกว่าที่ฉันคาดไว้ ...


5
ฉันคิดว่าคุณอาจให้คำตอบ C ++ กับคำถาม C ที่นี่ ... อย่างน้อยตอนที่สอง
Detly

ตัวชี้ไปยังพอยน์เตอร์ใช้กับ C เช่นกัน: p
David Titarenco

1
หึ? ฉันเห็นพอยน์เตอร์ถึงพอยน์เตอร์เมื่อผ่านอาร์เรย์เท่านั้น - ตัวอย่างที่สองของคุณใช้ไม่ได้กับรหัส C ที่เหมาะสมที่สุด นอกจากนี้คุณกำลังลาก C ไปสู่ความยุ่งเหยิงของ C ++ - การอ้างอิงใน C ไม่มีอยู่
new123456

คุณต้องติดต่อกับพอยน์เตอร์ถึงพอยน์เตอร์ในหลายกรณีที่ถูกกฎหมาย ตัวอย่างเช่นเมื่อจัดการกับตัวชี้ฟังก์ชั่นชี้ไปที่ฟังก์ชั่นที่ส่งกลับตัวชี้ ตัวอย่างอื่น: โครงสร้างที่สามารถเก็บหมายเลขตัวแปรของโครงสร้างอื่น ๆ และอีกมากมาย ...
David Titarenco

2
"ตัวชี้ไปยังการอ้างอิงที่ผิดกฎหมายใน C" - เหมือน "ขาด" :) เพิ่มเติม
คอส

29

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

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


3
ใช่. 'int foo = 5; int * pfoo = & foo; ดูว่ามีประโยชน์อย่างไร? ตกลงย้ายไป ... 'ฉันไม่ได้ใช้พอยน์เตอร์จนกว่าฉันจะเขียนไลบรารี่แบบลิสต์ของตัวเอง
John Lopez

2
+1 ฉันใช้เพื่อติวนักเรียน CS100 และปัญหามากมายของพวกเขาได้รับการแก้ไขเพียงแค่ไปพอยน์เตอร์ด้วยวิธีที่เข้าใจได้
benzado

1
+1 สำหรับบริบททางประวัติศาสตร์ หลังจากนั้นไม่นานฉันก็ไม่เคยเกิดขึ้นกับฉันเลย
Lumi

26

- มีบทความดีดีที่สนับสนุนความคิดที่ว่าตัวชี้มีความยากในเว็บไซต์โจ Spolsky เป็นมีดหมอ JavaSchools

[ข้อจำกัดความรับผิดชอบ - ฉันไม่ใช่ผู้เกลียดชัง Java - ต่อ ]


2
@ Jason - นั่นเป็นความจริง แต่ไม่ได้คัดค้านการโต้แย้ง
Steve Townsend

4
Spolsky ไม่ได้บอกว่า JavaSchools เป็นเหตุผลที่ผู้คนพบตัวชี้ที่ยาก เขาบอกว่าพวกเขาส่งผลให้คนที่ไม่รู้หนังสือชี้ด้วยวิทยาการคอมพิวเตอร์
benzado

1
@benzado - จุดยุติธรรม - โพสต์สั้น ๆ ของฉันจะดีขึ้นถ้ามันอ่าน 'บทความที่ดีที่สนับสนุนความคิดที่ชี้เป็นเรื่องยาก' สิ่งที่บทความบอกเป็นนัยก็คือ "การได้รับปริญญา CS จาก 'โรงเรียนดี' 'นั้นไม่ดีพอที่จะทำนายความสำเร็จในฐานะนักพัฒนาอย่างที่มันเคยเป็นในขณะที่" เข้าใจตัวชี้ "(และการเรียกซ้ำ) ยังคงเป็นเช่นนั้น
Steve Townsend

1
@ Steve Townsend: ฉันคิดว่าคุณพลาดจุดโต้เถียงของ Mr. Spolsky
jason

2
@Steve Townsend: Mr. Spolsky ยืนยันว่าโรงเรียน Java กำลังสร้างโปรแกรมเมอร์รุ่นหนึ่งที่ไม่รู้จักตัวชี้และการเรียกซ้ำไม่ใช่ตัวชี้ที่ยากเนื่องจากความแพร่หลายของโรงเรียน Java ดังที่คุณกล่าวว่า "มีบทความที่ดีเกี่ยวกับสาเหตุที่ทำให้ยาก" และเชื่อมโยงกับบทความดังกล่าวดูเหมือนว่าคุณจะมีการตีความหลัง ยกโทษให้ฉันถ้าฉันผิด
สัน

24

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

ฉันแน่ใจว่าเกือบทุกคนใน SO สงสัยว่าทำไมมันมีประโยชน์ที่จะได้รับขั้นพื้นฐาน เราลืมสิ่งที่เป็นเหมือนไม่รู้ว่าจะเขียนโปรแกรมอย่างไร การเล่นกับคอมพิวเตอร์ของเล่นดังกล่าวจะทำให้เกิดความคิดรวบยอดโดยที่คุณไม่สามารถโปรแกรมได้เช่นความคิดที่การคำนวณเป็นกระบวนการทีละขั้นตอนโดยใช้พื้นฐานเบื้องต้นจำนวนเล็กน้อยเพื่อสร้างโปรแกรมและแนวคิดของหน่วยความจำ ตัวแปรเป็นสถานที่ที่เก็บหมายเลขซึ่งที่อยู่หรือชื่อของตัวแปรนั้นแตกต่างจากหมายเลขที่มี มีความแตกต่างระหว่างเวลาที่คุณเข้าสู่โปรแกรมและเวลาที่ "รัน" ฉันเปรียบการเรียนรู้ที่จะเขียนโปรแกรมเป็นชุดของ "การกระแทกความเร็ว" เช่นโปรแกรมที่ง่ายมากจากนั้นวนซ้ำและรูทีนย่อยจากนั้นอาร์เรย์แล้วลำดับ I / O ตามลำดับจากนั้นตัวชี้และโครงสร้างข้อมูล

ในที่สุดเมื่อไปถึง C พอยน์เตอร์จะสับสนแม้ว่า K&R ทำงานได้ดีมากในการอธิบายพวกเขา วิธีที่ฉันเรียนรู้พวกเขาใน C คือรู้วิธีอ่านจากขวาไปซ้าย เช่นเมื่อฉันเห็นint *pในหัวฉันพูดว่า " pชี้ไปที่int" C ถูกประดิษฐ์ขึ้นหนึ่งก้าวจากภาษาแอสเซมบลีและนั่นคือสิ่งที่ฉันชอบเกี่ยวกับมัน - มันอยู่ใกล้กับ "พื้นดิน" ตัวชี้เช่นเดียวกับสิ่งอื่นนั้นยากที่จะเข้าใจถ้าคุณไม่มีสายดินนั้น


1
วิธีที่ดีในการเรียนรู้สิ่งนี้คือการเขียนโปรแกรมไมโครคอนโทรลเลอร์ 8 บิต พวกเขาเข้าใจง่าย ใช้คอนโทรลเลอร์ Atmel AVR พวกเขาได้รับการสนับสนุนโดย gcc
Xenu

@ Xenu: ฉันเห็นด้วย สำหรับฉันมันคือ Intel 8008 และ 8051 :-)
Mike Dunlavey

สำหรับฉันมันเป็นคอมพิวเตอร์ 8 บิตที่กำหนดเอง ("อาจจะ") ที่ MIT ในสายหมอกแห่งกาลเวลา
QuantumMechanic

Mike - คุณควรให้ CARDIAC เป็นนักเรียนของคุณ :)
QuantumMechanic

1
@Quantum: CARDIAC- อันที่ดีไม่เคยได้ยินเรื่องนี้ "อาจจะ" - ให้ฉันเดาว่าเมื่อ Sussman (และคนอื่น ๆ ) มีคนอ่านหนังสือ Mead-Conway และ fabbing ชิป LSI ของตัวเอง? นั่นเป็นเพียงเล็กน้อยหลังจากที่ฉันมีเวลา
Mike Dunlavey

17

ฉันไม่ได้รับพอยน์เตอร์จนกว่าฉันจะอ่านคำอธิบายใน K&R จนกว่าจะถึงจุดนั้นพอยน์เตอร์ก็ไม่สมเหตุสมผล ฉันอ่านเรื่องทั้งหมดที่ผู้คนพูดว่า "อย่าเรียนรู้พอยน์เตอร์พวกเขาสับสนและจะทำให้คุณปวดหัวและโป่งพองให้คุณ" ดังนั้นฉันจึงเบือนหน้าหนีเป็นเวลานานและสร้างแนวคิดที่ไม่จำเป็นนี้ขึ้นมา .

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

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

คลาสที่ต้องใช้พอยน์เตอร์เพื่อเข้าถึง / แก้ไขพอร์ตบนพีซีโดยใช้ตัวชี้เลขคณิตเพื่อระบุตำแหน่งหน่วยความจำที่แตกต่างกันและดูรหัส C ที่ซับซ้อนมากขึ้นซึ่งแก้ไขข้อโต้แย้งของพวกเขาทำให้ฉันคิดว่า


4
หากคุณมีทรัพยากร จำกัด ในการทำงานกับ (RAM, ROM, CPU) เช่นในแอปพลิเคชันแบบฝังตัวชี้จะทำให้เข้าใจได้เร็วขึ้น
Nick T

+1 สำหรับความคิดเห็นของ Nick - โดยเฉพาะอย่างยิ่งสำหรับการส่งผ่าน structs
new123456

12

นี่คือตัวอย่างของตัวชี้ / อาร์เรย์ที่ให้ฉันหยุดชั่วคราว สมมติว่าคุณมีสองอาร์เรย์:

uint8_t source[16] = { /* some initialization values here */ };
uint8_t destination[16];

และเป้าหมายของคุณคือคัดลอกเนื้อหา uint8_t จากปลายทางต้นทางโดยใช้ memcpy () เดาว่าสิ่งใดต่อไปนี้บรรลุเป้าหมาย:

memcpy(destination, source, sizeof(source));
memcpy(&destination, source, sizeof(source));
memcpy(&destination[0], source, sizeof(source));
memcpy(destination, &source, sizeof(source));
memcpy(&destination, &source, sizeof(source));
memcpy(&destination[0], &source, sizeof(source));
memcpy(destination, &source[0], sizeof(source));
memcpy(&destination, &source[0], sizeof(source));
memcpy(&destination[0], &source[0], sizeof(source));

คำตอบ (Spoiler Alert!) นั้นเป็นคำตอบทั้งหมด "ปลายทาง", "& ปลายทาง" และ "& ปลายทาง [0]" เป็นค่าเดียวกันทั้งหมด "& ปลายทาง" เป็นประเภทที่แตกต่างจากอีกสองรายการ แต่ยังคงเป็นค่าเดิม เช่นเดียวกันกับการเรียงสับเปลี่ยนของ "แหล่งที่มา"

ในฐานะที่เป็นกันฉันเองชอบรุ่นแรก


ฉันชอบรุ่นแรกเช่นกัน (เครื่องหมายวรรคตอนน้อยกว่า)
sigjuice

++ ดังนั้นฉัน แต่คุณต้องระวังsizeof(source)ให้ดีเพราะถ้าsourceเป็นตัวชี้sizeofมันจะไม่เป็นอย่างที่คุณต้องการ บางครั้งฉัน (ไม่เสมอ) เขียนsizeof(source[0]) * number_of_elements_of_sourceแค่อยู่ห่างจากจุดบกพร่องนั้น
Mike Dunlavey

ปลายทาง & ปลายทางและปลายทาง [0] ไม่เหมือนกัน - แต่แต่ละอันจะผ่านกลไกที่แตกต่างกันจะถูกแปลงเป็นโมฆะ * เดียวกันเมื่อใช้ใน memcpy อย่างไรก็ตามเมื่อใช้เป็นอาร์กิวเมนต์ของ sizeof คุณจะได้ผลลัพธ์ที่แตกต่างกันสองแบบและผลลัพธ์ที่แตกต่างกันสามแบบนั้นเป็นไปได้
gnasher729

ฉันคิดว่าต้องระบุที่อยู่ของโอเปอเรเตอร์หรือไม่
MarcusJ

7

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

สิ่งแรกที่ทำให้ฉันสับสนเกี่ยวกับพอยน์เตอร์เมื่อเรียนรู้ C นั้นง่าย:

char ch;
char str[100];
scanf("%c %s", &ch, str);

ความสับสนนี้ส่วนใหญ่เกิดจากการได้รับการแนะนำให้ใช้การอ้างอิงตัวแปรสำหรับอาร์กิวเมนต์ OUT ก่อนที่จะมีการแนะนำพอยน์เตอร์ให้ฉัน ฉันจำได้ว่าฉันข้ามการเขียนตัวอย่างแรก ๆ ในC สำหรับ Dummiesเพราะมันง่ายเกินไปที่จะไม่ได้รับโปรแกรมแรกที่ฉันเขียนเพื่อทำงาน (น่าจะเป็นเพราะสิ่งนี้)

สิ่งที่ทำให้เกิดความสับสนเกี่ยวกับเรื่องนี้คือสิ่งที่&chเกิดขึ้นจริงและทำไมstrไม่ต้องการมัน

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

char * x = NULL;
if (y) {
     char z[100];
     x = z;
}

เพื่อพยายามจัดสรรพื้นที่แบบไดนามิก มันไม่ทำงาน ฉันไม่แน่ใจว่ามันจะทำงานได้ แต่ฉันไม่รู้ว่ามันจะทำงานได้อย่างไร

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

หลังจากนั้นไม่นานฉันก็ถูกสอนให้รู้จักการเรียกซ้ำอีกครั้ง (ฉันได้เรียนรู้ด้วยตัวเองก่อนหน้านี้ แต่อยู่ในชั้นเรียนตอนนี้) และฉันถามว่ามันทำงานอย่างไรภายใต้ประทุน - ที่เก็บตัวแปรแยกต่างหาก อาจารย์ของฉันพูดว่า "ในกองซ้อน" และมีหลายสิ่งที่ชัดเจนสำหรับฉัน ฉันเคยได้ยินคำศัพท์นี้มาก่อนและเคยใช้งานซอฟต์แวร์ซ้อนกันมาก่อน ฉันเคยได้ยินคนอื่นพูดถึง "กองซ้อน" นานมาแล้ว แต่ลืมไปแล้ว

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

ตั้งแต่ฉันเขียน C ++ สำหรับโรงเรียนในปีหน้าหรือสองปีฉันมีประสบการณ์มากมายในการใช้พอยน์เตอร์สำหรับโครงสร้างข้อมูล ที่นี่ฉันมีชุดปัญหาใหม่ - การพอยน์เตอร์พอยน์เตอร์ ฉันจะมีพอยน์เตอร์หลายระดับ (เช่นnode ***ptr;) พาฉันขึ้นมา ฉันจะตรวจสอบตัวชี้จำนวนครั้งที่ผิดและในที่สุดก็หันไปหาจำนวนที่*ฉันต้องการโดยการลองผิดลองถูก

ในบางจุดฉันได้เรียนรู้ว่าฮีปของโปรแกรมทำงานอย่างไร (เรียงลำดับ แต่ดีพอที่มันจะไม่ทำให้ฉันไม่ทำงานอีกต่อไปในตอนกลางคืน) ฉันจำได้ว่าอ่านว่าถ้าคุณดูสักสองสามไบต์ก่อนที่ตัวชี้mallocบนระบบหนึ่งจะกลับมาคุณจะเห็นจำนวนข้อมูลที่จัดสรรจริง ฉันรู้ว่ารหัสในmallocสามารถขอหน่วยความจำเพิ่มเติมจากระบบปฏิบัติการและหน่วยความจำนี้ไม่ได้เป็นส่วนหนึ่งของไฟล์ปฏิบัติการของฉัน การมีความคิดในการทำงานที่ดีว่าการmallocทำงานนั้นมีประโยชน์จริงๆ

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

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

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

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

int foo(struct frog * f, int x, int y) {
    struct leg * g = f->left_leg;
    struct toe * t = g->big_toe;
    process(t);

เพื่อที่ว่าถ้าฉันสกรูประเภทตัวชี้มันชัดเจนมากโดยข้อผิดพลาดของคอมไพเลอร์ปัญหาคืออะไร ถ้าฉันทำ:

int foo(struct frog * f, int x, int y) {
    process(f->left_leg->big_toe);

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


1
+1 อย่างละเอียดและลึกซึ้ง ฉันลืมเกี่ยวกับ scanf แต่ตอนนี้ที่คุณนำมันมาฉันจำได้ว่ามีความสับสนเดียวกัน
Joe White

6

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

  1. การเขียนรูทีนที่ใช้ตัวชี้ไปยังจำนวนเต็มและปรับเปลี่ยนจำนวนเต็ม สิ่งนี้ทำให้ฉันมีรูปแบบที่จำเป็นสำหรับการสร้างแบบจำลองทางจิตของวิธีการทำงานของพอยน์เตอร์

  2. การจัดสรรหน่วยความจำแบบไดนามิกหนึ่งมิติ การหาการจัดสรรหน่วยความจำ 1 มิติทำให้ฉันเข้าใจแนวคิดของตัวชี้

  3. การจัดสรรหน่วยความจำแบบไดนามิกสองมิติ การหาการจัดสรรหน่วยความจำ 2 มิติเสริมแนวคิดดังกล่าว แต่สอนด้วยว่าตัวชี้ต้องการการจัดเก็บและต้องนำมาพิจารณาด้วย

  4. ความแตกต่างระหว่างตัวแปรสแต็กตัวแปรโกลบอลและหน่วยความจำฮีพ การค้นหาความแตกต่างเหล่านี้สอนให้ฉันทราบถึงประเภทของหน่วยความจำที่ตัวชี้ชี้ / อ้างอิง

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

กลับไปที่คำถามเดิมของคุณ จากรายการก่อนหน้ามีหลายรายการที่ฉันมีปัญหาในการจับ

  1. จะใช้ตัวชี้อย่างไรและทำไม
  2. มีความแตกต่างและคล้ายกับอาร์เรย์อย่างไร
  3. ทำความเข้าใจกับที่เก็บข้อมูลตัวชี้
  4. ทำความเข้าใจว่าตัวชี้ชี้ไปที่ใดและที่ไหน

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

1
@AlexStarbuck - ฉันไม่ได้ตั้งใจให้เรื่องนี้ฟังดูโอ้อวด แต่วิธีการทางวิทยาศาสตร์เป็นเครื่องมือที่ยอดเยี่ยม วาดภาพเกี่ยวกับสิ่งที่คุณคิดว่าอาจเกิดขึ้นสำหรับสถานการณ์เฉพาะ ตั้งโปรแกรมเพื่อทดสอบและวิเคราะห์สิ่งที่คุณมี ตรงกับสิ่งที่คุณคาดหวังหรือไม่ หากไม่ระบุที่แตกต่างกันอย่างไร ทำซ้ำตามความจำเป็นค่อยๆเพิ่มความซับซ้อนเพื่อทดสอบทั้งความเข้าใจและแบบจำลองทางจิตของคุณ
Sparky

6

ฉันมี "ช่วงเวลาตัวชี้" ของฉันทำงานกับโปรแกรมโทรศัพท์ในซีฉันต้องเขียนโปรแกรมจำลองการแลกเปลี่ยน AXE10 โดยใช้โพรโทคอลวิเคราะห์ที่เข้าใจคลาสสิกซีเท่านั้นทุกอย่างที่เกี่ยวกับการรู้พอยน์เตอร์ ฉันพยายามเขียนโค้ดโดยไม่มีพวกเขา (เดี๋ยวก่อนฉันเป็น "ตัวชี้ล่วงหน้า" ทำให้ฉันหย่อนบ้าง) และล้มเหลวอย่างสิ้นเชิง

กุญแจสำคัญในการเข้าใจพวกเขาสำหรับฉันคือตัวดำเนินการ & (ที่อยู่) เมื่อฉันเข้าใจที่&iหมายถึง "ที่อยู่ของฉัน" จากนั้นทำความเข้าใจที่*iหมายถึง "เนื้อหาของที่อยู่ที่ชี้ไปที่ฉัน" มาในภายหลัง เมื่อใดก็ตามที่ฉันเขียนหรืออ่านรหัสของฉันฉันมักจะทำซ้ำสิ่งที่ "&" ความหมายและสิ่งที่ "*" ความหมายและในที่สุดฉันก็มาใช้พวกเขาอย่างสังหรณ์ใจ

สำหรับความอับอายของฉันฉันถูกบังคับให้ VB และ Java ดังนั้นความรู้ของตัวชี้ของฉันไม่คมชัดเหมือนครั้งหนึ่งเคย แต่ฉันดีใจที่ฉัน "post-pointer" อย่าขอให้ฉันใช้ห้องสมุดที่ต้องให้ฉันเข้าใจ* * p


หาก&iเป็นที่อยู่และ*iเนื้อหาคือiอะไร
โทมัส Ahle

2
ฉันกำลังใช้งาน i มากเกินไป สำหรับตัวแปรตามอำเภอใจฉัน & ฉันหมายถึง "ที่อยู่ของ" ฉันฉันหมายถึงตัวเอง "เนื้อหาของ & ฉัน" และ * ฉันหมายถึง "ถือว่าเนื้อหาของ & ฉันเป็นที่อยู่ไปที่ที่อยู่นั้นและส่งกลับ เนื้อหา"
Gary Rowe

5

ปัญหาหลักสำหรับพอยน์เตอร์อย่างน้อยสำหรับฉันคือฉันไม่ได้เริ่มต้นด้วย C. ฉันเริ่มต้นด้วย Java ความคิดทั้งหมดของพอยน์เตอร์นั้นต่างชาติจริง ๆ จนกระทั่งสองชั้นเรียนในวิทยาลัยที่ฉันคาดว่าจะรู้ซีดังนั้นฉันจึงสอนตัวเองเกี่ยวกับพื้นฐานของ C และวิธีการใช้พอยน์เตอร์ในความรู้สึกพื้นฐานของพวกเขา ถึงแม้ทุกครั้งที่ฉันพบว่าตัวเองกำลังอ่านรหัส C ฉันต้องค้นหาไวยากรณ์ของตัวชี้

ดังนั้นในประสบการณ์ที่ จำกัด ของฉัน (โลกแห่งความจริง 1 ปี + 4 ในวิทยาลัย) พอยน์เตอร์ทำให้ฉันสับสนเพราะฉันไม่เคยใช้มันในสิ่งอื่นใดนอกจากการตั้งค่าห้องเรียน และฉันสามารถเห็นอกเห็นใจกับนักเรียนตอนนี้เริ่มต้น CS กับ JAVA แทน C หรือ C ++ ดังที่คุณกล่าวว่าคุณได้เรียนรู้พอยน์เตอร์ในยุค 'ยุคหินใหม่' และอาจใช้มันตั้งแต่นั้นเป็นต้นมา สำหรับเราคนใหม่ความคิดในการจัดสรรหน่วยความจำและการคำนวณเลขคณิตนั้นเป็นสิ่งที่แปลกใหม่เพราะภาษาเหล่านี้มีความเป็นนามธรรม

ป.ล. หลังจากอ่านบทความของ Spolsky แล้วคำอธิบายของเขาเกี่ยวกับ 'JavaSchools' ไม่เหมือนกับสิ่งที่ฉันได้เรียนในวิทยาลัยที่ Cornell ('05 -'09) ฉันใช้โครงสร้างและฟังก์ชั่นการเขียนโปรแกรม (sml), ระบบปฏิบัติการ (C), อัลกอริธึม (ปากกาและกระดาษ) และคลาสอื่น ๆ ทั้งหมดที่ไม่ได้สอนด้วยภาษาจาวา อย่างไรก็ตามชั้นเรียนอินโทรและวิชาเลือกทั้งหมดทำด้วยภาษาจาวาเพราะมีคุณค่าในการไม่พลิกโฉมวงล้อเมื่อคุณพยายามทำสิ่งที่สูงกว่าการใช้ hashtable พร้อมกับพอยน์เตอร์


4
จริงๆแล้วถ้าคุณมีปัญหากับพอยน์เตอร์ผมก็ไม่แน่ใจว่าประสบการณ์ของคุณที่ Cornell ขัดแย้งกับบทความของ Joel มาก เห็นได้ชัดว่าสมองของคุณมีสายในชุด Java-mindset เพื่อชี้ประเด็น
jkerian

5
วัด? การอ้างอิงใน Java (หรือ C # หรือ Python หรือภาษาอื่น ๆ นับสิบ) เป็นเพียงตัวชี้ที่ไม่มีเลขคณิต การทำความเข้าใจพอยน์เตอร์หมายถึงการเข้าใจว่าเหตุใดจึงvoid foo(Clazz obj) { obj = new Clazz(); }ไม่มีโอปในขณะvoid bar(Clazz obj) { obj.quux = new Quux(); }ที่โต้แย้งอาร์กิวเมนต์ ...

1
ฉันรู้ว่าสิ่งอ้างอิงอยู่ใน Java แต่ฉันแค่บอกว่าถ้าคุณขอให้ฉันสะท้อนใน Java หรือเขียนสิ่งที่มีความหมายใน CI ไม่สามารถตัดมันออก มันต้องมีการวิจัยมากมายเช่นการเรียนรู้มันเป็นครั้งแรกทุกครั้ง
shoebox 639

1
เป็นอย่างไรบ้างที่คุณผ่านคลาสระบบปฏิบัติการใน C โดยไม่ต้องพูดคล่องใน C ไม่มีความผิดที่ตั้งใจมันเป็นเพียงฉันจำได้ว่าต้องพัฒนาระบบปฏิบัติการอย่างง่ายตั้งแต่เริ่มต้น ฉันต้องใช้พอยน์เตอร์หลายพันครั้ง ...
แรงดึงดูด

5

นี่คือคำตอบที่ไม่ใช่: ใช้ cdecl (หรือ c ++ decl) เพื่อหาคำตอบ:

eisbaw@leno:~$ cdecl explain 'int (*(*foo)(const void *))[3]'
declare foo as pointer to function (pointer to const void) returning pointer to array 3 of int

4

พวกเขาเพิ่มมิติพิเศษให้กับรหัสโดยไม่มีการเปลี่ยนแปลงที่สำคัญในไวยากรณ์ คิดเกี่ยวกับสิ่งนี้:

int a;
a = 5

การเปลี่ยนแปลงมีเพียงสิ่งเดียวเท่านั้น: a. คุณสามารถเขียนa = 6และผลลัพธ์นั้นชัดเจนสำหรับคนส่วนใหญ่ แต่ตอนนี้พิจารณา:

int *a;
a = &some_int;

มีสองสิ่งเกี่ยวกับสิ่งaที่เกี่ยวข้องในเวลาที่ต่างกัน: ค่าจริงของaตัวชี้และค่า "หลัง" ตัวชี้ คุณสามารถเปลี่ยนa:

a = &some_other_int;

... และsome_intยังคงอยู่ใกล้กับที่ซึ่งมีค่าเท่ากัน แต่คุณสามารถเปลี่ยนสิ่งที่ชี้ไปที่:

*a = 6;

มีช่องว่างทางแนวคิดระหว่างa = 6ซึ่งมีผลข้างเคียงในท้องถิ่นเท่านั้นและ*a = 6ซึ่งอาจส่งผลกระทบต่อสิ่งอื่น ๆ ในที่อื่น ๆ จุดของที่นี่คือไม่ได้ว่าแนวคิดของความร้ายเป็นเรื่องยุ่งยากโดยเนื้อแท้ แต่นั่นเพราะคุณสามารถทำทั้งสองทันทีสิ่งในท้องถิ่นที่มีaหรือสิ่งที่ทางอ้อมด้วย*a... ที่อาจจะเป็นสิ่งที่สร้างความสับสนให้ผู้คน


4

ฉันได้โปรแกรมใน c ++ เป็นเวลา 2 ปีแล้วเปลี่ยนเป็น Java (5 ปี) และไม่เคยมองย้อนกลับไป อย่างไรก็ตามเมื่อเร็ว ๆ นี้ฉันต้องใช้ของพื้นเมืองบางอย่างฉันก็ค้นพบ (ด้วยความประหลาดใจ) ที่ฉันไม่ได้ลืมอะไรเลยเกี่ยวกับพอยน์เตอร์และฉันก็พบว่ามันใช้งานง่าย นี่เป็นสิ่งที่ตรงกันข้ามกับสิ่งที่ฉันพบเมื่อ 7 ปีที่แล้วเมื่อฉันพยายามเข้าใจแนวคิดนี้เป็นครั้งแรก ดังนั้นฉันเดาความเข้าใจและความชอบเป็นเรื่องของการกำหนดโปรแกรม? :)

หรือ

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

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


3

พอยน์เตอร์นั้นทำได้ยากเนื่องจากมีการอ้อม


"ได้มีการกล่าวว่าไม่มีปัญหาทางวิทยาศาสตร์คอมพิวเตอร์ซึ่งไม่สามารถแก้ไขได้ด้วยการเปลี่ยนทิศทางอีกระดับหนึ่ง" (ไม่มีความคิดที่พูดก่อน)
The Archetypal Paul

มันเหมือนเวทมนตร์ที่การชี้แนะผิดเป็นสิ่งที่ทำให้ผู้คนสับสน (แต่มันยอดเยี่ยมมาก)
Nick T

3

พอยน์เตอร์ (รวมถึงด้านอื่น ๆ ของการทำงานระดับต่ำ) ต้องการให้ผู้ใช้กำจัดเวทมนตร์

โปรแกรมเมอร์ระดับสูงส่วนใหญ่ชอบเวทมนตร์


3

พอยน์เตอร์เป็นวิธีการจัดการกับความแตกต่างระหว่างการจัดการกับวัตถุและวัตถุเอง (ตกลงไม่จำเป็นต้องเป็นวัตถุ แต่คุณรู้ว่าฉันหมายถึงอะไรรวมถึงที่ใจของฉันอยู่ด้วย)

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

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

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

แท้จริงแล้วในแอปพลิเคชั่น C ++ ที่ทันสมัยจำนวนมากมักจะเป็นกรณีที่การคำนวณทางคณิตศาสตร์ใด ๆ เราไม่ต้องการให้นักพัฒนาทำเลขคณิตของตัวชี้ไปทั่ว เราต้องการ API แบบรวมศูนย์ที่ผ่านการทดสอบเป็นอย่างดีซึ่งทำตัวเลขคณิตของตัวชี้ในระดับต่ำสุด การเปลี่ยนแปลงรหัสนี้จะต้องกระทำด้วยความระมัดระวังและการทดสอบอย่างละเอียด


3

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

ใน C พอยน์เตอร์จะคุ้นเคยกับสิ่งอื่น ๆ :

  • กำหนดโครงสร้างข้อมูลแบบเรียกซ้ำ

ใน C คุณจะกำหนดรายการจำนวนเต็มที่เชื่อมโยงดังนี้:

struct node {
  int value;
  struct node* next;
}

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

data List = List Int List | Null

ค่อนข้างตรงไปตรงมา - รายการว่างเปล่าหรือเกิดจากค่าและส่วนที่เหลือของรายการ

  • วนซ้ำสตริงและอาร์เรย์

นี่คือวิธีที่คุณอาจใช้ฟังก์ชันfooกับอักขระทุกตัวของสตริงใน C:

char *c;
for (c = "hello, world!"; *c != '\0'; c++) { foo(c); }

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

  • บรรลุความหลากหลาย

นี่คือลายเซ็นฟังก์ชันที่แท้จริงที่พบในglib :

typedef struct g_list GList;

void  g_list_foreach    (GList *list,
                 void (*func)(void *data, void *user_data),
                         void* user_data);

โว้ว! ที่ค่อนข้างคำหนึ่งของvoid*'s และมันเป็นเพียงการประกาศฟังก์ชั่นที่วนซ้ำรายการที่มีสิ่งต่าง ๆ ใช้ฟังก์ชันกับสมาชิกแต่ละคน เปรียบเทียบกับวิธีการmapประกาศใน Haskell:

map::(a->b)->[a]->[b]

นั่นคือตรงไปตรงมามากขึ้น: mapเป็นฟังก์ชั่นที่ใช้ฟังก์ชั่นที่แปลงเป็นaไปbและนำไปใช้กับรายการaของที่จะให้ผลผลิตรายการb's เช่นเดียวกับในฟังก์ชั่นซีg_list_foreach, mapไม่จำเป็นต้องรู้อะไรในความหมายของตัวเองเกี่ยวกับประเภทที่มันจะถูกนำไปใช้

เพื่อสรุป:

ผมคิดว่า C ตัวชี้จะเป็นมากน้อยสับสนถ้าคนแรกที่ได้เรียนรู้เกี่ยวกับโครงสร้าง recursive ข้อมูล iterators, polymorphism ฯลฯ เป็นแนวคิดที่แยกจากกันและจากนั้นได้เรียนรู้วิธีการชี้สามารถนำมาใช้ในการดำเนินการความคิดเหล่านั้นใน Cมากกว่า mashing ทั้งหมดเหล่านี้ แนวคิดร่วมกันเป็นเรื่องเดียวของ "พอยน์เตอร์"


วัตถุประสงค์ของ c != NULLใน "สวัสดีชาวโลก" ตัวอย่างเช่น ... *c != '\0'คุณหมายถึง
Olaf Seibert

2

ฉันคิดว่ามันต้องมีรากฐานที่แข็งแกร่งซึ่งอาจมาจากระดับเครื่องด้วยการแนะนำรหัสเครื่องจักรการประกอบและวิธีแสดงรายการและโครงสร้างข้อมูลใน RAM ใช้เวลาเล็กน้อยทำการบ้านหรือฝึกแก้ปัญหาและคิดบ้าง

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


2
พอยน์เตอร์ Groking ไม่จำเป็นต้องเข้าใจรหัสเครื่องหรือชุดประกอบ
สัน

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

2

ปัญหาที่ฉันมี (มักสอนด้วยตนเอง) คือ "เมื่อ" เพื่อใช้ตัวชี้ ฉันสามารถล้อมรอบไวยากรณ์เพื่อสร้างพอยน์เตอร์ได้ แต่ฉันต้องรู้ภายใต้สถานการณ์ที่ควรใช้พอยน์เตอร์

ฉันเป็นคนเดียวที่มีความคิดนี้หรือไม่? ;-)


ฉันเข้าใจ. การตอบสนองของฉันเกี่ยวข้องกับสิ่งนั้น
J. Polfer

2

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


2

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

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


2

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

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

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

อย่างน้อยนั่นก็เป็นวิธีที่ฉันเห็นในตอนนี้!


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

1

พูดในฐานะมือใหม่ C ++ ที่นี่:

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

(1) การประกาศตัวแปร:

A a(1);

เมื่อเทียบกับ

A a = A(1);

เมื่อเทียบกับ

A* a = new A(1); 

และเห็นได้ชัดว่า

A a(); 

เป็นการประกาศฟังก์ชันและไม่ใช่การประกาศตัวแปร ในภาษาอื่นมีวิธีการประกาศตัวแปรเพียงวิธีเดียว

(2) เครื่องหมายแอมเปอร์แซนด์ถูกใช้ในหลายวิธี ถ้ามันเป็น

int* i = &a;

ดังนั้น & a คือที่อยู่หน่วยความจำ

OTOH ถ้าเป็น

void f(int &a) {}

ดังนั้น & a คือพารามิเตอร์แบบส่งต่อโดยอ้างอิง

แม้ว่าสิ่งนี้อาจดูเล็กน้อย แต่ก็อาจสร้างความสับสนให้กับผู้ใช้รายใหม่ - ฉันมาจากภาษาจาวาและภาษาจาวาที่ใช้ตัวดำเนินการที่สม่ำเสมอกว่า

(3) ความสัมพันธ์ของ Array-pointer

สิ่งหนึ่งที่น่าผิดหวังเล็กน้อยที่จะเข้าใจคือตัวชี้

int* i

สามารถเป็นตัวชี้ไปยัง int

int *i = &n; // 

หรือ

สามารถเป็นอาร์เรย์ให้กับ int

int* i = new int[5];

และเพื่อให้สิ่งต่าง ๆ ที่ยุ่งเหยิงพอยน์เตอร์และอาเรย์ไม่สามารถใช้แทนกันได้ในทุกกรณีและพอยน์เตอร์ไม่สามารถส่งผ่านเป็นพารามิเตอร์อาเรย์ได้

สิ่งนี้สรุปถึงความผิดหวังพื้นฐานบางอย่างที่ฉันมีกับ C / C ++ และพอยน์เตอร์ซึ่ง IMO นั้นประกอบขึ้นจากความจริงที่ว่า C / C ++ มีนิสัยเฉพาะภาษาเหล่านี้ทั้งหมด


C ++ 2011 ได้ปรับปรุงสิ่งต่าง ๆ ให้ดีขึ้นเท่าที่มีการประกาศตัวแปร
gnasher729

0

โดยส่วนตัวฉันไม่เข้าใจตัวชี้ถึงแม้ว่าหลังจากสำเร็จการศึกษาและหลังจากงานแรก สิ่งเดียวที่ฉันรู้ก็คือคุณต้องการมันสำหรับรายการที่เชื่อมโยงต้นไม้ไบนารีและสำหรับการส่งผ่านอาร์เรย์เข้าสู่ฟังก์ชั่น นี่คือสถานการณ์แม้ในงานแรกของฉัน เมื่อฉันเริ่มให้สัมภาษณ์ฉันเข้าใจว่าแนวคิดของตัวชี้นั้นลึกและมีประโยชน์และศักยภาพอย่างมาก จากนั้นฉันก็เริ่มอ่าน K & R และเขียนโปรแกรมทดสอบของตัวเอง เป้าหมายทั้งหมดของฉันคือการขับเคลื่อนงาน
ในเวลานี้ฉันพบว่าพอยน์เตอร์นั้นไม่เลวหรือยากถ้าพวกเขาได้รับการสอนในวิธีที่ดี น่าเสียดายที่เมื่อฉันเรียนรู้ C ในการสำเร็จการศึกษาครูผู้สอนไม่ได้ตระหนักถึงตัวชี้และแม้แต่การมอบหมายก็ใช้พอยน์เตอร์น้อย ในระดับบัณฑิตศึกษาการใช้ตัวชี้เป็นเพียงการสร้างไบนารีทรีและรายการที่เชื่อมโยงเท่านั้น ความคิดนี้ที่คุณไม่ต้องการความเข้าใจที่ถูกต้องของตัวชี้เพื่อทำงานกับพวกเขาฆ่าความคิดในการเรียนรู้พวกเขา


0

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


0

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

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

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

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