ThaiGameDevX - Thai Game Developers eXchange Forums
23 September 2017, 06:10:17 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: หากมาครั้งแรก เชิญอ่าน ประกาศเจตนารมณ์ของ ThaiGameDevX และ กติกา ข้อตกลงในการใช้เว็บบอร์ด ครับ
 
   Home   Help Search Calendar Login Register  
Pages: [1] 2   Go Down
  Print  
Author Topic: เทคนิคการเขียนโปรแกรมให้ปลอด Bug  (Read 30455 times)
0 Members and 1 Guest are viewing this topic.
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« on: 07 September 2005, 08:32:51 AM »

การแก้ bug เป็นสิ่งที่เสียเวลาและน่าเบื่อมาก กระทู้นี้เรามาช่วยกันแชร์แนวทางและแสดงความเห็นเกี่ยวกับการเขียนโปรแกรมให้ปลอด Bug หรือช่วยให้ debug ง่ายโปรแกรมขึ้นครับ
Logged
นายตาหวาน (Mr.Tawan)
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +61/-36
Offline Offline

Gender: Male
Posts: 1,591


« Reply #1 on: 07 September 2005, 08:55:22 AM »

ผมเริ่มก่อนแล้วกัีน Cheesy
เข้าใจคอนเซพท์ของแต่ละภาษาให้ถูกต้อง
ผมว่า  การที่โปรแกรมที่เขียนมีปัญหา ส่วนใหญ่เลยคือเราเข้าใจ Concept อะไรในแต่ละภาษาผิดพลาดไป  ตัวอย่างเช่น ... สมมติว่า
Code:
Date Date::Today(){
   //สร้าง object ขึ้นมาตัวนึง
   Date date("today");
   return date;
}
(อันที่จริงภาษา C++ ผมไม่ค่อยแข็งน่ะครับ ท่าน Mak ช่วยมาเช็คอีกทีแล้วกันว่าผมเข้าใจถูกมั้ย)

ดูเผิน ๆ มันถูกครับ  สร้าง Object ขึ้นมาแล้วก็คืนไป  แต่ว่า ... ตัวแปร date จะถูกลบไปเมื่อหมด Scope ดังนั้นถ้าเราเรียก
Code:
Date today = Date::Today();
มันจะคืนค่าอะไรกลับมาก็ไม่ทราบครับ (เนื่องจากว่าค่าที่ื่มันคืนมาเป็นค่าที่ถูกลบไปแล้ว) เนื่องจากมีความเป็นไปได้ที่ตัวระบบจะคืนค่าตัวแปรกลับมาให้หลังจากตัวแปรถูกลบไปแล้วครับ

วิธีที่ถูกต้องคิือ ประมาณว่า
Code:
Date* Date::Today(){
   //สร้าง object ขึ้นมาตัวนึง
   Date *date = new Date("today");
   return date;
}

แล้วก็สร้าง pointer มาร้ับแทน จากนั้นก็ต้องลบค่านี้เอง (อย่าลืิมล่ะ สำคัญมาก!!!)
ผมจำตัวนี้ไม่ค่อยได้ ไว้ผมขอกลับไปอ่าน Effective C++ อีกรอบนึงแล้วจะกลับมาแก้นะครับ 555
Logged

Are you feeling fine?
眠れない夜には君の幻が...
She said, "Loving you made me happy everyday"
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #2 on: 07 September 2005, 09:07:41 AM »

(C++)

ผมจะพยายามใส่ const ทุกที่ ที่ไม่มีการเปลี่ยนแปลงค่า

เช่น

Code:
class Matrix44
{
public:
 �Matrix44 Add(const Matrix44 &m) const;
};

นอกจากจะทำให้เราไม่ไปเผลอเปลี่ยนค่าของมันโดยบังเอิญแล้ว ตอนดู declaration ของมันเราก็รู้เลยว่ามันจะทำอะไร
ตัวอย่าง การบวก Matrix จะ return ค่าเป็น Matrix ผลลัพธ์ของการบวกกันของ Matrix และจะไม่เปลี่ยนแปลง Matrix ที่เป็น input และไม่มีการแก้ไขค่าใน object ด้วย
Logged
Cray
Global Moderator
Sr. Member
*****

จำนวน ชม/ไม่พอใจ: +10/-0
Offline Offline

Gender: Male
Posts: 385



« Reply #3 on: 07 September 2005, 09:12:40 AM »

เมื่อก่อนผมคิดไปเขียนไป  สุดท้ายตอนตามมาแก้งานละปวดหัวจริงๆครับ  Code มันเป็นสปาเก็ตตี้ เพราะแก้กัน
แบบ Inline เลย แต่ส่วนหนึ่งก็เป็นเพราะงานมันมีแต่งานเผาด้วย  Sad


เพราะฉะนั้น.... จะให้โปรแกรมปลอด Bug (หรือให้มี Bug น้อยๆ) ต้องเริ่มตั้งแต่ตอน Design เลยครับ 
ใช้โปรแกรมจำพวก Mind Mapping มาช่วยในการรวมรวมและจัดกลุ่มความคิดต่างๆก่อน

