เช่นเดียวกับทุกสิ่งที่ดูน่ากลัวกว่าในตอนแรกวิธีที่ดีที่สุดในการเอาชนะความกลัวครั้งแรกคือการจมดิ่งลงไปในความรู้สึกไม่สบายตัวจากสิ่งที่ไม่รู้จัก ! บางครั้งก็เป็นเช่นนั้นที่เราเรียนรู้มากที่สุดหลังจากนั้น
น่าเสียดายที่มีข้อ จำกัด ในขณะที่คุณยังเรียนรู้ที่จะใช้ฟังก์ชันคุณไม่ควรสวมบทบาทเป็นครูเช่น ฉันมักจะอ่านคำตอบจากผู้ที่ดูเหมือนไม่รู้วิธีใช้realloc
(เช่นคำตอบที่ยอมรับในปัจจุบัน! ) บอกคนอื่นว่าใช้ไม่ถูกต้องในบางครั้งภายใต้หน้ากากว่าพวกเขาละเว้นการจัดการข้อผิดพลาดแม้ว่านี่จะเป็นข้อผิดพลาดทั่วไปก็ตาม ที่ต้องพูดถึง นี่คือคำตอบที่อธิบายวิธีการใช้realloc
อย่างถูกต้อง จะทราบว่าคำตอบคือการจัดเก็บค่าตอบแทนเป็นที่แตกต่างกันตัวแปรเพื่อดำเนินการตรวจสอบข้อผิดพลาด
ทุกครั้งที่คุณเรียกใช้ฟังก์ชันและทุกครั้งที่คุณใช้อาร์เรย์คุณจะใช้ตัวชี้ การแปลงกำลังเกิดขึ้นโดยปริยายซึ่งหากมีสิ่งใดน่าจะน่ากลัวกว่านี้เนื่องจากเป็นสิ่งที่เราไม่เห็นซึ่งมักทำให้เกิดปัญหามากที่สุด ตัวอย่างเช่นความจำรั่ว ...
ตัวดำเนินการอาร์เรย์เป็นตัวดำเนินการตัวชี้ array[x]
เป็นทางลัดสำหรับ*(array + x)
ซึ่งสามารถแบ่งออกเป็น: *
และ(array + x)
. เป็นไปได้มากว่านั่น*
คือสิ่งที่ทำให้คุณสับสน เราสามารถกำจัดส่วนที่เพิ่มออกจากปัญหาได้มากขึ้นโดยสมมติว่าx
เป็น0
ดังนั้นจึงarray[0]
กลายเป็น*array
เพราะการเพิ่ม0
จะไม่เปลี่ยนค่า ...
... และทำให้เราสามารถเห็นได้ว่าเทียบเท่ากับ*array
array[0]
คุณสามารถใช้อันที่คุณต้องการใช้อีกอันและในทางกลับกัน ตัวดำเนินการอาร์เรย์เป็นตัวดำเนินการตัวชี้
malloc
, realloc
และเพื่อน ๆ ไม่ได้คิดค้นแนวคิดของตัวชี้ที่คุณเคยใช้มาตลอดนั้น พวกเขาเพียงใช้นี้ในการดำเนินการบางส่วนคุณสมบัติอื่น ๆ ซึ่งเป็นรูปแบบที่แตกต่างกันของระยะเวลาการจัดเก็บที่เหมาะสมที่สุดเมื่อคุณต้องการที่รุนแรงเปลี่ยนแปลงแบบไดนามิกในขนาด
เป็นเรื่องน่าเสียดายที่คำตอบที่ได้รับการยอมรับในปัจจุบันยังขัดต่อคำแนะนำอื่น ๆ ที่เป็นที่ยอมรับอย่างดีเกี่ยวกับ StackOverflowและในขณะเดียวกันก็พลาดโอกาสที่จะนำเสนอคุณลักษณะที่ไม่ค่อยมีใครรู้จักซึ่งส่องแสงสำหรับกรณีนี้: อาร์เรย์แบบยืดหยุ่น สมาชิก! นั่นเป็นคำตอบที่ค่อนข้างแย่ ... :(
เมื่อคุณกำหนดของคุณstruct
ให้ประกาศอาร์เรย์ของคุณที่ส่วนท้ายของโครงสร้างโดยไม่มีขอบเขตบน ตัวอย่างเช่น:
struct int_list {
size_t size;
int value[];
};
สิ่งนี้จะช่วยให้คุณสามารถรวมอาร์เรย์ของคุณเข้าด้วยกันint
ในการจัดสรรเดียวกันกับของคุณcount
และการมีขอบเขตเช่นนี้จะมีประโยชน์มาก !
sizeof (struct int_list)
จะทำหน้าที่เป็นแม้ว่าจะvalue
มีขนาด 0, ดังนั้นมันจะบอกคุณขนาดของโครงสร้างที่มีรายการที่ว่างเปล่า คุณยังต้องเพิ่มขนาดที่ส่งถึงrealloc
เพื่อระบุขนาดรายการของคุณ
เคล็ดลับที่มีประโยชน์อีกประการหนึ่งคือการจำไว้ว่าrealloc(NULL, x)
เทียบเท่าmalloc(x)
และเราสามารถใช้สิ่งนี้เพื่อทำให้รหัสของเราง่ายขึ้น ตัวอย่างเช่น:
int push_back(struct int_list **fubar, int value) {
size_t x = *fubar ? fubar[0]->size : 0
, y = x + 1;
if ((x & y) == 0) {
void *temp = realloc(*fubar, sizeof **fubar
+ (x + y) * sizeof fubar[0]->value[0]);
if (!temp) { return 1; }
*fubar = temp; // or, if you like, `fubar[0] = temp;`
}
fubar[0]->value[x] = value;
fubar[0]->size = y;
return 0;
}
struct int_list *array = NULL;
เหตุผลที่ฉันเลือกใช้struct int_list **
เป็นอาร์กิวเมนต์แรกอาจดูเหมือนไม่ชัดเจนในทันที แต่ถ้าคุณคิดถึงอาร์กิวเมนต์ที่สองการเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นvalue
จากภายในpush_back
จะไม่สามารถมองเห็นได้จากฟังก์ชันที่เราเรียกใช้ใช่ไหม เช่นเดียวกันกับอาร์กิวเมนต์แรกและเราจำเป็นต้องสามารถปรับเปลี่ยนของเราarray
ได้ไม่ใช่แค่ที่นี่แต่อาจเป็นไปได้ในฟังก์ชันอื่น ๆ ที่เราส่งต่อให้ ...
array
เริ่มจากการชี้ไปที่ความว่างเปล่า มันเป็นรายการว่างเปล่า การเริ่มต้นจะเหมือนกับการเพิ่มเข้าไป ตัวอย่างเช่น:
struct int_list *array = NULL;
if (!push_back(&array, 42)) {
// success!
}
ปล. อย่าลืมว่าfree(array);
เมื่อคุณทำเสร็จแล้ว!