วันนี้เป็นอีกวันที่เขียน C อย่างเมามัน และไปงงกับ Error แปลกๆอยู่หลายชั่วโมง เหตุเพราะอยากได้ข้อมูลที่มีพื้นที่ของหน่วยความจำที่ต่อเนื่องกัน ในตอนแรกจึงเลือกใช้ static array สำหรับเก็บค่า ซึ่งตอนหลังก็พบว่าไม่จำเป็นต้องใช้
ลองมาดูตัวอย่างกันดีกว่า
มี function หนึ่ง return ค่า string หรือถ้าเรียกให้ถูกคือ address ของหน่วยความจำที่เก็บ string แต่ค่าที่ได้มันกลายเป็นภาษาขยะ ยกตัวอย่างง่ายๆจาก code ด้านล่าง ซึ่งจะ print ตัวอักษร A สิบตัวอักษร
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *
get_var()
{
char x[11];
memset(x,65 /*A*/,10);
x[10] = '\0';
printf("get_var()\n");
printf("%s\n", x);
return x;
}
int
main()
{
char * y;
y = get_var();
printf("%s\n", y);
return 0;
}
หลังจาก compile และ run ได้ค่าแบบนี้
$ gcc x.c -o x
$ ./x
get_var()
AAAAAAAAAA
��Y��do
เมื่อ print ตัวแปร x ใน function get_var() ออกมา มันก็ออกมาอย่างที่ควรจะเป็น แต่พอ ค่าของตัวแปร y ที่อยู่ใน main() ซึ่งรับมาจาก get_var() อีกทีมันดันออกมาเป็นขยะ
คราวนี้ลองมาดูของที่ถูกดูบ้าง
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *
get_var()
{
char * x;
x = (char *) malloc(11);
memset(x,65 /*A*/,10);
x[10] = '\0';
printf("get_var()\n");
printf("%s\n", x);
return x;
}
int
main()
{
char * y;
y = get_var();
printf("%s\n", y);
free(y);
return 0;
}
$ gcc x.c -o x
$ ./x
get_var()
AAAAAAAAAA
AAAAAAAAAA
คราวนี้ค่าที่ได้ออกมาถูกต้อง
ในตัวอย่างแรกเวลา compile จะมี warning แบบนี้
x.c:19: warning: function returns address of local variable
ซึ่งถ้าเราไม่ทันสังเกตุ หรือไม่สนใจว่ามันคืออะไร มันก็จะเกิดปัญหาแบบที่ผมเป็น
อธิบายง่ายๆว่า การประกาศตัวแปร char x[11]; ในตัวอย่างแรกนั้น address ของ x จะถูกเก็บอยู่ใน stack ของ get_var() ซึ่งไม่สามารถอ้างถึงได้จาก function อื่น พอ function get_var() return ค่าข้อมูลใน stack ของ get_var() ก็หายหมด ทำให้ค่า address ของหน่วนคว่มจำที่ใน function main() รับมาไม่มีอยู่จริง pointer ก็ไปชี้อะไรมั่วซั่ว เวลา print ออกมาดูทำให้ได้ขยะมาอย่างที่เห็น
วิธีแก้ก็ง่ายๆคือ ใช้ตัวแปรที่เก็บใน Heap
char * x;
x = (char *) malloc(11);
เมื่อ get_var() return address ของหน่วยความจำออกมา function อื่นก็ยังสามารถอ้างถึงได้ แต่เมื่อใช้เสร็จต้องคืน memory โดยใช้คำสั่ง free() ด้วย
char * y;
y = get_var();
y ควรจะจอง heap ไว้รับค่าเช่นกัน ไม่งั้น ค่าที่รับได้อาจจะไปเรียงทับข้อมูลชุดอื่นๆในหน่วยความจำได้