ตอนนี้ผมใช้ Mind Mapping ในการช่วยจัดระเบียบความคิดเวลาทำงาน คือต้องคิดให้ได้ก่อนว่า เราจะทำอะไรและอย่างไร มีอะไรเป็น
Input และ Output บ้าง และอาจรวมไปถึงการคาดการถึงอุปสรรคที่อาจจะมี (อันนี้อาจจะมีผลต่อโปรแกรมเวลาที่กำหนด Timeline พลาด แล้วต้องเผางาน Grin)
แต่ว่านี่แค่เริ่มต้นเท่านั้นครับ แค่รวบรวมและจัดระเบียบความคิดเท่านั้น ยังต้องผ่านกระบวนการแปลงความคิดเป็น Specification อีกหลายเล่มเลย
ทั้ง Design Spec., Functional Spec. and blab balb blab....
ค่อนข้างเหนื่อยครับ แต่ช่วยได้มากเลย

Spec. มีความชัดเจน.... แค่นี้โปรแกรมก็ปลอดภัยจาก Bug ไประดับนึงแล้วครับ


แนะนำของ Free ครับ http://freemind.sourceforge.net/wiki/index.php/Main_Page
เป็น Java ครับ
Logged

"I'm starting with the man in the mirror, I'm asking him to change his ways." - Michael Jackson (Man in the mirror)
Cray
Global Moderator
Sr. Member
*****

จำนวน ชม/ไม่พอใจ: +10/-0
Offline Offline

Gender: Male
Posts: 385



« Reply #4 on: 07 September 2005, 09:25:28 AM »

ถ้าเอาเรื่อง Programming ก็มีเรื่องนึงครับ.... ตัวแปรจำพวก Array ให้ประกาศไว้ท้ายสุด
ไม่งั้นเวลามันใช้เกินขอบเขตที่กำหนดแล้ว.... ตัวแปรถัดมาจะตายอนาถครับ เจอมากับตัว   Embarrassed

ตัวอย่าง เป็น VC++ ครับ
Code:

ฺํBYTE bzArray[10];
int nInteger = 10;

strcpy((const char*)bzArray, "aaaaaaaaaaaaaa");
printf("%d", nInteger);  <--- ค่ามันกลายเป็นขยะ เพราะโดน a ทีี่่เกินมาทับจนปางตาย


เคยเข้าใจว่าการทำ malloc() หรือ new operator จะช่วยให้ memory ส่วนที่จองไว้ ไม่สามารถเขียนทับได้ เลยลองแบบนี้มั่ง

Code:

ฺํBYTE bzArray[10];
int* nInteger = new int;

*nInteger = 10;
strcpy((const char*)bzArray, "aaaaaaaaaaaaaa");
printf("%d", *nInteger);  <--- ตายอยู่ดี เพราะค่ามันไม่โดนเขียนทับ แต่ที่โดนเขียนทับคือ address มันครับ กลายเป็น 0xa...... ซะงั้น


สุดท้ายต้องแก้โดย

Code:

ฺํint nInteger = 10;
BYTE bzArray[10];

strcpy((const char*)bzArray, "aaaaaaaaaaaaaa");
printf("%d", nInteger);



ปล. เขียนจากความทรงจำครับ คำสั่ง strcpy มันใช่อย่างนี้ป่าวหว่า Huh? ลองพิสูจน์กันดูก็แล้วกันครับ  Tongue
Logged

"I'm starting with the man in the mirror, I'm asking him to change his ways." - Michael Jackson (Man in the mirror)
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #5 on: 07 September 2005, 09:36:01 AM »

#1 สำหรับการ return Pointer นั้นมันค่อนข้างอันตรายมาก

จากตัวอย่าง

Code:
Date* Date::Today(){
 � //สร้าง object ขึ้นมาตัวนึง
 � Date *date = new Date("today");
 � return date;
}

หรือ

Code:
class Date
{
public:
 � Date*Today()
 � {
 � � �return &today;
 � }
private:
 � Date today;
};

ผู้ใช้ (หรือ ตัวเราเองในภายหลัง) อาจไม่รู้ว่าสมควร delete pointer ที่สร้างขึ้นมาหรือไม่ ต้องมาไล่ code ดูใหม่

สิ่งที่ผมสังเกตได้จาก library หรือ api ส่วนใหญ่ เค้าจะใช่วิธีตั้งชื่อเอาน่ะครับ (GetXXX, CreateXXX)

เช่น
ScreenPointer CreateScreen(); � อันนี้มันสร้าง pointer แน่ ต้องใช้ funtion พวก DeleteScreen หรือ operator delete เมื่อเลิกใช้
ScreenPointer GetScreen(); �อันนี้มัน return pointer มาให้ใช้ อย่าไปลบของมันเชียวล่ะ
และมี document อธิบายเอาไว้ว่าควรลบหรือไม่ควร

อีกวิธีคือ ไม่ return เป็น pointer

