Skip to content

Latest commit

 

History

History
236 lines (185 loc) · 14.5 KB

File metadata and controls

236 lines (185 loc) · 14.5 KB

ฟังก์ชัน (Function)

  • เหมือนกันทั้ง C และ C++

การเรียกใช้ฟังก์ชัน

  • ฟังก์ชันต่างๆ ที่ภาษา C และ C++ กำหนดให้ มีรูปแบบการใช้งานในรูป functionName(arg1, arg2, ...)
  • เมื่อโค้ดอ่านถึงส่วนที่เรียกใช้ฟังก์ชัน โปรแกรมจะ
    • จำสถานะของฟังก์ชันเดิมที่เรียกใช้ (เช่น main)
    • คัดลอกค่าจาก argument ไปให้ฟังก์ชัน (ถ้ามี)
    • ฟังก์ชันจะทำงาน หากฟังก์ชันส่งค่ากลับมา ค่าจะมาอยู่แทนที่ตำแหน่งที่เรียกฟังก์ชัน
  • ฟังก์ชันที่ถูกเรียก จะไม่ทราบ local variable ของฟังก์ชันที่เรียกใช้ (ต่อเรื่อง scope)

ตัวอย่าง

int a = pow(2, 3);
// pow(2, 3) เมื่อคำนวณเสร็จแล้ว จะทำให้โค้ดมีลักษณะคล้าย int a = 8;

printf("%d", a);
// printf ไม่ส่งค่าใดๆ ที่เราจำเป็นต้องใช้กลับมา ดังนั้นจึงเรียกโดดๆได้เลย

printf("%d", pow(2, 3));
// pow(2, 3) อาจมาแทนที่เป็น argument ของฟังก์ชันอื่นอีกได้เลย
// ได้เป็น printf("%d", 8);

getch();
// บางฟังก์ชันไม่จำเป็นต้องใช้ argument

การกำหนดฟังก์ชัน

  • เราสามารถสร้างฟังก์ชันขึ้นได้เอง โดยโปรแกรมที่มีการประกาศฟังก์ชัน จะมีลักษณะดังนี้
#include <stdio.h>

int sum(int x, int y) // ฟังก์ชันที่ผู้ใช้สร้างขึ้น
{
    int s = x + y;
    return s;
}

int main(void)
{
    int a, b;
    scanf("%d", &a);
    scanf("%d", &b);
    printf("%d", sum(a, b));
    // sum(a, b) จะถูกแทนที่ด้วยค่าของ a + b
}
  • สังเกตว่ารูปแบบการประกาศฟังก์ชันจะเป็นดังนี้
returnType functionName(argType1 argName1, argType2 argName2, ...)
{
    // statements
    return something; // ถ้า returnType เป็น void ไม่ต้องมีบรรทัดนี้
}
  • returnType คือ ชนิดของตัวแปรที่ต้องการคืนค่ากลับมา หากไม่ต้องการคืน ให้ใส่ void
  • functionName คือ ชื่อของฟังก์ชันที่ต้องการ ฟังก์ชันอื่นจะเรียกฟังก์ชันนี้ผ่านชื่อที่ระบุตรงนี้
  • argType คือ ชนิดของ argument/parameter ที่ต้องการรับ
  • argName คือ ชื่อของ parameter ที่ตัวฟังก์ชันจะใช้เรียก
    • สังเกตว่าชื่อตัวแปรตอนเรียกฟังก์ชันไม่ต้องตรงกับชื่อตัวแปรในนิยาม เช่น sum(a, b) แต่ฟังก์ชันประกาศเป็น int sum(int x, int y)
    • เพราะฟังก์ชันจะคัดลอกค่าจากตัวแปรแต่ละตัว ไปใส่ในตัวแปรที่เราประกาศตรงหัวฟังก์ชัน
  • หากฟังก์ชันมีการส่งค่ากลับมา ควรเรียกใช้ฟังก์ชันตามหัวข้อข้างบน
  • การประกาศฟังก์ชัน จะมีผลให้ฟังก์ชันที่อยู่ข้างล่างเรียกใช้ได้เท่านั้น ส่วนข้างบนจะใช้ไม่ได้
    • ต้องประกาศฟังก์ชันก่อนฟังก์ชันที่เรียกใช้ (เช่น main)

