พื้นฐานการใช้อาร์กิวเมนต์ (Arguments) ในภาษา C: วิธีส่งค่าและตัวอย่างการใช้งาน

目次

1. พื้นฐานของอาร์กิวเมนต์ในภาษา C

อาร์กิวเมนต์คืออะไร

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

อาร์กิวเมนต์จริงและอาร์กิวเมนต์จำลอง

ค่าที่ส่งจากฝั่งที่เรียกฟังก์ชันเรียกว่าอาร์กิวเมนต์จริง ส่วนค่าที่รับในฟังก์ชันเรียกว่าอาร์กิวเมนต์จำลอง เช่น ใน PrintScore(score); ค่าของ score คืออาร์กิวเมนต์จริง และใน void PrintScore(int score) ค่า score คืออาร์กิวเมนต์จำลอง การเข้าใจความแตกต่างของอาร์กิวเมนต์ทั้งสองชนิดนี้มีความสำคัญต่อการใช้ฟังก์ชันอย่างถูกต้อง

2. ความแตกต่างระหว่างอาร์กิวเมนต์จริงและอาร์กิวเมนต์จำลอง

อาร์กิวเมนต์จริง

อาร์กิวเมนต์จริงคือค่าที่ถูกส่งเข้าสู่ฟังก์ชันขณะเรียกใช้งาน เช่น PrintScore(100); ค่า 100 คืออาร์กิวเมนต์จริง อาร์กิวเมนต์จริงจะถูกนำไปใช้ภายในฟังก์ชัน

อาร์กิวเมนต์จำลอง

อาร์กิวเมนต์จำลองคือชื่อชั่วคราวสำหรับข้อมูลที่ฟังก์ชันรับเข้ามา อาร์กิวเมนต์จำลองจะอ้างอิงค่าของอาร์กิวเมนต์จริงภายในฟังก์ชัน แต่ไม่สามารถเปลี่ยนแปลงค่าจากภายนอกฟังก์ชันได้ ตัวอย่างเช่น ใน void PrintScore(int score) ค่าของ score คืออาร์กิวเมนต์จำลอง

侍エンジニア塾

3. วิธีการส่งอาร์กิวเมนต์เข้าสู่ฟังก์ชัน

การส่งค่าโดยตรง (Pass by Value)

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

void LevelUp(int lv) {
    lv++;
}

int main() {
    int level = 1;
    LevelUp(level);
    printf("Level: %dn", level); // ผลลัพธ์: Level: 1
}

ในตัวอย่างนี้ lv ในฟังก์ชัน LevelUp จะเพิ่มขึ้น แต่ค่า level ใน main จะไม่เปลี่ยนแปลง ข้อดีของการส่งค่าโดยตรงคือช่วยป้องกันข้อมูลของฝั่งที่เรียกใช้งาน แต่หากส่งข้อมูลขนาดใหญ่ อาจใช้หน่วยความจำมากขึ้น

การส่งด้วยตัวชี้ (Pass by Pointer)

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

void LevelUp(int *plv) {
    (*plv)++;
}

int main() {
    int level = 1;
    LevelUp(&level);
    printf("Level: %dn", level); // ผลลัพธ์: Level: 2
}

ในตัวอย่างนี้ ค่า level จะถูกเปลี่ยนแปลงโดยตรงจากในฟังก์ชัน LevelUp ข้อดีของการส่งด้วยตัวชี้คือสามารถเปลี่ยนแปลงค่าหลาย ๆ ค่าได้ แต่หากใช้งานตัวชี้ผิดพลาด อาจก่อให้เกิดบั๊กหรือปัญหาการจัดการหน่วยความจำ

4. รูปแบบของจำนวนอาร์กิวเมนต์และการคืนค่า

มีอาร์กิวเมนต์ ไม่มีค่าคืนกลับ

ตัวอย่างฟังก์ชันที่รับอาร์กิวเมนต์แต่ไม่คืนค่ากลับ เช่น void PrintScore(int score) ที่รับค่า score มาแสดงผลโดยไม่คืนค่ากลับ

ไม่มีอาร์กิวเมนต์ มีค่าคืนกลับ