1. return เป็นแบบ copy object ออกมาเลย
Code:
Date Date::Today() const{
 � //สร้าง object ขึ้นมาตัวนึง
 � return Date("today");
}

หรือ
2. ถ้า Today เป็น object ที่ไม่มีการเปลี่ยนแปลง (immutable object) ก็ return เป็น reference ออกมาได้และไม่ยอมให้แก้ไขค่า
Code:
static const Date &Date::Today() {
 � static Date today("today");
 � return today;
}

ตอนรับค่าที่ return ถ้าประกาศตัวแปรที่มารับเป็นแบบ refก็จะเป็นการ return by ref
const Date &today = Date::Today();

แต่ถ้าเป็น
Date today = Date::Today(); // มันจะมีการ copy ค่าที่ return มาอยู่ในตัวแปร today อีกที


แต่ ถ้าใครอุตริ delete &Date::Today(); หรือ cast ให้เอา const ออก มันก็ยังเจ๊งอยู่ดี
« Last Edit: 07 September 2005, 12:03:33 PM by mak » Logged
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #6 on: 07 September 2005, 09:56:07 AM »

มีอยู่อันนึงผมอ่านมาจากหนังสือ Writing Solid Code โดย Steve McGuire

คือมีโปรเจคหนึ่งเขาต้องการ optimize algorithm ให้เร็วขึ้น จากนั้นใช้วิธีคล้ายๆแบบนี้ครับ (ผมจำไม่ได้เลยเขียนมาใหม่)

Call_New_Algorithm(&result1);
#ifdef DEBUG
Call_Old_Algorithm(&result2);
ASSERT(Compare_Results_Equal(&result1, &result2));
#endif

คือในการ build แบบ debug จะมีการเปรียบเทียบผลลัพธ์ที่ได้จากทั้ง 2 alg's นี้ด้วย
Logged
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #7 on: 07 September 2005, 10:09:21 AM »

สำหรับ code แบบนี้

Code:
// 1
errcode func(cond1, cond2...)
{
� � if (cond1 && cond2 && cond3 && cond4)
� � � �if (cond5 && cond6 && cond7)
� � � � � � if (cond8 && cond9 && cond10)
� � � � � � {
� � � � � � � � // do whatever
� � � � � � � �
� � � � � � }
� � � � � � else return error3;
� � � � else return error2;
� � else return error1;
� � return ok;
}

กับ
Code:
// 2
errcode func(cond1, cond2...)
{
� � if (!cond1 || !cond2 || !cond3 || !cond4)
� � � �return error1;

� � if (!cond5 || !cond6 || !cond7)
� � � �return error2;

� � if (!cond8 || !cond9 || !cond10)
� � � �return error3;

� � // do whatever
� � return ok;
}


สำหรับแบบที่ 2 (reject ถ้า input ไม่ถูกต้อง) จะทำให้อ่านง่ายกว่า และ debug (วาง break point และ step over) ได้ง่ายกว่าด้วยครับ
« Last Edit: 07 September 2005, 10:14:16 AM by mak » Logged
นายตาหวาน (Mr.Tawan)
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +61/-36
Offline Offline

Gender: Male
Posts: 1,591


« Reply #8 on: 07 September 2005, 10:23:32 AM »

ผมทำเป็น Sticky ให้แล้วนะครับ Cheesy

ถ้าทำโปรเจคแบบ Cross Platform ให้หลีกเลี่ยงการใช้ Feature ที่มีในเฉพาะ Platform และ ถ้าหลีกเลี่ยงไม่ได้ก็ต้องระมัดระวังในการใช้ครับ

ตัวอย่างนี้อาจจะไม่ค่อยเกี่ยวกับเกมนัก แต่ ถ้าหากทำพวก Console Mode App เนี่ยน่าจะเจอ คือ ตัว ขึ้นบรรทัดใหม่ของแต่ละ Platformไม่เหมือนกันนะครับ  บน Linux ใช้ \n บน Windows เป็น \r\n แต่บน Mac ใช้แค่ \r เฉย ๆ ครับ

ดังนั้น ถ้าโค๊ดของคุณ ไม่ทำให้แยกแพลตฟอร์มกัน หรือใช้ตัวร่วมที่ใช้งานได้หมดทุกแพลตฟอร์ม (ในที่นี้คือ \r\n) ล่ะก็ คุณก็จะเจอปัญหาแปลก ๆ แน่ ๆ ครับ
Logged

Are you feeling fine?
眠れない夜には君の幻が...
She said, "Loving you made me happy everyday"
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #9 on: 07 September 2005, 12:01:36 PM »

เครื่องหมาย แยก directory ('\\' ของ windows กับ '/' ของ unix) ก็ต้องระวังครับ

« Last Edit: 08 September 2005, 12:44:28 AM by mak » Logged
NICK
Administrator
Full Member
*****

จำนวน ชม/ไม่พอใจ: +6/-0
Offline Offline

Gender: Male
Posts: 149


WWW
« Reply #10 on: 07 September 2005, 08:35:25 PM »

แจมบ้าง... Wink