Prototype

  • Prototype เป็นการประกาศฟังก์ชันที่ระบุเพียงแค่ชื่อฟังก์ชัน argument และ return type เท่านั้น
  • มีไว้เพื่อให้ฟังก์ชันอื่นๆ ที่ต้องการเรียกใช้ รับทราบว่าฟังก์ชันนี้มีอยู่จริง
  • ส่วนรายละเอียดการทำงานของฟังก์ชัน สามารถระบุไว้ได้ที่อื่น

การประกาศ Prototype

  • ทำได้หลายแบบ แต่วิธีที่ขอกล่าว ณ ที่นี้คือการคัดลอกหัวฟังก์ชันมา เพื่อจะได้ไม่งง
  • จากตัวอย่างข้างบน เราสามารถย้าย sum ลงมาข้างล่าง main แล้วประกาศ prototype ของ sum ข้างบนได้ดังนี้
#include <stdio.h>

int sum(int x, int y); // prototype ที่ประกาศโดยคัดลอกหัวฟังก์ชันมา

int main(void)
{
    int a, b;
    scanf("%d", &a);
    scanf("%d", &b);
    printf("%d", sum(a, b));
    // sum(a, b) จะถูกแทนที่ด้วยค่าของ a + b
}

int sum(int x, int y) // หัวฟังก์ชันที่ประกาศ
{
    int s = x + y;
    return s;
}

Pass by Value

  • ดังที่ระบุไว้ในหัวข้อการเรียกใช้ฟังก์ชัน การเรียกฟังก์ชันนั้นจะคัดลอกค่ามาใส่ในตัวแปร parameter
  • ดังนั้น การแก้ไขตัวแปรใดๆ ภายในฟังก์ชันจะไม่ส่งผลต่อค่าเดิมข้างนอกฟังก์ชัน

ตัวอย่าง

#include <stdio.h>

void swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
    // here, a = 5, a = 3
}

int main(void)
{
    int a = 3;
    int b = 5;
    swap(a, b); // but after this, a = 3, b = 5
    printf("a is %d. b is %d.", a, b);
}

// Output:
// a is 3. b is 5.
  • สังเกตว่าฟังก์ชันตัวอย่าง ไม่ได้สลับค่าตัวแปร a กับ b ของ main แต่อย่างใด เพราะ swap สลับแค่ตัวแปรภายในฟังก์ชันตัวเองเท่านั้น
  • ทั้งนี้ทั้งนั้น ลักษณะการเรียกฟังก์ชันแบบ Pass by Value จึงสามารถอธิบายได้ว่าทำไมเราถึงเรียกฟังก์ชันด้วยค่าคงที่ได้
    • ดังตัวอย่างแรกสุด pow(2, 3) สังเกตว่า 2 กับ 3 ไม่ได้อยู่ในตัวแปรใดๆ ทั้งสิ้น

Pass by Reference (Address)

  • หากเราต้องการให้ฟังก์ชันแก้ไขตัวแปรของเราได้ ฟังก์ชันจะต้องรับ ตำแหน่งที่อยู่ตัวแปร แทนค่าของตัวแปร ซึ่งก็คือ pointer

  • เมื่อฟังก์ชันทราบตำแหน่งที่อยู่ตัวแปร ฟังก์ชันสามารถเข้าถึงค่าที่ตำแหน่งนั้น และแก้ไขได้อย่างถูกต้อง

  • การประกาศตัวแปร pointer สามารถทำได้โดยเติมเครื่องหมาย asterisk * หลังชนิดตัวแปร

  • หากต้องการทราบตำแหน่งของตัวแปรใดๆ ให้ใส่เครื่องหมาย ampersand & นำหน้าตัวแปรนั้นๆ

  • หากต้องการเข้าถึงตัวแปรผ่าน pointer ให้ใส่เครื่องหมาย asterisk * นำหน้าชื่อตัวแปร pointer

#include <stdio.h>

// swap รับ int pointer 2 ตัว โดยเรียกว่า a และ b
void swap(int *a, int *b)
{
    int temp = *a;  // นำค่าที่ a ชี้อยู่ ใส่ลงไปใน temp
    *a = *b;        // นำค่าที่ b ชี้อยู่ ใส่ลงไปในที่ที่ a ชี้เลย
    *b = temp;      // นำค่าของ temp ใส่ลงไปที่ที่ b ชี้
}

