หนึ่งในเรื่องปราบเซียนที่คนเข้าใจผิดกันมากมายคือเรื่อง Pass by Reference กับ Pass by Value ครับ ก่อนอื่นต้องขออนุญาตบอกก่อนว่าเรื่องนี้ไม่เหมาะกับมือใหม่ เพราะอ่านแล้วอาจจะปวดหัวและหมดกำลังใจในการเรียนได้ ดังนั้นถ้าท่านอ่านแล้วไม่เข้าใจก็ไม่ต้องกังวลไปครับ ผู้ที่ลงเรียนกับทาง EPT ขอให้ลองย้อนมาอ่านอีกครั้งหลังเรียนและทำการบ้านจบเรื่อง OOP จะทำให้เข้าใจมากขึ้น ส่วนผู้ที่ไม่ได้เรียนกับทางเรานั้น ผมก็ไม่รู้จะแนะนำอย่างไรดีเพราะแต่ละสถาบันเนื้อหาที่เรียนจะหนักเบาไม่เท่ากับแม้จะชื่อหัวข้อเหมือนกันก็ตาม ดังนั้นหากท่านอ่านแล้วไม่เข้าใจขอแนะนำให้ลงคอร์ส เรียนเขียนโปรแกรมภาษา JAVA (J103) หรือถ้าอยากเรียนแบบยาว ๆ สะใจเพื่อทำงานด้านโปรแกรมเมอร์ก็ขอแนะนำคอร์ส SET B ภาษา Java (SET-B-J) เรียนจบแล้วเทพแน่นอนครับ
เมื่อจบคำนำให้กำลังใจไปแล้ว ตอนนี้ก็มาเข้าเรื่องกันดีกว่าครับ สำหรับผู้เรียนที่เรียนกับทาง EPT นั้น โดยทั่วไปผมจะสอนโดยมองในมุมของของสิ่งของหรือ Object ที่ถูกส่ง ไม่ใช่มองในมุมมองของตัวแปรเนื่องจากจะเข้าใจง่ายสำหรับมือใหม่ และสามารถนำความเข้าใจนี้ไปประยุกต์ใช้ได้แทบทุกภาษา นั่นคือจะสอนด้วยนิยามตามที่กำหนดไว้ในทฤษฎี Computer Science ดังนี้
เมื่ออ่านมาถึงตรงนี้น่าจะยังไม่มีใครงงใช่ไหมครับ ทีนี้เราจะมาเข้าถึงปัญหากันครับ ขอย้ำอีกครั้งว่าถ้าอ่านแล้วงงไม่ต้องตกใจหรือหมดกำลังใจในการเรียนนะครับ มันเป็นแค่เรื่องของคำนิยามและภาษาต่างชาติเท่านั้น ไม่ได้ส่งผลใด ๆ ต่อการเขียนโปรแกรมของเราเลยถ้าเรามีความเข้าใจโปรแกรมมิ่งพื้นฐานตามที่ผมสอนอยู่แล้ว เพียงแต่ผู้ที่จะสอบ Certification จะต้องระวังหน่อยครับ โดยต้องใช้นิยามดังที่จะกล่าวถึงต่อไปนี้แทน
ก่อนอื่นจะเริ่มอธิบายขอให้อ่านสรุปของแต่ละภาษานี้ก่อนครับ
ที่เราเรียนกันเราพูดว่า ตัวแปรแบบ reference type จะ pass by value โดยอัตโนมัติ แต่การ pass ตัวแปรที่เป็น reference type ไปยังฟังก์ชันสามารถมองได้เป็น การ pass by value เช่นกันถ้ามองเฉพาะตัวแปรนั้น ๆ ที่เป็น pointer ไม่ใช่มองถึง Object ที่ pointer นั้นชี้อยู่ ดังเช่นตัวอย่าง Code ต่อไปนี้
static void Change(int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change is local.
System.Console.WriteLine("Inside the method, : {0}", pArray[0]);
}
static void Main()
{
int[] arr = {1, 4, 5};
System.Console.WriteLine("Inside Main, before : {0}", arr [0]);
Change(arr);
System.Console.WriteLine("Inside Main, after : {0}", arr [0]);
}
/* Output:
Inside Main, before c: 1
Inside the method, : -3
Inside Main, after : 888
*/
ใน Code ด้านบน มีการแก้ไขค่าใน method 2 อย่างคือ แก้ค่าใน Array และการแก้ให้ pArray ไปชี้ object ใหม่เลย ซึ่งหนังสือบางเล่มจะมองว่า ในเมื่อการแก้ให้ pArray ไปชี้ Object ใหม่เป็นการแก้ค่า "Value" ของ pArray จึงเรียกว่า เป็นการ pass by value ของตัวแปรแบบ reference type โดยมองว่าตัวแปร pArray (ที่เป็น parameter)เป็น Copy ของ ตัวแปร arr ใน Main คำพูดที่ถูกต้องแต่ชวนงงคือ
"หากไม่กำหนดอะไรเพิ่มเติม C#/Java จะ pass by value เสมอ (มองในมุมของตัวแปร) เรียกว่า pass the reference by value"
แต่ที่เราเรียนเรามองในมุมมองของ Object ที่ตัวแปรนั้น ๆ ชี้อยู่จึงเป็นการ pass by reference ของ Object นั้น ดังนั้นตัวแปร reference type หากมีการส่งค่าให้กับ method จะมีการ pass by reference โดยอัตโนมัติมองในมุมของ Object เพราะที่เราสอนเรามองในมุมของของ Object นั้นเอง (ซึ่งหนังสือบางเล่มจะมองในมุมมองของตัวแปร) ซึ่งขัดกับมุมมองของตำราบางเล่ม
ลองดูคนดังพูดเรื่องนี้อย่างไร
1. มุมมองของ James Gosling คิดว่าการส่ง reference type ให้ Oject เรียกว่า pass the reference by value
Some people will say incorrectly that objects are passed "by reference." In programming language design, the term pass by reference properly means that when an argument is passed to a function, the invoked function gets a reference to the original value, not a copy of its value. If the function modifies its parameter, the value in the calling code will be changed because the argument and parameter use the same slot in memory.... The Java programming language does not pass objects by reference; it passes object references by value. Because two copies of the same reference refer to the same actual object, changes made through one reference variable are visible through the other. There is exactly one parameter passing mode -- pass by value -- and that helps keep things simple. -- James Gosling, et al., The Java Programming Language, 4th Edition
2. มุมมองของ Microsoft ก็มีในแนวทางเดียวกัน
สำหรับเรื่องนิยามที่ผมสอนในคอร์สอาจใช้คำพูดที่ผิดจากคนทั่วไป แต่ไม่ได้สอนผิดในหลักการที่สำคัญ
กรุณาดู https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value เถียงกันยาวเหยียด ถึงแม้ใน link จะเป็น JAVA แต่ก็หลักการเดียวกันกับของ C# ครับ
ลองมาดูรูปบางส่วน (credit ตาม link ด้านบน)
สรุปอีกครั้งก็คือในคอร์สจะนิยามโดยมองในมุมของ Object ที่ส่ง แต่ในมุมมองของ microsoft และ James Gosling มองในมุมของตัวแปร ดังนั้นผู้เรียนที่จะสอบ Certification ระวังเรื่องนี้ดีดีนะครับ ต้องใช้นิยามตามของเขา ที่ผมสอนอาจจะใช้คำไม่ถูก(เพราะมองในมุมของ Object ) แต่หลักการเดียวกันครับ
ภาษา C++ นี้ มีทั้ง Pass by Value และ Pass by Reference ซึ่งตรงตามนิยามที่ผมสอนในคอร์ส ดังนั้นมาดูตัวอย่าง Code pass by reference เพื่อเปรียบเทียบกับของ Java/C# กันครับ (ที่มา https://www.ibm.com/docs/en/zos/2.4.0?topic=calls-pass-by-reference-c-only)
#include <stdio.h>
void swapnum(int &i, int &j) {
int temp = i;
i = j;
j = temp;
}
int main(void) {
int a = 10;
int b = 20;
swapnum(a, b);
printf("A is %d and B is %d\n", a, b);
return 0;
}
เมื่อเรียกฟังก์ชัน swapnum() ค่าของตัวแปร a และ b จะเปลี่ยนไปด้วยเนื่องจากเป็นการ pass by reference ดังนั้นจะได้ผลลัพธ์ดังนี้
A is 20 and B is 10
Tag ที่น่าสนใจ: pass_by_reference pass_by_value programming_language java c# c++ object-oriented_programming variable function method parameter_passing computer_science
หากมีข้อผิดพลาด/ต้องการพูดคุยเพิ่มเติมเกี่ยวกับบทความนี้ กรุณาแจ้งที่ http://m.me/Expert.Programming.Tutor
085-350-7540 (DTAC)
084-88-00-255 (AIS)
026-111-618
หรือทาง EMAIL: NTPRINTF@GMAIL.COM
Copyright (c) 2013 expert-programming-tutor.com. All rights reserved. | 085-350-7540 | 084-88-00-255 | ntprintf@gmail.com