เมื่อมีการประกาศตัวแปร ควรจะมีการกำหนดค่าเริ่มต้นเสมอครับ เพราะไม่อย่างนั้น อาจจะได้เจอบั๊กประหลาดๆ เช่น ใน Debug Version โปรแกรมทำงานได้ดีไม่มีปัญหา แต่ใน Release Version เปิดโปรแกรมปุ๊บระเบิดปั๊บ เป็นต้น

อันนี้ผมแนะนำว่าควรทำให้เคยชินเลยจะดีมากครับ เพราะบางครั้ง เราอาจจะพัฒนางานอยู่บน Debug Version ไปเยอะแล้ว กว่าจะได้ทดสอบ Release Version และถ้าหลงลืมตรงจุดนี้ไป จะทำให้หาบั๊กได้ลำบากมากครับ (เพราะบน Debug Version มันทำงานได้)
Logged
mcca
Newbie
*

จำนวน ชม/ไม่พอใจ: +1/-0
Offline Offline

Posts: 5



« Reply #11 on: 07 September 2005, 11:28:07 PM »

เสริมนิดครับ ก็ เวลาตั้งชื่อตัวแปรใน program หรือ class  ใช้วิธีตั้งตามแบบ hungarian notation ให้หมดทุกตัวก็จะดีมากครับเพราะเวลา debug program บางที code ยาวๆ แล้วบางคนจำไม่ได้ว่าตัวแปรที่กำลังเรียกใช้มันเป็นชนิดไหนหว่า หรือเป็น object ของ class อะไรหว่า ตั้งชื่อให้สื่อมากที่สุดก็ดีครับแต่ก็อย่ายาวไป แล้วก็เช็คตัวแปรที่ ทำการ allocate ให้ดีๆ ครับ ว่าเรา destroy ไปแล้วรึยัง เพราะส่วนใหญ่เขียนๆ มาแล้วเจอ memory leak กันบ่อยเหมือนกันบางทีรีบๆ บางทีไปไล่ดูที่ debug ออกมาแต่แก้ไปแก้มามันก็ยังไม่หายก็มี หรือจะให้ดีก็ทำ mudule มาเช็คตัวแปรว่า free ไปหรือยังเอาไว้ใส่เวลา destroy ไว้เลยก็ดีครับ   Smiley
Logged
Cray
Global Moderator
Sr. Member
*****

จำนวน ชม/ไม่พอใจ: +10/-0
Offline Offline

Gender: Male
Posts: 385



« Reply #12 on: 07 September 2005, 11:38:31 PM »

อิๆ ใครตอบกระทู้นี้ได้จากผมคนละ +1   Grin
Logged

"I'm starting with the man in the mirror, I'm asking him to change his ways." - Michael Jackson (Man in the mirror)
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #13 on: 08 September 2005, 12:43:53 AM »

คุณ Nick พูดถึง Debug Version/Release Version

Debug Version ของ VC++

สมมติถ้าเรา allocate พื้นที่สำหรับ array
int *arr = new int[1000];

จากนั้น
delete[] arr;

มันจะ fill ทุกค่าใน array ให้เป็น garbage (0xCC) ก่อนที่จะคืนพื้นที่ส่วนนั้นให้ระบบ
ถ้าตอน debug แล้วเห็นค่าใน array ของเราเป็น 0xCC เต็มไปหมดแสดงว่า มีการ delete array ตัวนั้นไปแล้วครับ
Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #14 on: 08 September 2005, 01:56:53 AM »

สำหรับ code แบบนี้

Code:
// 1
� � if (cond1() && cond2() && cond3() && cond4())

กับ
Code:
// 2
� � if (!cond1() || !cond2() || !cond3() || !cond4())

อืม จากคุณ� mak เป็นตัวอย่างที่ดีมากเลย ขอยกตัวอย่างมาใช้ต่อนะครับ
ถ้าเปลี่ยนจาก boolean cond1 เป็นฟังก์ชั่น cond1()

อาจจะทำให้เกิดบักได้ครับ ดังนี้
 compiler บางตัว จะทดสอบบางอันแล้วกระโดดข้ามไป ใน optimize mode
มาดูว่ามีกรณีไหนบ้าง

กรณี and กัน
ถ้า A && B && C จะเป็นจริงได้ ก็ต่อเมื่อ A และ B และ C เป็นจริงทั้งหมด ถ้าตัวใดตัวหนึ่งเป็น เท็จ ก็จะให้ค่าเท็จทั้งประโยค
ดังนั้นใน optimize ถ้า ทราบว่า A เป็น เท็จ ดังนั้น ค่า B กับ C ที่เหลือไม่ต้องสนใจ� เพราะว่าประโยคนี้เป็นเท็จแน่ๆ

กรณี or กัน
ถ้า A || B || C จะเป็นเท็จได้ ก็ต่อเมื่อ A และ B และ C เป็นเท็จทั้งหมด ถ้าตัวใดตัวหนึ่งเป็น จริง ก็จะให้ค่าจริง ทั้งประโยค
ดังนั้นใน optimize ถ้า ทราบว่า A เป็น จริง ดังนั้น ค่า B กับ C ที่เหลือไม่ต้องสนใจ� เพราะว่าประโยคนี้เป็น จริง แน่ๆ