int main(void)
{
    int a = 3;
    int b = 5;
    swap(&a, &b);   // ส่งตำแหน่งที่อยู่ของ a และ b ไปให้ swap ทำให้ swap สามารถแก้ไขค่าได้
    printf("a is %d. b is %d.", a, b);
}

// Output:
// a is 5. b is 3.

Pass by Reference (C++)

  • ส่วนนี้ใช้ได้กับภาษา C++ เท่านั้น
  • นอกจาก pointer ในภาษา C ภาษา C++ ยังมีสิ่งที่เรียกว่า reference ซึ่งทำงานคล้ายคลึงกับ pointer
  • การประกาศให้ฟังก์ชันรับตัวแปร reference สามารถทำได้โดยเติมเครื่องหมาย ampersand & หลังชนิดตัวแปร
  • ตัวแปร reference จะจัดการเรื่องการเก็บที่อยู่ และการเข้าถึงค่าให้อัตโนมัติ สามารถใช้เหมือนตัวแปรทั่วไปได้เลย
    • ไม่ต้องเติม & ข้างหน้าชื่อตัวแปรเพื่อส่งตำแหน่งตัวแปรเวลาเรียกใช้ฟังก์ชัน
    • ไม่ต้องเติม * ข้างหน้าชื่อตัวแปร reference เพื่อเข้าถึงค่าของตัวแปรนั้นๆ
#include <iostream>
using namespace std;

// swap รับ int reference 2 ตัว โดยเรียกว่า a และ b
void swap(int &a, int &b)
{
    int temp = a;   // สามารถเรียกใช้แบบตัวแปรทั่วไปได้เลยโดยไม่ต้องสนว่า a เป็น reference
    a = b;          // แต่เมื่อมีการเปลี่ยนแปลงค่า a และ b ในนี้ ค่าที่ reference ชี้อยู่ (และจัดการให้อัตโนมัติ) ก็จะเปลี่ยนแปลงตาม
    b = temp;
}

int main()
{
    int a = 3;
    int b = 5;
    swap(a, b); // ระบบจะจัดการส่งที่อยู่ของ a และ b ใปให้โดยอัตโนมัติ
    cout << "a is " << a << ". b is " << b << ".";
}

// Output:
// a is 5. b is 3.
  • ฟังก์ชัน swap แบบ reference ที่ยกมานี้ มีอยู่แล้วใน #include <algorithm> ซึ่งสามารถใช้ได้กับตัวแปรทุกชนิด

ฟังก์ชันที่ใช้ array

  • เหมือนกันทั้ง C และ C++
  • หาก array ที่รับไม่มีขนาดที่แน่นอน ควรจะรับขนาดของ array มาด้วย เพื่อการ loop
#include <stdio.h>

void printStudents(int studentID[], int n)
{
    for (int i = 0; i < n; i++)
        printf("Student #%d: ID %d\n", i + 1, studentID[i]);
}

int main()
{
    int students[] = { 1652, 5830, 2601, 3524 };
    printStudents(students, 4);
}

// Output:
// Student #1: ID 1652
// Student #2: ID 5830
// Student #3: ID 2601
// Student #4: ID 3524
  • หากต้องการส่งค่ากลับเป็น array ควรให้ฟังก์ชันที่เรียกใช้ เตรียม array ไว้ แล้วเราจึงแก้ไข array นั้น
    • เช่น scanf กับ string จะสังเกตได้ว่า เราต้องประกาศตัวแปร char [] ก่อน แล้ว scanf จึงจะทำหน้าที่เปลี่ยนข้อความของเราให้
  • เนื่องจาก array ที่ส่งมามีลักษณะเป็น pointer จึงสามารถแก้ไขสมาชิกแต่ละตัวได้โดยตรง เหมือน Pass by Reference
    • แต่ไม่สามารถกำหนดให้เป็น array ชุดใหม่ได้
  • อีกวิธีหนึ่ง คือการสร้าง array ด้วยการ malloc แล้วส่ง pointer กลับมา (ผู้ใช้มีหน้าที่ free) ซึ่งจะได้เรียนในเรื่อง pointer