ตัวอย่างฟังก์ชันที่ไม่รับอาร์กิวเมนต์แต่คืนค่ากลับ เช่น int GetCurrentScore() ซึ่งจะคำนวณและคืนค่าคะแนนปัจจุบัน

มีอาร์กิวเมนต์และค่าคืนกลับ

ตัวอย่างฟังก์ชันที่รับอาร์กิวเมนต์และคืนค่ากลับ เช่น int Add(int a, int b) ที่รับตัวเลข 2 ค่าและคืนค่าผลรวม ฟังก์ชันประเภทนี้มีความยืดหยุ่นสูงและใช้ได้ในหลากหลายสถานการณ์

5. การเรียกฟังก์ชันแบบเวียนกลับและอาร์กิวเมนต์

การเรียกฟังก์ชันแบบเวียนกลับคืออะไร

การเรียกฟังก์ชันแบบเวียนกลับ (Recursion) คือการที่ฟังก์ชันเรียกตัวเองเพื่อแก้ปัญหาที่สามารถแบ่งย่อยได้ มีประสิทธิภาพเมื่อจัดการปัญหาแบบซ้ำซ้อน แต่หากไม่ควบคุมให้ดีอาจทำให้เกิด stack overflow ได้

ตัวอย่างการเรียกฟังก์ชันแบบเวียนกลับ

ตัวอย่างต่อไปนี้จะแสดงการใช้ recursion เพื่อหารค่าด้วย 2 ไปเรื่อย ๆ

int funcA(int num) {
    if(num % 2 != 0) {
        return num;
    }
    return funcA(num / 2);
}

int main() {
    int result = funcA(20);
    printf("Result: %dn", result); // ผลลัพธ์: Result: 5
}

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

6. มาโครแบบฟังก์ชันและอาร์กิวเมนต์

มาโครแบบฟังก์ชันคืออะไร

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

ตัวอย่างมาโครแบบฟังก์ชัน

ตัวอย่างต่อไปนี้เป็นมาโครสำหรับนับจำนวนสมาชิกในอาเรย์

#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))

int main() {
    int arr[10];
    printf("Array size: %dn", ARRAY_SIZE(arr)); // ผลลัพธ์: Array size: 10
}

มาโครแบบฟังก์ชันจะถูกแทนที่ก่อนคอมไพล์ จึงไม่มี overhead ระหว่างรันไทม์ และสามารถใช้กับชนิดข้อมูลใดก็ได้ แต่ต้องใช้อย่างระมัดระวังเพราะไม่มี type check

7. ฟังก์ชันมาตรฐานในภาษา C และอาร์กิวเมนต์

การใช้ฟังก์ชันมาตรฐาน

ภาษา C มีฟังก์ชันมาตรฐานมากมายที่สามารถใช้อาร์กิวเมนต์ในการประมวลผล เช่น ฟังก์ชัน printf ที่รับอาร์กิวเมนต์ได้หลายค่าเพื่อแสดงผลข้อมูลในรูปแบบที่ต้องการ

ตัวอย่างฟังก์ชันมาตรฐาน

ตัวอย่างต่อไปนี้จะแสดงการใช้ printf กับอาร์กิวเมนต์

printf("Name: %s, Age: %dn", "Alice", 30); // ผลลัพธ์: Name: Alice, Age: 30

ในตัวอย่างนี้ ฟังก์ชัน printf ใช้อาร์กิวเมนต์ทั้งสตริงและตัวเลขเพื่อแสดงผล ข้อดีของการใช้ฟังก์ชันมาตรฐานคือช่วยให้โค้ดอ่านง่ายและมีประสิทธิภาพ

8. สรุป

การใช้อาร์กิวเมนต์แบบจำนวนไม่แน่นอน (Variadic Arguments)

ภาษา C รองรับฟังก์ชันที่รับอาร์กิวเมนต์จำนวนไม่แน่นอน โดยใช้สัญลักษณ์ ... ในการกำหนด สามารถสร้างฟังก์ชันที่รับอาร์กิวเมนต์จำนวนไม่จำกัดได้ เช่น printf ที่รับอาร์กิวเมนต์ตามรูปแบบที่กำหนด

ตัวอย่างการใช้อาร์กิวเมนต์แบบจำนวนไม่แน่นอน