จากตัวอย่างคือ ถ้า cond1() ให้ค่าเท็จตั้งแต่แรก (ไม่ว่าในประโยค && หรือ ||) ฟังก์ชันที่เหลือก็จะไม่ถูกเรียกเพื่อทดสอบว่าได้ค่าอะไร

ถึงตอนนี้โปรแกรมเมอร์บางคนจะดีใจมากๆ เพราะว่า ถ้าทำแบบนี้จะเสียเวลาตอน run-time ลดลงเพราะถ้า cond1()
เป็นเท็จ ก็ไม่ต้องไปทำ cond2() , cond3(), cond4() อีก โปรแกรมก็จะทำงานเร็วขึ้น� Grin

แล้วถ้าเราทราบว่า ฟังชันใด โอกาสโดยเฉลี่ยให้ค่า เท็จ � มากกว่า ฟังก์ชันอื่นๆ ก็เอาไว้ข้างหน้าสุด* เพื่อที่ฟังก์ชันหลังๆ จะได้ไม่ต้องไปคำนวนให้เสียเวลา อันนี้เป็น TRICK ที่ใช้กันปกติทั่วไป� �;)

.. แต่พอเห็นบักที่จะเกิดขึ้นไหมครับ ?� Roll Eyes

บักที่จะมีและมองไม่เห็นเลยก็เกิดขึ้นได้ตรงจุดนี้ ถ้าเราจำเป็นต้องทำ cond2(), cond3(), cond4() ก่อน ที่เข้าไปทำ loop หลังจาก if ครับ
ถ้า cond2() ไปปรับปรุงค่าอะไรบางอย่าง แล้วต่อจากนั้น เราจำเป็นจะต้องใช้มันอีก โอกาสที่ cond2() จะไม่ได้ไปปรับปรุงค่าก็จะมีมาก� Cheesy
วิธีแก้ไขก็คือ ก่อนที่จะเข้าไปทำงานที่ IF ก็รับค่าจากฟังก์ชันลงตัวแปร bool ซะก่อน ดังนั้นก็จะได้เป็นตัวอย่างแบบที่คุณ mak เขียนนะครับ แต่ก็จะเสียโอกาสใช้ TRICK ที่พูดถึงไปครับ� Wink

 Roll Eyes แต่เราสามารถใช้ผสมๆ กันก็ได้นะครับ

(*หรือเอาไว้ข้างหลังประโยค boolean exp แล้วแต่อุปนิสัยของ compiler ว่าทดสอบตัวแปรทางด้านไหนก่อน)
Logged

..
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #15 on: 08 September 2005, 02:35:23 AM »

เพิ่มเติมต่อจากคุณ Yod นะครับ

หากจะใช้การทดสอบ condition แบบที่เป็น function เช่น� if (cond1() && cond2() && cond3())
หาก condXX() ไม่ modify ตัวแปร global หรือ class member ก็ไม่มีปัญหาอะไร
แต่ถ้ามีการ modify และเราไม่ดูให้ดี อาจมีปัญหาครับ

สำหรับ c++ เราสามารถใส่ const ให้กับ class-member function ที่ไม่ modify ค่าใน class ได้ครับ
เช่น

Code:
bool MyClass::cond() const
{
� �// ข้างในนี้ modify class-member ไม่ได้ และ call function ที่เป็น const ได้อย่างเดียว
}

ปกติผมจะ test condition ที่เป็น function (แบบไม่เป็น const) เพียงตัวเดียว เพราะผมขี้เกียจมาดูว่า function ไหนมันได้ทำ function ไหนไม่ได้ทำ

Code:
if (!cond())� �// 1
� �return error;

if (!cond2()) // 2
� �return error;

if (!const_cond1() || !const_cond2() || !const_cond()) // 3
� �return error;
 
// yahhhoo!!
return ok;

ปกติแบบ 1, 2 ผม test มันตัวเดียวเลย
แบบ 3 แน่ใจได้ว่ามันจะไม่ modify ค่าใน class แต่ตอน debug ว่าอันไหนมัน fail จะ check ลำบาก เพราะต้อง step เข้าไปทีละตัว แต่อาจใช้วิธีเอาตัวแปรมารับ หรือแก้เป็นแบบ 1, 2 ไปเลย ก็จะสะดวกดี (ใช้ step over ไปเรื่อยๆ เพลินๆ) แต่จะไม่ได้ใช้ feature ของ compiler ครับ

Code:
if (!cond())� �
� �return error;

if (!cond2())
� �return error;

if (!const_cond1())
� �return error;

if (!const_cond2())
� �return error;

if (!const_cond3())
� �return error;

// yahhhoo!!
return ok;

แบบนี้ทำให้สามารถวาง break point ได้ว่ามันรอดตาย มาถึง cond ไหน
« Last Edit: 08 September 2005, 02:44:57 AM by mak » Logged
sun_tct
Approved Member
Jr. Member
*

