String Termination — ทำไม VARCHAR ถึงมี
แนวคิดหลัก
Section titled “แนวคิดหลัก”String ใน C ไม่รู้ความยาวตัวเอง — มันรู้แค่ว่า “จบที่ \0” นี่คือปัญหาพื้นฐานที่ทำให้ SQL ต้องสร้าง VARCHAR ขึ้นมา
Sheets จัดการให้หมด:
=LEN("HI") → 2=LEN("HELLO") → 5คุณไม่ต้องคิดเรื่อง byte, null terminator หรือ allocation เลย
sizeof vs strlen — ความแตกต่างที่สำคัญ
Section titled “sizeof vs strlen — ความแตกต่างที่สำคัญ”#include <stdio.h>#include <string.h>
int main() { char s1[] = "HI"; char s2[] = "HELLO"; char s3[] = ""; // empty string ก็มี \0
printf("=== sizeof vs strlen ===\n"); printf("\"HI\": sizeof=%zu strlen=%zu\n", sizeof(s1), strlen(s1)); printf("\"HELLO\": sizeof=%zu strlen=%zu\n", sizeof(s2), strlen(s2)); printf("\"\": sizeof=%zu strlen=%zu\n", sizeof(s3), strlen(s3));
// แสดง bytes ของ "HI" printf("\n=== bytes ใน \"HI\" ===\n"); for (int i = 0; i < (int)sizeof(s1); i++) { printf("byte[%d] = %3d (0x%02x) '%c'\n", i, (unsigned char)s1[i], (unsigned char)s1[i], s1[i] ? s1[i] : '.'); }
return 0;}ผลลัพธ์:
=== sizeof vs strlen ==="HI": sizeof=3 strlen=2"HELLO": sizeof=6 strlen=5"": sizeof=1 strlen=0
=== bytes ใน "HI" ===byte[0] = 72 (0x48) 'H'byte[1] = 73 (0x49) 'I'byte[2] = 0 (0x00) '.'strlen ทำงานอย่างไร
Section titled “strlen ทำงานอย่างไร”#include <stdio.h>
// strlen ทำงานแบบนี้ข้างในint my_strlen(char *s) { int count = 0; while (*s != '\0') { // วนจนเจอ \0 count++; s++; } return count;}
int main() { char word[] = "DATA";
printf("my_strlen(\"DATA\") = %d\n", my_strlen(word)); // 4 printf("strlen(\"DATA\") = %zu\n", strlen(word)); // 4 (ใช้ string.h)
// ทดลอง: ถ้าใส่ \0 ตรงกลาง char tricky[] = "ABCDE"; tricky[2] = '\0'; // ตัด string ตรงกลาง!
printf("\nหลังใส่ \\0 ตรงกลาง:\n"); printf("printf: \"%s\"\n", tricky); // "AB" printf("strlen: %zu\n", strlen(tricky)); // 2 (หยุดที่ \0) printf("sizeof: %zu\n", sizeof(tricky)); // 6 (ขนาดจริงไม่เปลี่ยน)
return 0;}ทำไม VARCHAR ถึงเกิดขึ้น
Section titled “ทำไม VARCHAR ถึงเกิดขึ้น”#include <stdio.h>#include <string.h>
int main() { // CHAR(10) — จองที่ 10 ตัวเสมอ (เปลืองถ้าข้อมูลสั้น) char fixed[11] = "HI"; // 11 = 10 chars + \0 printf("CHAR(10): ใช้ %zu bytes เก็บ \"%s\" (strlen=%zu)\n", sizeof(fixed), fixed, strlen(fixed));
// VARCHAR — เก็บแค่ที่ใช้ + ตัวบอกความยาว // ใน C เราจำลองด้วย pointer char *dynamic = "HI"; printf("VARCHAR: ใช้ %zu bytes เก็บ \"%s\" (strlen=%zu)\n", strlen(dynamic) + 1, dynamic, strlen(dynamic));
printf("\nCHAR(10) เสีย: %zu bytes ว่างเปล่า\n", sizeof(fixed) - strlen(fixed) - 1);
return 0;}Python ไม่มี null terminator — มัน track ความยาวแยกต่างหาก:
s = "HI"print(len(s)) # 2 (ไม่มี +1 เหมือน C)
# Python string เป็น immutable — ไม่มีปัญหา buffer overflow# ขนาดจริงใหญ่กว่า C มาก เพราะ object overheadimport sysprint(sys.getsizeof(s)) # ~52 bytes (object header + length + chars)-- CHAR(10) = fixed size (เหมือน char[11] ใน C)-- VARCHAR(100) = variable size (เก็บ length + data)
CREATE TABLE comparison ( fixed_col CHAR(10), -- ใช้ 10 bytes เสมอ var_col VARCHAR(100) -- ใช้ bytes ตามข้อมูลจริง + 1-2 bytes สำหรับ length);
INSERT INTO comparison VALUES ('HI', 'HI');-- fixed_col ใช้ 10 bytes → 'HI ' (padded)-- var_col ใช้ 4 bytes → 2 (length) + 'HI'| คำถาม | C | SQL | Python |
|---|---|---|---|
| string จบที่ไหน? | \0 null terminator | length field (VARCHAR) หรือ padding (CHAR) | length attribute |
| ขนาดจริง? | sizeof | storage engine จัดการ | sys.getsizeof |
| ความยาวข้อความ? | strlen (นับถึง \0) | LENGTH() / CHAR_LENGTH() | len() |