การเข้าถึงที่ไม่ถูกต้อง
การเข้าถึงนอกขอบเขตและข้อมูลที่ยังไม่เริ่มต้น
การเข้าถึงที่ไม่ถูกต้อง เป็นบทเรียน C Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
นอกเหนือจากหน่วยความจำรั่ว
หน่วยความจำรั่วทำให้เสียพื้นที่หน่วยความจำ แต่ข้อผิดพลาดจาก การเข้าถึงที่ไม่ถูกต้อง ทำให้ข้อมูลเสียหาย memcheck ตรวจจับได้หลายกรณี:
- อ่านหรือเขียนเลยจุดสิ้นสุดของบัฟเฟอร์
- ใช้หน่วยความจำหลังเรียก
free - อ่านค่าที่ยังไม่ได้กำหนด
- อ่านหรือเขียนผ่านพอยน์เตอร์ที่ไม่ถูกต้อง
ข้อผิดพลาดเหล่านี้ทำให้เกิดการล้มเหลวที่อันตรายที่สุดและทำซ้ำได้ยากที่สุด
การเขียนเกินขอบเขต
โค้ดนี้จัดสรรพื้นที่สำหรับ int จำนวน 5 ตัว แต่เขียนไปยังดัชนี 5 ซึ่งเป็นช่องที่หก
ดัชนี 5 อยู่ถัดจากจุดสิ้นสุดหนึ่งสมาชิก memcheck จะแจ้ง การเขียนไม่ถูกต้องขนาด 4 ที่บรรทัดนี้
#include <stdlib.h>
int main(void) {
int *a = malloc(5 * sizeof(int));
a[5] = 99; /* valid indices are 0..4 */
free(a);
return 0;
}การอ่านข้อผิดพลาด
รายงานจะระบุการดำเนินการ ขนาด และความสัมพันธ์กับบล็อก:
Invalid write of size 4Address 0x... is 0 bytes after a block of size 20 alloc'd
ข้อความ '0 bytes after a block of size 20' บอกว่าการเข้าถึงเกิดขึ้นทันทีหลังการจัดสรรขนาด 20 ไบต์ (int 5 ตัว)
การอ่านเกินขอบเขต
การอ่านก็ตรวจจับได้เช่นกัน ในกรณีนี้ลูปทำงานเลยไปหนึ่งสมาชิก
memcheck จะแจ้ง การอ่านไม่ถูกต้องขนาด 4 ในรอบสุดท้าย แม้โปรแกรมอาจจะ 'ทำงานได้' โดยบังเอิญ
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int *a = calloc(3, sizeof(int));
for (int i = 0; i <= 3; i++) /* should be i < 3 */
printf("%d\n", a[i]);
free(a);
return 0;
}ค่าที่ยังไม่ได้กำหนด
malloc ไม่ได้กำหนดค่าให้หน่วยความจำเป็นศูนย์ การนำหน่วยความจำไปใช้ก่อนกำหนดค่าจะทำให้ได้ค่าที่คาดเดาไม่ได้
memcheck จะแจ้ง การกระโดดหรือการย้ายตามเงื่อนไขขึ้นอยู่กับค่าที่ไม่ได้กำหนด เมื่อค่าดังกล่าวส่งผลต่อการควบคุมการทำงานหรือผลลัพธ์
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int *p = malloc(sizeof(int));
if (*p == 0) /* *p was never set */
printf("zero\n");
free(p);
return 0;
}เหตุใดรายงานค่าที่ยังไม่ได้กำหนดจึงล่าช้า
memcheck จะไม่แจ้งเตือนทันทีที่คุณอ่านหน่วยความจำที่ยังไม่ได้กำหนดค่า แต่จะติดตาม 'การมีค่าที่กำหนด' และรายงานก็ต่อเมื่อค่าที่ไม่กำหนดนั้น มีผลจริง เช่น ถูกใช้ในแขนง การแสดงผล หรือการเรียกระบบ
วิธีนี้ช่วยหลีกเลี่ยงการแจ้งเตือนเกินจริงเมื่อคุณเพียงคัดลอกไบต์ที่ยังไม่ได้กำหนดค่าไปมาโดยไม่ก่อให้เกิดอันตราย
การใช้หน่วยความจำหลังคืนแล้ว
การแตะต้องหน่วยความจำหลังจากคืนหน่วยความจำแล้วถือเป็นพฤติกรรมที่ไม่ได้กำหนด memcheck ตรวจจับกรณีนี้ได้อย่างแม่นยำ
โปรแกรมจะแจ้ง การอ่านไม่ถูกต้องขนาด 4 พร้อมหมายเหตุ Address ... is 0 bytes inside a block of size 4 free'd และยังแสดงตำแหน่งที่เรียก free ด้วย
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int *p = malloc(sizeof(int));
*p = 7;
free(p);
printf("%d\n", *p); /* read after free */
return 0;
}การคืนหน่วยความจำซ้ำสองครั้ง
การคืนหน่วยความจำให้พอยน์เตอร์เดิมสองครั้งจะทำให้ข้อมูลการจัดการหน่วยความจำของตัวจัดสรรเสียหาย
memcheck จะแจ้ง การคืนหน่วยความจำที่ไม่ถูกต้อง และแสดงทั้งการคืนหน่วยความจำครั้งปัจจุบันกับการจัดสรรหน่วยความจำเดิม ทำให้เห็นข้อผิดพลาดได้อย่างชัดเจน
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int));
free(p);
free(p); /* freed twice */
return 0;
}ติดตามต้นกำเนิด
สำหรับข้อผิดพลาดจากค่าที่ยังไม่ได้กำหนดค่า ให้เพิ่ม --track-origins=yes
จากนั้น memcheck จะแจ้งว่าค่าที่ไม่ถูกต้องถูก สร้างขึ้น ที่ใด ไม่ใช่เพียงแค่ถูกใช้งานที่ใด ทำให้คำเตือนที่คลุมเครือกลายเป็นแนวทางแก้ไขที่ชัดเจน
valgrind --track-origins=yes ./progสแตกและตัวแปรโกลบอล
memcheck ทำงานได้ดีเยี่ยมกับข้อผิดพลาดในฮีป แต่การตรวจจับการเขียนเกินขอบเขตบนสแตกหรือในอาร์เรย์โกลบอลยังทำได้ไม่ดีเท่า
ในกรณีดังกล่าว AddressSanitizer (gcc -fsanitize=address) มักเป็นตัวเลือกที่ดีกว่า เครื่องมือทั้งสองช่วยเสริมการทำงานซึ่งกันและกัน
เกินหนึ่งตำแหน่ง: ตัวสิ้นสุดค่าว่าง
การเขียนเกินขอบเขตฮีปที่พบบ่อยคือการลืมตัวสิ้นสุดสตริง strlen จะคืนค่า 5 สำหรับ 'hello' แต่สตริงต้องใช้ 6 ไบต์เพื่อเก็บ '\0' ที่ต่อท้าย
memcheck จะแจ้งการเขียนที่ไม่ถูกต้อง เมื่อ strcpy เขียนไบต์สุดท้ายนั้นเลยจุดสิ้นสุดออกไป
#include <stdlib.h>
#include <string.h>
int main(void) {
char *s = malloc(strlen("hello")); /* needs +1 */
strcpy(s, "hello"); /* writes the '\0' past end */
free(s);
return 0;
}ตรวจสอบอย่างรวดเร็ว
ระบุสิ่งที่ memcheck แจ้งสำหรับโค้ดส่วนนี้
สรุปทบทวน
ขณะนี้คุณสามารถตรวจจับข้อผิดพลาดจากการเข้าถึงที่ไม่ถูกต้องได้แล้ว:
- การอ่านหรือเขียนนอกขอบเขตจะแสดงเป็นการอ่านหรือเขียนที่ไม่ถูกต้อง พร้อมออฟเซ็ตของบล็อก
- ค่าที่ยังไม่ได้กำหนดค่าจะถูกแจ้งเตือนเมื่อส่งผลต่อการตัดสินใจหรือเอาต์พุต
- ตรวจจับการใช้งานหลังคืนหน่วยความจำและการคืนหน่วยความจำซ้ำสองครั้ง พร้อมแสดงตำแหน่งการเรียกทั้งสองจุด
- ใช้
--track-origins=yesเพื่อระบุตำแหน่งของค่าที่ไม่กำหนด และใช้ร่วมกับ ASan สำหรับข้อผิดพลาดบนสแตก
ถัดไป: การอ่านรายงานฉบับเต็ม
คำถามที่พบบ่อย
บทเรียน “การเข้าถึงที่ไม่ถูกต้อง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การเข้าถึงที่ไม่ถูกต้อง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การเข้าถึงที่ไม่ถูกต้อง”
การเข้าถึงนอกขอบเขตและข้อมูลที่ยังไม่เริ่มต้น คุณปฏิบัติ C Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “การเข้าถึงที่ไม่ถูกต้อง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C Academy นี้ได้ไหม
ได้ บทเรียน C Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เหตุใดจึงใช้ Valgrind
- การตรวจจับหน่วยความจำรั่ว
- การเข้าถึงที่ไม่ถูกต้อง
- การอ่านรายงาน