จำนวน ชม/ไม่พอใจ: +5/-0
Offline Offline

Gender: Male
Posts: 56


ทำไม่ได้ หรือ ไม่ได้ทำ


WWW
« Reply #16 on: 08 September 2005, 04:18:55 AM »

ผมอ่านแล้วมึนครับ แต่ล่ะอันต้องอ่านแล้ววิเคราะห์ทั้งนั้นเลย แต่ก็น่าสนใจมากๆครับ ผมอ่อนๆเลยแนะนำหนังสือแล้วกัน หนังสือชื่อ Code_Complete ของ Micorsoft (เขียนผิด) เอาเล่มนี้มาอ่านแล้วจะทำให้การเขียนโปรแกรมของคุณเป็นหลักเป็นการขึ้นเยอะ แล้วก็ลด/หลีกเลี่ยงบั๊กไปได้เยอะครับ  ราคาสูงนิดนึงแต่คุ้มครับ Cheesy
Logged

แมลงมันก็บินอยู่ทั่วไปหมด แต่เรารู้แค่ตัวที่อยู่ใกล้ๆหูเอง
<หลวงพี่เท่ง>
AAG_th4
Approved Member
Hero Member
*

จำนวน ชม/ไม่พอใจ: +42/-7
Offline Offline

Gender: Male
Posts: 601


Fighter/Attack Pilot


WWW
« Reply #17 on: 08 September 2005, 09:39:29 PM »

เป็นTopic แนะนำที่มีประโยชน์มากครับ
Logged

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

"Rig for Sillent Running"
Cray
Global Moderator
Sr. Member
*****

จำนวน ชม/ไม่พอใจ: +10/-0
Offline Offline

Gender: Male
Posts: 385



« Reply #18 on: 08 September 2005, 10:46:25 PM »

เป็นTopic แนะนำที่มีประโยชน์มากครับ

อ่า... แบบนี้ไม่ได้ +1 นะคับ  Tongue
Logged

"I'm starting with the man in the mirror, I'm asking him to change his ways." - Michael Jackson (Man in the mirror)
AAG_th4
Approved Member
Hero Member
*

จำนวน ชม/ไม่พอใจ: +42/-7
Offline Offline

Gender: Male
Posts: 601


Fighter/Attack Pilot


WWW
« Reply #19 on: 09 September 2005, 04:16:29 AM »

อ้าว! หมายความว่าผมต้องPost Code อะไรหน่อยใช่ไหมครับ
เอาเป็นเรื่องการตั้งตัวแปรสำหรับการคำนวนค่าแล้ว
ปกติแล้วในโปรแกรมขนาดเล็กการตั้งค่าตัวแปรสั้นเช่น X, Y นั้นเป็นการเขียนปกติที่มักจะใชเกน เช่นการแทนค่าในสมการการค่าการกระจัดของวัตถุ
ตัวอย่างเช่นสมมุติว่าตั้งเงื่อนไขขึ้นมา
Code:
IF X <= 180 && X >=90 THEN X = 180;

แต่การตั้งตัวแปรในลักษณะนี้ในการเขียนProgramจริงๆจะมีปัญหาต่อไปในอนาคต เช่น ตัวแปรชื่อซ้ำกัน หรือ จำไม่ได้ว่าใช้เก็บหรือรับค่าอะไรเป็นต้น
และมันก็เจอกันบ่อยๆถึงแม้ว่าจะเขียนมานานก็ตาม

วิธีแก้ไขปัญหานี้คือตั้งชื่อตัวแปรให้บอกถึงจุดประสงค์การใช้งานมันเลยดีกว่า เช่น
Code:
INT int_Player_AngleX; //ตัวแปรค่าX ของPlayer
การตจั้งชื่อแบบนี้อาจจะยาวแต่การตั้งชื่อตัวแปรให้สั้นและสื่อความหมายในตัวย่อมส่งผลดีครับ
Logged

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

"Rig for Sillent Running"
The Miss
Full Member
***

จำนวน ชม/ไม่พอใจ: +2/-1
Offline Offline

Gender: Male
Posts: 117


I am a Krajog (คนกระจอก)


« Reply #20 on: 09 September 2005, 08:39:51 AM »

อะต่อจาก      Hungarian Notation

เอามาจาก Thaidev สำหรับคนที่ยังไม่รู้นะครับ

PREFIX   TYPE
sz    Pointer to first character of a zero terminated string (such as : szName )
str    string (such as : strName, strValue)
i    Integer (iValue, iData)
n    it is a number ( : nStatus)
ui, UINT    unsigned integer ( uiInterval)
c    Character (cSex, c_Sex)
w    Word (unsigned short)
dw    DWORD (unsigned long)
fn    Function Pointer
d    Double
by    Byte
l    Long
p    Pointer
lp    Long Pointer
lpstr    Long Pointer to a String
h    Handle (hInstance, hWnd)
m_    member of function (m_name, m_edit you can see more in MFC variables)
g_    Global variable
hwnd    Window Handle

