C Programming : เขียนโปรแกรมภาษา C แบบพื้นฐาน ตอนที่ 5 – Function
สวัสดีครับ บทความนี้มาต่อกันเรื่องใหม่ เรื่อง Function (ฟังก์ชัน) ที่เป็นพื้นฐานเหมือนกัน แต่ผมว่ามันเข้าใจง่ายกว่า pointer นะ
แต่ว่าที่ผมพูดถึงทีหลัง pointer เพราะว่า ผมจะเอา pointer มาใช้กับ ฟังก์ชัน ด้วย มาลุยกันต่อเลย
รู้จักกับ Function
มันคือ การแยกส่วนโค้ด ให้ทำงานออกมาเป็นส่วนๆ กล่าวคือการเขียนโปรแกรมที่ดี มันต้องแยกการทำงานออกจากัน ไม่ใช่ทำอะไรก็ไม่รู้อยู่ที่เดียว พอแยกแล้ว ผลคือ เข้าใจง่ายขึ้น แล้วก็ทำงานร่วมกับคนอื่นง่ายขึ้นนั่นเอง
แล้วรู้มัยว่า เราใช้ ฟังก์ชันมาก่อนแบบไม่รู้ตัวแล้ว มันคือ คำสั่งต่างๆ จริงๆมันก็คือ ฟังก์ชันอย่างนึง ซึ่งเราตะรู้แค่ว่ามันชื่ออะไร ใส่อะไรให้มันบ้าง แล้วมันให้อะไรเราออกมา แค่นี้แหละที่เราต้องเข้าใจเกี่ยวกับ ฟังก์ชัน
ทีนี้เราจะมาลองทำ ฟังก์ชันของเราเองบ้าง ก่อนอื่นมาดูโปรแกรมตัวอย่างนี้ก่อน
มันคือโปรแกรมที่บอกว่าเป็นเลขคู่หรือเลขคี่ เขียนง่ายๆแบบนี้
#include<stdio.h> main(){ int a = 10; int b = 13; int c = 8; if(a%2==0){ printf("\n a is Even"); }else{ printf("\n a is Odd"); } if(b%2==0){ printf("\n b is Even"); }else{ printf("\n b is Odd"); } if(c%2==0){ printf("\n c is Even"); }else{ printf("\n c is Odd"); } }
ทีนี้ผมลองเขียนฟังก์ชันขึ้นมา อันนึงชื่อว่า checkEvenOod เอาไว้ทำงานเกี่ยวกับการตรวจสอบ เลขคู่เลข คี่โดยเฉพาะ ใครอยากรู้ว่าเลขนี้เป็นเลขอะไร ก็ส่งเลขมาที่ฟังก์ชันนี้ได้เลย
#include<stdio.h> void checkEvenOod(char name,int value){ if(value%2==0){ printf("\n %c is Even",name); }else{ printf("\n %c is Odd",name); } } main(){ int a = 10; int b = 13; int c = 8; checkEvenOod( 'a', a); checkEvenOod( 'b', b); checkEvenOod( 'c', c); }
สังเกตว่า ชื่อ checkEvenOod มันจะเขียนเป็นแบบตัวแรกตัวเล็ก แต่คำถัดไปจะขึ้นต้นด้วยตัวใหญ่ อันนี้เป็นการตั้งชื่อฟังก์ชันที่นิยมกัน เรียกว่า Camel Case
ที่บรรทัด checkEvenOod ของผมนั้น สังเกตุด้านหน้ามีคำว่า void อยู่ซึ่งหมายถึง ฟังก์ชันนี้ไม่มีอะไรคืนค่ามา ส่วนด้านหลัง
จะเป็น argument (สิ่งที่ฟังก์ชันต้องการ) ในที่นี้คือ checkEvenOod ต้องการ name กับ value
เสร็จแล้วผมก็ย้ายการทำงานเกี่ยวกับการเช็คเลขคู่เลขคี่ มาไว้ในฟังก์ชัน
ผลคือ เกิดการแยกส่วนของโค้ดขึ้น อ่านง่ายขึ้น
ถ้ามีข้อมูลจำนวนมาก ก็สามารถนำ loop มาใช้ได้เหมือนเดิม
#include<stdio.h> void checkEvenOod(int i,int value){ if(value%2==0){ printf("\n data[%d] is Even",i); }else{ printf("\n data[%d] is Odd",i); } } main(){ int a[] = {10,11,12,13,14,15,16,17,18,19}; int i=0; for(i=0;i<10;i++){ checkEvenOod( i, a[i]); } }
ลองเขียนฟังก์ชันแบบมีการคืนค่า
ทีนี้ลองเอาฟังก์ชันมาปรับให้มีการ return หรือคืนค่ากลับไปให้กับคนที่เรียกมัน ซึ่งผมจะเขียนฟังก์ชันใหม่ คือเช็คเลขคู่
ถ้าใช่ก็ return 1 ไม่ใช่ก็เป็น 0 แล้วเอาค่าที่ return นี้ไปปริ้น
#include<stdio.h> int isEvenNumber(int value){ if(value%2==0){ return 1; }else{ return 0; } } main(){ int data[] = {10,11,12,13,14,15,16,17,18,19}; int i=0; for(i=0;i<10;i++){ int isEven = isEvenNumber(data[i]); printf("\n data[%d] = %d",i,isEven); } }
อ่านถึงตรงนี้ ผมคิดว่าฟังก์ชันน่าจะไม่ใช่เรื่องยากอะไรแล้วละ เพราะมันก็แค่เหมือนการแยกโค้ดออกมาทำงาน ทีนี้มาลองดูเรื่องอื่นๆที่ควรรู้อีกสักหน่อยนะ
Pass by Value -VS- Pass by reference
ก่อนอื่นต้องรู้ด้วยว่า การส่งค่าเข้าไปใน function โดยปกติแล้ว ค่าที่ส่งมาทาง argument มันจะส่งไปก็จริง แต่มันไม่ใช่ตัวแปรเดียวกัน แม้ว่าเราจะเปลี่ยนค่าของมันในฟังก์ชัน แต่ตัวแปรจริงๆมันไม่ได้เปลี่ยนค่าตามไปด้วยนะ
พูดง่ายๆ คือ ส่งค่าเข้ามาในฟังก์ชัน โปรแกรมมันประกาศตัวแปรใหม่ แล้วก็ copy ค่ากำหนดให้กับมันเฉยๆเท่านั้นเอง
นี่คือ สิ่งที่เรียกว่า Pass by Value
#include<stdio.h> int helloData(int data){ data =999; } main(){ int data = 100; helloData(data); printf("data = %d",data); }
ส่วนอีกแบบที่ต่างออกไปคือ เปลี่ยนค่าในฟังก์ชันแล้วตัวแปรจริงๆก็เปลี่ยนค่าตาม มันคือการใช้ pointer นั่นเอง
เพราะ pointer มันก็ร่างเงา สามารถแก้ไขค่าของตัวแปรที่มันชี้อยู่ได้โดยใช้ร่างเงา นี่คือ Pass by Reference
#include<stdio.h> int helloData(int *ptr){ *ptr =999; } main(){ int data = 100; helloData(&data); printf("data = %d",data); }
สังเกตบรรทัดที่ 9 ผมส่ง &data ไป มันคือ address ของตัวแปร data
แล้วบรรทัดที่ 3 ซึ่งเป็น argument ของ ฟังก์ชัน มันรับค่าตัวแปรมาเป็น pointer ชื่อว่า ptr
บรรทัดที่ 4 ผมใช้ร่างเงา เปลี่ยนค่า ซึ่งร่างจริงมันคือตัวแปร data
หลังจากทำงานในฟังกชันเสร็จ ก็มาปริ้นค่าในบรรทัด 12 ปรากฏว่าค่าเปลี่ยนไป
และนี่คือเหตุผลว่า ทำไมในตอนก่อนผมถึงพูดเรื่อง pointer เพราะจะได้นำมาใช้กับเรื่องนี้นั่นเอง
Array ใน function
ทีนี้มาดูการใช้ array กับฟังก์ชัน ผมส่ง array เข้ามาในฟังก์ชัน แล้วเปลี่ยนค่าใน array ซะ
จากนั้นก็ไปปริ้นค่า array ที่ main แบบไม่มีการ return นะ
#include<stdio.h> void helloArray(int data[]){ data[0] = 111; data[1] = 111; data[2] = 111; data[3] = 111; data[4] = 111; } main(){ int data[] = {6,7,8,9,10,11}; helloArray(data); int i=0; for(i=0;i<5;i++){ printf("\n data[%d] = %d",i,data[i]); } }
คิดว่า ค่าของ array จะเหมือนเดิมหรือจะเปลี่ยนไป ?
นี่คือการ Pass by Value หรือ Pass by Reference กันแน่
มาดูคำตอบ
มันคือ Pass by Reference !!
เป็นไปได้ไง ไม่เห็นมี pointer เลยนะ ร่างเงาก็ไม่ได้ใช้สักหน่อยเลย
เพราะว่า อย่างที่ผมบอกไปในบทความที่แล้ว ว่า array มันคือ pointer
เบื้องหลังมันใช้ pointer ในการจัดการ
ทำให้ตอนเราส่ง array ไปในฟังก์ชัน จริงๆมันส่ง pointer ไปแทน
ดังนั้น ตอนเราส่งค่าไป ใช้ &data[0] แบบนี้ก็ได้เหมือนกัน
#include<stdio.h> void helloArray(int data[]){ data[0] = 111; data[1] = 111; data[2] = 111; data[3] = 111; data[4] = 111; } main(){ int data[] = {6,7,8,9,10,11}; helloArray(&data[0]); int i=0; for(i=0;i<5;i++){ printf("\n data[%d] = %d",i,data[i]); } }
หรือว่าจะเขียนแบบรับเป็น pointer ตรงๆก็ได้
แล้วเอาpointer มาเปลี่ยนค่า
#include<stdio.h> void helloArray(int *ptr){ *(ptr + 0) = 111; *(ptr + 1) = 111; *(ptr + 2) = 111; *(ptr + 3) = 111; *(ptr + 4) = 111; } main(){ int data[] = {6,7,8,9,10,11}; helloArray(&data[0]); int i=0; for(i=0;i<5;i++){ printf("\n data[%d] = %d",i,data[i]); } }
ต้องนิยามฟังก์ชันไว้ก่อนใช้งานเสมอ
สำหรับในภาษา c ต้องเขียนฟังก์ชันไว้ก่อนเรียกใช้นะ ไม่อย่างนั้นมันจะไม่รู้จัก
เช่นผม ย้ายฟังก์ชันไว้ข้างล่าง main แบบนี้ มันก็จะ error
จบแล้วฮะ กับเรื่องฟังก์ชัน เนื้อๆทั้งนั้นเลย ลองไปฝึกใช้งานกันนะ
ตอนต่อไปจะพาไปเขียน C++ และรู้จักกับโลกของการเขียนโปรแกรมเชิงวัตถุหรือที่เรียกว่า OOP ครับ
C Programming : เขียนโปรแกรมภาษา C แบบพื้นฐาน ตอนที่ 6 – OOP World