ตัวอย่างต่อไปนี้คือฟังก์ชันที่รับตัวเลขหลายตัวและคำนวณผลรวม

#include <stdarg.h>
#include <stdio.h>

int sum(int count, ...) {
    va_list args;
    va_start(args, count);
    int total = 0;

    for (int i = 0; i < count; i++) {
        total += va_arg(args, int);
    }

    va_end(args);
    return total;
}

int main() {
    printf("Sum: %dn", sum(4, 1, 2, 3, 4)); // ผลลัพธ์: Sum: 10
}

ในตัวอย่างนี้ ฟังก์ชัน sum จะรับอาร์กิวเมนต์จำนวนไม่จำกัด และใช้ va_list, va_start, va_arg, va_end เพื่อจัดการอาร์กิวเมนต์เหล่านั้น

ข้อควรระวัง

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

กรณีการใช้งานจริงและการใช้ประโยชน์จากอาร์กิวเมนต์

การใช้อาร์กิวเมนต์อย่างมีประสิทธิภาพ

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

ประสิทธิภาพหน่วยความจำและการทำงาน

กรณีส่งข้อมูลขนาดใหญ่เป็นอาร์กิวเมนต์ ควรใช้ pointer เพื่อลดการใช้หน่วยความจำ เช่น เมื่อส่ง array หรือ struct ขนาดใหญ่ หากใช้การส่งค่าโดยตรงจะมีการคัดลอกข้อมูลทั้งหมด แต่หากใช้ pointer จะส่งเฉพาะ address ช่วยประหยัดหน่วยความจำ

แนวทางปฏิบัติที่ดีในการเขียนโค้ด

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

9. เทคนิคขั้นสูงเกี่ยวกับอาร์กิวเมนต์

ฟังก์ชันคอลแบ็ก (Callback Function)

Callback Function คือการส่งฟังก์ชันเป็นอาร์กิวเมนต์เข้าไปในฟังก์ชันอื่นเพื่อให้เรียกใช้ในภายหลัง นิยมใช้ในโปรแกรมแบบ event-driven หรือ asynchronous เพื่อเพิ่มความยืดหยุ่นในการประมวลผล

#include <stdio.h>

void executeCallback(void (*callback)(int)) {
    callback(10);
}

void printValue(int val) {
    printf("Value: %dn", val);
}

int main() {
    executeCallback(printValue); // ผลลัพธ์: Value: 10
}

ในตัวอย่างนี้ ฟังก์ชัน printValue ถูกส่งเป็น callback เข้าไปและถูกเรียกใน executeCallback

พอยน์เตอร์ของฟังก์ชัน (Function Pointer)

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

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int main() {
    int (*operation)(int, int) = add;
    printf("Result: %dn", operation(2, 3)); // ผลลัพธ์: Result: 5
}

ในตัวอย่างนี้ add ถูกเก็บใน function pointer operation และสามารถเรียกใช้งานผ่านตัวแปรนี้ได้

10. อาร์กิวเมนต์ของฟังก์ชันกับการจัดการหน่วยความจำ

หน่วยความจำแบบไดนามิกและอาร์กิวเมนต์

ภาษา C สามารถจัดสรรหน่วยความจำแบบไดนามิกด้วย malloc และ free การส่ง pointer ของหน่วยความจำที่จัดสรรแบบไดนามิกเป็นอาร์กิวเมนต์ต้องจัดการหน่วยความจำอย่างรอบคอบ

#include <stdlib.h>
#include <stdio.h>

void allocateMemory(int **ptr, int size) {
    *ptr = (int *)malloc(size * sizeof(int));
}

int main() {
    int *arr;
    allocateMemory(&arr, 5);
    for (int i = 0; i < 5; i++) {
        arr[i] = i + 1;
    }
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]); // ผลลัพธ์: 1 2 3 4 5
    }
    free(arr); // ปล่อยหน่วยความจำ
}

ในตัวอย่างนี้ ฟังก์ชัน allocateMemory จะจัดสรรหน่วยความจำให้ pointer และส่งต่อเป็นอาร์กิวเมนต์ หากไม่จัดการหน่วยความจำให้ดี อาจเกิด memory leak ได้