ที่มา http://www.thaidev.com/cpage.php?page=hungarian
Logged

Standing By
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #21 on: 09 September 2005, 09:21:01 AM »

hungarian notation ใช้ใน code c/c++ ของ Microsoft เช่น DirectX, WinAPI
แต่ใน c#, java จะไม่มี เพราะ c# เป็นภาษาที่เป็น type-safe อยู่แล้ว แต่ assign ค่าผิดประเภท compiler มันก็จะเตือนให้เอง

ปัญหาที่ผมว่าใครๆก็น่าจะเคยเจอกับ hungerian notation คือ ตอนแรกชนิดตัวแปรเป็นอย่างหนึ่ง เช่น float (ใส่ prefix f) จากนั้นตัดสินใจเปลี่ยนเป็น double ทีหลัง เลยต้องมาเปลี่ยนชื่อตัวแปรทั้งโปรแกรม [แต่ก็สามารถ replace all ได้ไม่ลำบาก]
« Last Edit: 09 September 2005, 10:25:13 AM by mak » Logged
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #22 on: 09 September 2005, 09:44:20 AM »

จากหนังสือ Writing Solid Code และ Writing Secure Code

bug ที่เกิดขึ้น บ่อยๆ จากโปรแกรมที่เขียนโดยใช้ภาษา c คือ string ที่เป็น char* นั่นเอง (แต่ผมก็ดันยังใช้อยู่ -_-;)

ตัวอย่าง

Code:
void ErrorMessage(const char *msg, int line)
{
   char buf[1000];
   sprintf(buf, "Error: %s, at line %d", msg, line);
   MessageBox(NULL, buf, "Message", MB_OK|MB_ICONERROR);
}

ปัญหาที่เห็นได้ชัดคือ เราอาจจองเนื้อที่ buf[1000] น้อยกว่า ความยาวของ msg ก็ได้


Code:
void ErrorMessage(const char *msg, int line)
{
   char *buf = (char *)calloc(sizeof(char), strlen(msg)+1);
   sprintf(buf, "Error: %s, at line %d", msg, line);
   MessageBox(NULL, buf, "Message", MB_OK|MB_ICONERROR);
   free(buf);
}

ถ้าแก้ให้เป็นการจองเนื้อที่แบบ dynamic โดยใช้ strlen เป็นตัวนับความยาว string ล่ะ?
strlen จะนับความยาว string จากอักษรตัวแรกไปจนเจอ null ('\0') แต่ถ้าฝั่งที่เรียก function นี้ดันไม่ได้ใส่ '\0' ปิดท้าย string ล่ะ? 

Code:
void ErrorMessage(const char *msg, size_t len, int line)
{
   char *buf = (char *)calloc(sizeof(char), len+1);
   sprintf(buf, "Error: %s, at line %d", msg, line);
   MessageBox(NULL, buf, "Message", MB_OK|MB_ICONERROR);
   free(buf);
}

กลายเป็นว่ามี parameter ยุ่งยากเพิ่มเข้ามาอีกตัวซะแล้ว (และหวังว่า parameter len จะถูกต้องด้วย)

ใน c++ มี std::string ซึ่งทำให้ชีวิตง่ายขึ้น หรือลองหา opensource project ที่เกี่ยวข้องกับ string จะมี string class อื่นๆให้เลือกใช้ได้อีก
Logged
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #23 on: 09 September 2005, 09:54:15 AM »

เก็บกวาด memory leaks ให้หมด ใช้ MMGR ได้ครับ เยี่ยมมาก:
http://www.fluidstudios.com/
วิธีใช้คือ include mmgr.h เป็น .h สุดท้ายในทุกๆ cpp file ครับ
Logged
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #24 on: 11 September 2005, 12:33:02 PM »

OOP:
การใช้ class factory แทน constructor

Code:
class MyClass
{
protected:
� � MyClass(); // ซ่อน ctor เอาไว้ไม่ให้ client access ได้

public:
� �static MyClass *CreateObject(...param...); // class factory

};


client (ผู้ที่เรียกใช้บริการจาก class นี้) สามารถสร้าง instance ได้ทางเดียวคือจาก static function CreateObject()

ข้อดี
สามารถควบคุมการสร้าง instance ได้ เช่น อาจสร้าง instance ใหม่ทุกครั้ง หรือส่งค่า instance ที่เป็น singleton หรืออาจมีการจำกัดการสร้าง instance และในภายหลังสามารถเปลี่ยนวิธีการสร้างใหม่ได้อีกโดยที่ client ไม่ต้องคอยตามเปลี่ยน code

Logged
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #25 on: 11 September 2005, 12:54:23 PM »

