Practical 14 ปฏิบัติการครั้งสุดท้ายสำหรับภาคเรียนนี้ เป็นการอธิบายประกแบการสาธิตเรื่อง error และการทำ program tracing ตอนที่ 1: ความผิดพลาดในโปรแกรม ---------------------------- ความผิดพลาดในโปรแกรมมี 3 แบบคือ 1. Syntax error เป็นความผิดพลาดที่เกิดจากการใช้คำสั่งที่ไม่ถูกต้องตามรูปแบบของไวยากรณ์ ความผิดพลาดเช่นนี้ตัวแปลภาษา ตรวจสอบและแสดงคำเตือน เช่น วงเล็บไม่ครบคู่ เครื่องหมายคำพูดไม่ครบคู่ หรือรูปแบบคำสั่งไม่ถูกต้องตามไวยากรณ์ ความผิดพลาดแบบนี้ตัวแปลภาษาที่เป็น compiler จะแสดงความผิดพลาดระหว่างการคอมไพล์ เรียกว่า compile time error ส่วนภาษาที่เป็น interpreter จะทำงานไปจนกว่าจะพบบรรทัดคำสั่งที่ผิดพลาด 2. Run-time error เป็นความผิดพลาดที่เกิดขึ้นในระหว่างการทำงาน เช่น การหารด้วยศูนย์ (Division by zero), การอ้างอิงถึงหน่วยความจำที่อยู่นอกขอบเขตที่ได้รับอนุญาต (Segment violation หรือ Segmentation fault) 3. Logical error ความผิดพลาดที่เกิดจากแนวคิดผิด โปรแกรมทำงานได้โดยไม้เกิด error ใด แต่ผลลัพธ์ที่ได้จากการทำงานของโปรแกรมไม่ถูกต้อง อันเป็นผลจากแนวคิดของโปรแกรม หรือตรรกะของโปรแกรมผิด เป็นความผิดพบาดที่หาที่ผิดได้ยาก ตอนที่ 2: Program tracing ----------------------- Tracing เป็นการติดตามการทำงานของโปรแกรมทีละคำสั่ง เพื่อดูการเปลี่ยนแปลงค่าของตัวแปร การติดตามการทำงานทำได้โดยใช้กระดาษดินสอติดตามการทำงาน หรือใช้โปรแกรม debugger ติดตามการทำงาน การติดตามการทำงานโดยใช้กระดาษดินสอเป็นวิธีที่ดีสำหรับผุ้เริ่มต้นเขียนโปรแกรมในอันที่จะช่วยให้เข้าใจได้อย่างขัดเจนว่าคำสั่วแต่ละคำสั่งทำงานอย่างไร วิธีการอย่างง่ายคือเขียนชื่อตัวแปรทุกตัวที่สนใจ และเขียนค่าของตัวแปรนั้นกำกับไว้ด้วย ทุกครั้งที่ค่าของตัวแปรนั้นเปลี่ยนไป ให้ขีดค่าเดิมออกและเขียนค่าใหม่แทน การทำเช่นนี้จะช่วยให้เข้าใจได้ว่าค่าของตัวแปรเปลี่ยนไปอย่างไร และมีผลอย่างไรต่อการทำงานของโปรแกรม โปรแกรมสำหรับสาธิตการทำ Program tracing $ cat -n avg1.sh 1 #!/bin/bash 2 a=0 3 i=1 4 5 echo "Enter data: " 6 read x 7 8 while [ $x != "-1" ]; do 9 a=$(echo "$a + ($x - $a) / $i" | bc -l) 10 i=$(($i + 1)) 11 read x 12 done 13 14 echo "Output = $a" วิธีการที่ดีอีกอย่างหนึ่งคือการแทรกคำสั่งพิมพ์ค่าของตัวแปร และค่าของนิพจน์ระหว่างการคำนวณ เพื่อเปรียบเทียบกับการติดตามการทำงานด้วยกระดาษดินสอ หากผลลัพธ์ที่ได้ไม่ตรงกัน แสดงว่าผู้ติดตามการทำงานเข้าใจผิด คอมพิวเตอร์เป็นฝ่ายถูก ซึ่งถือเป้นโอกาสอันดีที่จะได้ทำความเข้าใจเรื่องราวนั้นใหม่ ให้ชัดเจน เช่นการเพิ่มคำสั่ง echo สำหรับแสดงผลในโปรแกรมที่ผ่านมา $ cat -n avg2.sh 1 #!/bin/bash 2 a=0 3 i=1 4 5 echo "Enter data: " 6 read x 7 8 while [ $x != "-1" ]; do 9 10 echo "a = $a, i = $i, x = $x" 11 echo "$a + ($x - $a) / $i" 12 echo 13 14 a=$(echo "$a + ($x - $a) / $i" | bc -l) 15 i=$(($i + 1)) 16 17 read x 18 done 19 20 echo "Output = $a" $a เมื่อให้โปรแกรมทำงาน ปรากฏผลเช่น Enter data: ... 10.6 a = 10.50000000000000000000, i = 2, x = 10.6 10.50000000000000000000 + (10.6 - 10.50000000000000000000) / 2 ... Output = 10.60000000000000000000 จะเห็นว่าการแสดงผลสายอักขระของจำนวนจริงที่เก็บในตัวแปรมีความละเอียดมาก ซึ่งบางครั้งทำให้อ่านทำความเข้าใจได้ยาก จึงควรปรับปรุงการแสดงผล เพื่อให้เห็นผลลัพธ์ที่ชัดเจน โดยใช้คำสั่ง printf คำสั่ง printf - ในปัจจุบันมีทั้งคำสั่งของ Unix คือ /usr/bin/printf และเป็นคำสั่งภายในของ bash printf - จัดรูปแบบและแสดงผลข้อมูล (format and print data) รูปแบบการใช้งาน: printf รูปแบบ อาร์กิวเมนต์ "รูปแบบ" เป็นไปตามรูปแบบที่ใช้ในฟังก์ขัน printf() ของภาษา C ตัวอย่างการทำงานและความหมาย ------------------------- $ A=12.345678 $ printf "A = %f\n" $A # %f - แสดงผลเป็นจำนวนจริงชนิด float A = 12.345678 $ printf "A = %7.2f\n" $A #%7.2f - แสดงผลเป็นจำนวนจริงชนิด float ใช้เนื้อที่การพิมพ์ A = 12.35 # 7 ตำแหน่ง ตัวเหน้าลข 4 หลัก จุด 1 ตัว ตัวเลขหลังจุด 2 หลัก $ printf "A = %s\n" $A # %s - แสดงเป็นสายอักขระ A = 12.345678 $ printf "A = %d\n" $A # %d - แสดงเป็นจำนวนเต็ม -bash: printf: 12.345678: invalid number # เตือนว่ารูปแบบและข้อมูลไม่ตรงกัน A = 12 # คำเตือน ไม่ใช้ ความผิดพลาด จึงทำงานและแสดงผล เปลี่ยนแทนคำสั่งบรรทัดที่ 10 และ 11 เป็นคำสั่ง printf ดังนี้ 10 printf "a = %5.2f, i = %d, x = %5.2f\n" $a $i $x 11 printf "%5.2f + (%5.2f - %5.2f) / %d\n\n" $a $x $a $i ทดลองให้โปรแกรมทำงาน ผลลที่ได้เป็นอย่างไร การติดตามการทำงานด้วยกระดาษดินสอ กับการให้โปรแกรมแสดงค่าตัวแปรได้ผลตรงกันไหม การแสดงผลเพียงพอหรือไม่? หากไม่เพียงพอ ควรเพิ่มเติมที่จุดใด การติดตามการทำงานของโปรแกรม จุดมุ่งหมายมีสองประการคือ 1. ทำความเข้าใจการทำงานของ source code - โดยเฉพาะผู้ที่คิดจะทำงานกับ open source 2. หาจุดที่ผิดในโปรแกรม การฝึกหัดทำ program tracing เป็นพื้นฐานที่สำคัญในการใช้งาน symbolic debugger สำหรับหาที่ผิดในโปรแกรมขนาดใหญ่ ปฏิบัติการ: จงติดตามการทำงานของ shell script ต่อไปนี้ โดยใช้กระดาษดินสอ เมื่อกำหนดให้ เรียกใช้งานด้วย trace.sh 5 $ cat -n trace.sh 1 #!/bin/bash 2 3 a=2 4 r=3 5 rr=1 6 k=0 7 8 while [ $k -lt $1 ]; do 9 rr=$(($r * $rr)) 10 echo $(($a * $rr)) 11 k=$(($k + 1)) 12 done เมือได้ผลลัพธ์แล้ว ลองใช้คำสั่ง echo เพื่อพิมพ์ค่าของตัวแปร เปรียบเทียบกับการทำด้วยกระดาษดินสอ ผลลัพธ์ตรงกันหรือไม่? เข้าใขการทำงานของโปรแกรมชัดเจนขึ้นหรือไม่ คำแนะนำ ------ เมื่อทำงานใน loop ไปแล้วหนึ่งรอบ และต้องการหยุดเพื่อดูค่าตัวแปร และกดแป้น เมื่อต้องการทำงานต่อไป ทำได้โดยการแทรกคำสั่งต่อไปนี้ลงในตำแหน่งที่ต้องการของ loop echo -n "press enter to continue: " read key ตัวแปร key เป็นเพียงตัวแปรใดๆ มีไว้เพื่อให้ถูกต้องตามไวยากรณ์ของคำสั่ง read เท่านั้น เมื่อผู้ใช้กดแป้น ระบบจะกำหนดค่าสายอักขระว่างให้ตัวแปร key และทำงานต่อไป ค่าของตัวแปร key ไม่ได้รำไปใช้งานแต่อย่างใด :::โปรแกรมเพิ่มเติมสำหรับหัดทำ Program tracing::: 1. โปรแกรม fcmp.sh ------------------ $ cat fcmp.sh if [ $# -ne 2 ]; then echo "Usage: $0 filename1 filename2" echo "For eg.$0 abc.txt xyz.txt" exit 1 fi if [ ! -e $1 ]; then echo -e " $1 does not exist!\n" exit 1 fi if [ ! -e $2 ]; then echo -e " $2 does not exist!\n" exit 1 fi if cmp $1 $2 ; then echo "Both files content are same." rm -f $2 if [ $? -eq 0 ]; then echo -e "File $2 deleted successfully.\n" else echo -e "Error deleting file $2. Please check whether " \ "it is a regular file or not.\n" fi else echo -e "Files $1 and $2 are different.\n" fi 1. จงอ่าน source code ของโปรแกรม item3 และติดตามการทำงานด้วยตนเอง (program tracing) โปรแกรมนี้ใช้ทำอะไร -- ทำงานได้อย่างไร? 2. การทดสอบแฟ้มว่ามีอยู่หรือไม่ เพราะเหตุใดจึงใช้ option -e แทนที่จะเป็น -f อธิบายได้หรือไม่? 3. คำสั่ง cmp ใช้ทำอะไร? exit status ของคำสั่ง cmp จะเป็น 0 (ซึ่งทำให้เงื่อนไขของ if เป็นจริง) เมื่อใด? 4. โปรแกรมนี้มี "ข้อด้อย" อย่างไร? 2. โปรกรม comb.sh ----------------- $ cat comb.sh clear for i in 1 2 3 do for j in 1 2 3 do for k in 1 2 3 do echo "$i $j $k" done done done 1. โปรแกรมต่อไปนี้ใช้สำหรับทำอะไร? 2. จงอธิบายการทำงานของโปรแกรม