ในภาษาพวก OOP มี feature Encapsulation เราควรใช้มันเพื่อซ่อน data field เอาไว้ แล้วให้ client access ผ่านทาง function/method/property(ใน c#)


ตัวอย่าง เช่น ในตอนแรกต้องการเก็บข้อมูลนักเรียนแบบเป็น Array
Code:

class School
{
public:
   int GetStudentCount() const { return studentCount; }
   ErrorCode AddStudent(Student s);

private:
   Student students[100];
   int studentCount;
};


อาจจะมีปัญหาถ้า client สามารถ แก้ไขค่าภายใน class ได้หรือเพิ่มจำนวนได้นักเรียนเองโดยอาจไม่ได้ป้องกันเหตุการณ์ที่จะมี error เกิดขึ้น
และในตอนหลัง ต้องเราการเปลี่ยนข้อมูลนักเรียนให้เป็น list เราก็สามารถทำได้โดยที่ไม่ต้องเปลี่ยน code ที่ client
Logged
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #26 on: 11 September 2005, 01:14:04 PM »

C++

const double PI = 3.1413592;
#define PI (3.141592)

ทั้ง 2 ตัวนี้สามารถใช้กับค่าคงที่ได้เหมือนกัน

ในตอน compile
แบบ #define - compiler จะแทนที่ PI ที่เจอในโปรแกรม ด้วย 3.141592 ก่อนที่จะแปลงเป็น object code ทำให้ debugger ไม่รู้ว่ามี PI อยู่ในโปรแกรมด้วย

แบบ const - แล้วแต่ compiler ว่าจะแปลงเป็นตัวเลข(สำหรับใช้กับ instruction[1]) หรือยังเป็นตัวแปรที่อยู่ใน memory แต่ไม่ว่าแบบไหน มันก็ยังมี PI ให้ debugger เห็นอยู่ใน debug information[2]

[1] instruction - คำสั่งที่ให้เครื่องทำงาน
[2] debug information - เมื่อเลือก option การ compile ให้มี debug information ด้วย ตอนทำการ debug เราก็สามารถถาม debugger ได้ว่า ตัวแปรตามชื่อที่เราระบุเอาไว้มีค่าเป็นเท่าไหร่
Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #27 on: 11 September 2005, 06:37:30 PM »

+1 ให้คุณ mak คับ Smiley
Logged

..
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #28 on: 12 September 2005, 09:40:00 AM »

+1 ให้คุณ mak คับ Smiley

ขอบคุณครับ  Smiley

ช่วยกันเสนอและคอมเมนต์เยอะๆนะครับ
Logged
นายตาหวาน (Mr.Tawan)
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +61/-36
Offline Offline

Gender: Male
Posts: 1,591


« Reply #29 on: 12 September 2005, 11:59:06 AM »

ขอ Java หน่อย เดี๋ยวมีคนน้อยใจ

  • Class ใน Java ทั้งหมด สืบทอดมาจากคลาสที่ชื่อว่า "Object"
  • ตัวแปรของ Object ทุกชนิดใน Java เป็น Reference อันนี้ืมือใหม่ผิดบ่อยครับ  ผมเองตอนหัดแรก ๆ ก็เป็น  เอาง่าย ๆ เลยก็อย่างตัวอย่างข้างล่าง
Code:
Label A = new Label("this is Label 1");
Label B = A;
B.setText("this is Label 2");
ผลลัพท์จะกลายเป็น ทั้ง A และ B จะมี text เป็น "this is Label 2 " ทั้งคู่ครับ  เพราะว่าทั้ง A และ B นั้นที่จริงแล้วเป็น Object เดียวกันนั่นเอง
[li]ตัว Premitive Datatype ไม่ได้มีคุณสมบัติเป็น Object ครับ ดังนั้นเราจึงไม่สามารถทำแบบข้างล่างได
Code:
int A = 10;
Object B = A;
แต่ว่าตัว Premitive ทุกตัวจะมี Wrapper Class อยู่ครับ ดังนั้นเราจึงทำแบบนี้ได้
Code:
int A =10;
Object B = new Integer(A);
เพราะว่า Integer เป็นคลาส และ ทุกคลาสเป็นคลาสที่สืบทอดมาจาก Object (อ่านข้อบนสุดประกอบ)
[li]ตัวแปรบน Java เป็น Big Endian ตามปกติทั่วไปตัว Runtime จะดูแลตรงนี้ แต่มีบางกรณีเหมือนกันที่เราจะต้องไปดูแลเอง  ดังนั้นจงระวัง
ให้ดีโดยเฉพาะผู้ที่เคยชินกับตัว Windows ที่เป็น Little Endian[/li]
[li]ขออภัยครับ Java ไม่มี Unsigned และความจริงก็คือ ชุด Standard Library เหมือนจะพยายามแกล้งเราให้ใช้ Premitive Datatpe แค่ไม่กี่ตัว (ที่เจอบ่อยก็จะเป็น Double กับ Long) ดังนั้น ถ้าจำเป็นจะต้องแปลงข้อมูลให้มี Precision ต่ำลงก็ต้องระวังด้วยครั
Logged

Are you feeling fine?
眠れない夜には君の幻が...
She said, "Loving you made me happy everyday"
Pages: [1] 2   Go Up
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2013, Simple Machines Valid XHTML 1.0! Valid CSS!