Code: #include <stdio.h> #define PAY_RATE3 10 #define PAY_RATE1 8.75 #define PAY_RATE2 9.33 #define PAY_RATE4 11.2 #define OT_RATE 5 // additional ot #define TAX_FIRST 0.15 #define TAX_SECOND .20 #define TAX_REST .25 #define FIRST_STEP 300 #define SECOND_STEP 150 int main(void) { int hour,fla,fla2,choi,cyfla,avoi; int inn; float gros,taxes,net; float prate; char ch; cyfla = 1; prate = 0.0; do { fla2 = 0; while(fla2 == 0) { //introduces pay rate per hour for(inn = 0;inn < 65;inn++) printf("*"); printf("\n"); printf("1) $8.75/hr"); for(inn = 0;inn < 25;inn++) printf(" "); printf("2) $9.33/hr\n"); printf("3) $10.00/hr"); for(inn = 0;inn < 24;inn++) printf(" "); printf("4) $11.20/hr\n"); printf("5) quit\n"); for(inn = 0;inn < 65;inn++) printf("*"); printf("\n"); avoi = scanf("%d",&choi); if(avoi != 1) { ch = getchar(); printf("avoid\n"); choi = 6; } // assign prate by switch switch(choi) { case 1: prate = PAY_RATE1; break; case 2: prate = PAY_RATE2; break; case 3: prate = PAY_RATE3; break; case 4: prate = PAY_RATE4; break; case 5: cyfla = 0; break; default: printf("Please use choice 1 to 5.\n"); } if(prate > 0.0 || cyfla == 0) fla2 = 1; } if(choi != 5) { printf("Enter your work hours in a week\n"); fla = scanf("%d",&hour); } while(fla == 1 && cyfla != 0) { gros = hour * prate; if(hour > 40) gros += OT_RATE * (hour - 40); if(gros <= FIRST_STEP) taxes = TAX_FIRST * gros; else // gros > FIRST_STEP { taxes = TAX_FIRST * FIRST_STEP; if(gros <= (FIRST_STEP+SECOND_STEP)) { taxes += TAX_SECOND * (gros-FIRST_STEP); }else // gros > (FIRST_STEP+SECOND_STEP) { taxes += TAX_SECOND * SECOND_STEP; taxes += TAX_REST * (gros-(FIRST_STEP+SECOND_STEP)); } } net = gros - taxes; printf("gross pay is %.2f, taxes pay is %.2f and \ net pay is %.2f\n",gros,taxes,net); printf("Enter your work hours in a week\n"); fla = scanf("%d",&hour); ch = getchar(); } prate = 0.0; }while(cyfla == 1); return 0; } กรณีแรก อันตรายในการใช้ switch หาก variable ที่เรานำมาทดสอบกับ switch ไม่สามารถใส่ค่าลงไปได้ เช่นในกรณีนี้ ให้ใส่ 1-5 แต่ถ้าเราไปเผลอ กดตัวอักษรเช่น q แบบนี้ จะทำให้ variable นั้นไม่มีค่าเมื่อเข้ามา test ที่ switch ซึ่งอยู่ใน while loop จะเจอ loop นิรันดร์ทันที แบบไม่มีการเทสต์ใด ๆ ทั้งสิ้น อันตรายมาก ๆ เพราะฉะนั้น จึงจำเป็นต้องมีการเทสต์ว่า ค่า variable นั้นมีค่าหรือไม่ ถ้าไม่มี ก็ให้ assign ค่าให้อัตโนมัติไปเลย ส่วน ch = getchar() ที่ใส่ไปนั้น เพื่อรองรับ character ที่จะเกิดขึ้น กรณี ที่เรา พิมพ์ q หรือ ตัวอักษรอื่นเป็น input ซึ่ง มันจะถูก assign ค่าให้ scanf ถัดไปใน loop ทันที หากไม่มี getchar() ตรงนี้รองรับ
Code: #include <stdio.h> #define ARTICHOKE_PRICE 2.05 #define BEET_PRICE 1.15 #define CARROT_PRICE 1.09 #define DISCOUNT 0.05 #define SHIPPING_FIRST 6.5 #define SHIPPING_SECOND 14 #define SHIPPING_ADD 0.5 int main(void) { int a_po,b_po,c_po,pou,tol_po; float a_co,b_co,c_co; float tol_co,dis,ship,net; int fla,fla2,cyfla,inn; char choi,ch; float pric; pric = 0.0; a_po = b_po = c_po = pou = tol_po = 0; a_co = b_co = c_co = 0.0; tol_co = dis = ship = net = 0.0; do { fla2 = fla = 0; cyfla = 1; while(fla2 == 0) { //introduces all vegitable for(inn = 0;inn < 65;inn++) printf("*"); printf("\n"); printf("a) artichoke"); for(inn = 0;inn < 24;inn++) printf(" "); printf("b) beet\n"); printf("c) carrot"); for(inn = 0;inn < 27;inn++) printf(" "); printf("q) quit\n"); for(inn = 0;inn < 65;inn++) printf("*"); printf("\n"); choi = getchar(); // assign pric by switch switch(choi) { case 'a': pric = ARTICHOKE_PRICE; break; case 'b': pric = BEET_PRICE; break; case 'c': pric = CARROT_PRICE; break; case 'q': cyfla = 0; break; default: printf("Please use choice a b c or q.\n"); ch = getchar(); } if(pric > 0.0 || cyfla == 0) fla2 = 1; } if(choi== 'a' || choi == 'b' || choi=='c' ) { printf("Enter your order in pound\n"); fla = scanf("%d",&pou); } if(fla==1) { if(choi == 'a') { a_po += pou; a_co += pou * pric; }else if(choi == 'b') { b_po += pou; b_co += pou * pric; }else if(choi == 'c') { c_po += pou; c_co += pou * pric; } tol_co += pou * pric; printf("At moment your order is %d artichokes, \ %d beets, %d carrots and it costs %f dollars\n", \ a_po,b_po,c_po,tol_co); }else { if(cyfla != 0) printf("your order is not correct\n"); ch = getchar(); } if(cyfla != 0) { ch = getchar(); pric = 0.0; } }while(cyfla==1); net = tol_co; tol_po = a_po + b_po + c_po ; if(tol_co >= 100) { dis = tol_co * DISCOUNT; } net -= dis; if(tol_po <= 5) { ship = SHIPPING_FIRST; }else if(tol_po < 20) { ship = SHIPPING_SECOND; }else { ship = ((tol_po - 20) * SHIPPING_ADD) + SHIPPING_SECOND; } net += ship; printf("Overall your order is\n"); printf(" ARTICHOKES BEETS CARROTS TOTALS\n"); printf("order in pounds %12d %12d %12d %12d\n" \ ,a_po,b_po,c_po,tol_po); printf("price %12.2f %12.2f %12.2f\n" \ ,ARTICHOKE_PRICE,BEET_PRICE,CARROT_PRICE); printf("cost in dollars %12.2f %12.2f %12.2f %12.2f\n" \ ,a_co,b_co,c_co,tol_co); printf("And finally it bills\n"); printf(" VEGITABLE COST DISCOUNT SHIPPING COST NETS PAY\n"); printf(" %14.2f %14.2f %14.2f %14.2f\n" \ ,tol_co,dis,ship,net); printf("Thank you for your purchase, Good bye!\n"); return 0; } กรณีต่อไป คือ ที่ผมสังเกตจาก switch จะเห็นว่า หากเราเลือกค่าให้ variable แล้วค่าไม่ตรงกับที่เราเลือก เช่นกรณี code นี้ choi = getchar() หากเรากด อย่างอื่น นอกจาก a,b,c,q แล้ว เราจำเป็นต้อง มี code ch = getchar() มารองรับ ไม่เช่นนั้น เมื่อกลับเข้า loop จะมีตัวอักษรค้างอยู่ 1 ตัว สรุปได้คร่าว ๆ ว่า กรณีที่เราจะใช้ ch = getchar() มารองรับ ตัวอักษรคงค้าง มี 2 กรณีแล้ว คือ กรณีแรก หากผ่านการ scanf ไปแล้ว ไม่ว่าจะสำเร็จหรือไม่ แล้วต้อง วน loop เพื่อไปเจอกับ scanf อีกครั้งทันที กับกรณีที่สอง หากเรา assign ค่าให้ variable ที่จะไปทดสอบ กับ switch แล้วไม่ได้ค่าที่ต้องการ (ไม่มีค่า) ซึ่งกรณีนี้ จะเจอ loop นิรันดร์ อย่างที่เคยบอก หรือมีค่าแต่เข้า default แม้ไม่เจอ loop นิรันดร์ แต่จะเหลือ character ค้างอยู่ 1 ตัว ซึ่งหากเราไม่เก็บค่านี้ไป การวน loop แม้ทำได้ แต่จะเกินไป 1 ครั้งเสมอ คือ เข้า default 2 รอบนั่นเอง แต่หาก เราจะใช้ getchar ไว้รองรับตอนจบ เหมือน code ที่แล้ว หากกรณีที่ไม่มี character ค้างไว้ อย่างกรณี code นี้ หาก เรากด q เพื่อออก (ซึ่งไม่มี อักษรคงค้าง) ถ้าเราไม่ทดสอบ ว่าเป็นการออกอย่างถูกต้อง ดัง code if(cyfla != 0) เราจะเจอ getchar อีกตัว ซึ่งทำให้ user ต้องกด enter เพื่อผ่านไปยังจุดสรุป ซึ่งไม่ควรจะมี
จากการได้อ่านข้อมูล เพิ่มเติม ทำให้ผมลองมาสรุปผลใหม่ ว่า จากการอ่าน input ด้วย getchar กับ scanf นั้น มีข้อแตกต่างคือ getchar จะไม่ข้าม new line character ซึ่งเกิดจากการกด enter เพื่อส่งผ่านข้อมูล ที่ buffer ได้เก็บไว้ ขณะที่เราใช้ keyboard พิมพ์ลงไป ใน command line ขณะที่ scanf จะข้ามตัวนี้ไป ซึ่ง ผมก็ได้พิจารณาว่า เมื่อเราพิมพ์ข้อมูลลงไป ถ้า ยังอยู่ใน loop เราก็จะมองข้าม new line character ไปไม่ได้ หาก getchar เกิดขึ้นติดต่อกัน เราจำเป็นต้อง เคลียร์ new line ch ออกก่อน และหาก scanf ติดต่อกัน เราจำเป็นต้องพิจารณาว่า scanf นั้นสำเร็จหรือไม่ ถ้าไม่ ค่าที่ว่า จะถูกนำไปส่งต่อให้ scanf ถัดไป ซึ่งไม่ใช่สิ่งที่เราคาดหวัง จาก flow ของโปรแกรม ดังนั้นกรณีนี้ เพื่อความปลอดภัย เราควรจะเคลียร์ ด้วย getchar จะดีที่สุด เพราะไม่ว่าจะสำเร็จหรือไม่ getchar จะรองรับ character ได้อยู่ดี คือ ถ้าไม่สำเร็จ getchar จะรองรับ character ตัวนั้น แล้วส่งผ่าน new line ไปให้ scanf ตัวต่อไป ซึ่งจะถูกข้ามไป แต่ถ้าสำเร็จ getchar จะรองรับ new line ch ซึ่งก็ไม่ได้ถูกใช้งานอยู่แล้ว กับ กรณีสุดท้าย getchar แล้วต่อด้วย scanf กรณีนี้ จะต้องพิจารณาว่า จะต้องรองรับ new line ch หรือไม่ ถ้ากรณีที่ เมื่อผ่าน getchar แล้วต้องเข้า scanf แน่ ๆ อันนี้ ไม่ต้อง เพราะ scanf จะข้าม new line ch ไป แต่ถ้าถึงเงื่อนไข ที่ผ่าน getchar แล้ว ไม่ต้องผ่าน scanf อีก อันนี้ ต้องเทสต์ เพื่อทำการเคลียร์ new line ch ให้หมด
วิธี declare string ในภาษา C เนื่องจาก C ไม่มี object string แบบ java ก็เลยต้อง declare โดยใช้ 3 แบบ ของ char 1 char array แบบกำหนด size ล่วงหน้า เช่น char word[7] = "String"; กรณีนี้ต้องระวัง ไม่ให้ size ซึ่ง ณ ที่นี้ คือ 7 น้อยเกินไป เพราะ ต้องเผื่อพื้นที่ให้ null character ซึ่งเป็นตัวระบุการ "จบ string" คือ 'S' , 't' , 'r' , 'i' , 'n' , 'g' , '\0' 7 ตัวพอดี แต่จะน้อยกว่าก็ได้ ถ้าน้อยกว่า ตัวที่เหลือ ก็จะเป็น null character เช่นกัน 2 char array แบบไม่กำหนด ขนาดล่วงหน้า เช่น char word[] = "String"; อันนี้เกือบเหมือนอันแรก แต่ให้ compiler เป็นคนกำหนด size ให้เมื่ออ่านตัวหนังสือที่ให้ไป ในที่นี้คือ 7 byte 6 ตัวหนังสือบวกด้วย null character 3 char pointer เช่น char * word = "String"; อันนี้คือ บอกว่า pointer to char ตัวนี้ ชี้ไปที่ตัวแรก ก็คือ S ของตัวหนังสือ String ขนาดของ variable ตัวนี้ มีขนาดเท่ากับ pointer to char เท่านั้นไม่ใช่ขนาดของ ตัวหนังสือทั้งหมด แต่ pointer to char นี้ต้อง assign ค่าให้เท่านั้น ไม่ว่าจะในการ declare แบบนี้ หรือ assign ค่าจาก pointer to char ตัวอื่น ไม่สามารถทำแบบนี้ได้ char * word; word = "String"; แบบนี้ผิดทันที เพราะการ declare ครั้งแรก ไม่มีค่า address ให้ pointer ตัวนี้แต่อย่างใด ถ้ามาเจอบรรทัดที่สอง จะ undefined error ได้เลย แต่ถ้า char * word = "String"; char * copy; copy = word; อันนี้ได้ เพราะ address ที่ assign ให้ copy คือ address ที่ assign ให้ word ซึ่งก็คือ address ของ S จาก ตัวหนังสือ "String" นั่นเอง
ต่อไปเมื่อต้องการเปลี่ยนค่า string จะทำอย่างไร ในที่นี้ จะเปลี่ยนค่า string ได้จะต้องไม่ declare เป็น const char เท่านั้น ต้องเป็น char เฉย ๆ และทำได้โดยการใช้ function strcpy หรือ strncpy ของ header <string.h> เท่านั้น เช่น ถ้า declare char array char word[7] = "String"; สามารถเปลี่ยนเป็น strcpy( word, "Strong"); หรือ strncpy( word, "Strong", 6); แบบที่สอง ที่กำหนดจำนวนตัวหนังสือที่เปลี่ยน อย่าไปรวม null character ด้วยเพราะยังไงมันต้องจบ ด้วยตัวนี้อยู่แล้ว ทำไปก็ไร้ความหมาย แล้ว strncpy คือการทับไปเลย เช่นถ้าเราทำแบบนี้ strncpy( word, "Star", 4); เท่ากับ Star ทับ String ไม่ใช่ Star ทับแค่ Stri เท่านั้น เพราะจำนวน 4 บอกว่า เป็นกี่ตัวหนังสือจาก source ในที่นี้คือ "Star" ไม่ใช่ 4 ตัวหนังสือจาก destination คือ word ถ้าจะแทนที่บางตัวหนังสือ ต้องใช้การวนลูป char array เพื่อเปลี่ยนใน index ของ array ที่ต้องการ หรือถ้าต้องการแก้ตัวเดียวก็ word[3] = 'o'; index ที่ 4 ไม่ใช่ 3 นะ เพราะ index เริ่มที่ 0 0 , 1 , 2 , 3 <-- index ที่ 4 การใช้ strcpy กับ strncpy ต้องระวังอย่างมากที่จะใช้กับ destination ที่ไม่ใช่ array เพราะถ้าเป็น pointer to char จะเสี่ยงมากเนื่องจากเราอาจไม่ได้ assign address ตอน declare เช่น char * word; แบบนี้ ถ้าแบบนี้ ใช้ strcpy หรือ strncpy ก็ สามารถจะไปที่ไหนก็ได้ ไม่ดีแน่ ๆ หรือถ้า pointer to char นั้น ชี้ไปที่ "Star" แต่ไป strcpy( word, "String"); เช่นนี้ g กับ null character อาจไปทับ ข้อมูลตัวอื่นได้เลย เป็นการเสี่ยงอย่างมากเลยนะ ดังนั้นใช้กับ array ปลอดภัยที่สุดเรื่อง size
สรุปเรื่อง strcpy กับ strncpy ผิดพลาด ถ้าใช้ strcpy จะทับไปหมดจริง แต่ strncpy ทับไม่หมด ทับแค่จำนวนตัวที่เรากำหนดเท่านั้น แล้วอีกเรื่องที่พลาด ก็คือ strncpy ไม่ได้ใส่ null character ปิดตอนจบให้ หาก char array นั้นว่างเปล่าแต่แรก จำเป็นต้องปิดท้ายด้วย การ manual ใส่ null character เอาเอง เช่น ถ้าเดิม declare char word[7]; strncpy(word,"Star",4); word[5] = '\0'; ต้องทำแบบนี้ แล้ว แก้ความเข้าใจผิดจากเดิมถ้า char word[7] = "Ferror"; strncpy(word,"Terrain",3); printf("%s \n",word); จะออกมาเป็น "Terror" คือทับเท่าที่สั่ง ไม่ทับทั้งหมด เหมือน strcpy ตบท้ายด้วย strcpy แม้ว่าจะถูกทับไปแล้ว แต่หาก index ตัวใด ไม่ถูกทับไปด้วย จะยังคงอยู่ที่เดิมเช่น char word[7] = "String"; strcpy(word,"Star"); จะ printf("%s \n",word); ได้เป็น "Star" แต่จริง ๆ แล้ว เป็นการเปลี่ยนแบบนี้ 'S', 't' , 'a', 'r', '\0' , 'g', '\0' จะเห็นได้ว่า g กับ null character ของเดิมยังอยู่ ถ้าเรา printf("%c \n",word[5]); จะยังคง ได้ g ออกมา
เพิ่มเติม strncpy เดิมเข้าใจว่า ไม่เติม null character ในทุกกรณี แต่จริง ๆ มีอยู่กรณีหนึ่ง ที่จะเติมให้คือ ถ้าข้อมูล จาก source สั้นกว่า จำนวน character ที่ตั้งใจให้ copy เช่น char word[7] = "String"; strncpy(word,"Star", 5 ); ถ้าแบบนี้ ตัวที่ 5 ที่ copy จะเป็น null character ดังนั้นสรุปได้ว่า อยู่ที่ n character ที่จะใส่ลงไป ถ้ามากกว่า source จะเติม null แต่ถ้าไม่มากกว่า (พอดี หรือ น้อยกว่า) จะไม่เติม null ต้องเติมเอาเอง ถ้า target เดิมไม่มีข้อมูลอยู่
วันนี้มีเรื่องให้ปวดหัว short ครับ data type short ย่อมาจาก short int คือ 16 bit integer นั่นเองวันนี้ผมพบหายนะ มาก ๆ short flag; เพราะดันไป scanf("%d",&flag); เท่านั้นเอง หายนะบังเกิดกับ ข้อมูลตัวอื่นของผมทันที มันไปทับ char array ของผมข้อมูลหายวับไปกับตา ต้องทำงี้ครับ scanf("%hd",&flag); ต้องใส่ h เท่านั้นเองครับ เพราะ d มันใช้กับ 4 byte ไม่ใช่ 2 byte อย่าง short ครับ มันถึงขนาด ก่อกวนข้อมูลตัวอื่นทันที
วันนี้ ได้ศึกษาเกี่ยวกับ การต่อ string จากการทำแบบฝึกหัดอันนี้ Code: #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> char * sarr(int len,char * tem); void cleart(char * tem); int main(int argc, char *argv[]) { char ** sar; char tem[50]; char one[2]; int len,num,i; one[0] = '\0'; one[1] = '\0'; i = 0; cleart(tem); printf("How many word do you wish to enter? "); scanf("%d",&num); sar = (char**)malloc(num * sizeof(char*)); printf("enter %d words now:\n",num); getchar();//for previous new line while(i < num) { one[0] = getchar(); if(!isspace(one[0])) strcat(tem,one); else{ len = strlen(tem); //printf("%s %d",tem,i); sar[i] = sarr(len,tem); i++; cleart(tem); } } //printf("\n"); for(i = 0;i < num; i++) printf("%s\n",sar[i]); return 0; } char * sarr(int len,char * tem) { static char * res; res = (char*)malloc((len+1) * sizeof(char)); strcpy(res,tem); return res; } void cleart(char * tem) { int i; for(i = 0; i < 50; i++) tem[i] = '\0'; } ข้อแรกคือ การทำ strcat หรือการต่อ string จำเป็นต้องให้ ตัวต้นฉบับที่ใช้ต่อ เป็น array of char ผมใช้เป็น pointer to char แล้วไม่เวิร์ค เพราะแรกสุดเลย ต้องให้ string ต้นฉบับไม่มีอักษรใด ๆ เลย การทำให้เป็น pointer to char เช่น char * tem = ""; นำมาต่อด้วย string ตัวอื่นไม่ได้เลย ต้องใช้เป็น char array แล้ว clear array นี้ให้ว่างเปล่า ด้วยการใช้ loop แล้ว assign null character เข้าไป ส่วน string(ตัวแทน 1 ตัวอักษร) ที่จะเติมเข้ามา ก็จำเป็นต้อง clear ด้วย null character ก่อนที่จะ assing ตัวอักษรใด ๆ เข้าไป และกรณีการใช้ malloc เพื่อ allocate พื้นที่บรรจุตัวอักษร ก็จำเป็นต้องเพิ่มเข้าไปอีกหนึ่งตัว เผื่อ null character ของ คำ ๆ นั้นเสมอ
Code: #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX 41 int * rankF(FILE * fs); void numW(FILE * fs,int * num); int main(int argc, char *argv[]) { FILE *fp; char words[MAX]; int * ran; if ((fp = fopen("wordy", "a+")) == NULL) { fprintf(stdout,"Can't open \"werdy\" file.\n"); exit(EXIT_FAILURE); } puts("Enter words to add to the file; press the #"); puts("key at the beginning of a line to terminate."); while ((fscanf(stdin,"%40s", words) == 1) && (words[0] != '#')) { rewind(fp); ran = rankF(fp); rewind(fp); numW(fp,ran); putc(124,fp); fprintf(fp, "%s\n", words); } puts("File contents:"); rewind(fp); /* go back to beginning of file */ while (fscanf(fp,"%s",words) == 1) puts(words); puts("Done!"); if (fclose(fp) != 0) fprintf(stderr,"Error closing file\n"); return 0; } int * rankF(FILE * fs) { int cou,ch,tem,flag,i,pas; static int num[4] = {0,0,0,0};// suit 1-999; //num[3] indicate number of digit cou = 0;flag = 0;i = 0;pas = 0; while((ch = getc(fs)) != EOF) { tem = ch - 48; if((tem >= 0) && (tem < 10)) {//tem is 0-9 //printf("get in tem is %d\n",tem); if((i < 3) && (flag == 0) && (pas == 0)) {// if pas = 1 stop collect to array num[i] = tem; i++; num[3] = num[3]+1; }else if((i == 0) && (flag == 1) && (pas == 0)) {// start first digit of new word //printf("on we\n"); num[3] = 1; num[i] = tem; i++;flag = 0; } }else{//other char not 1-9 if(ch == '|') {//pas is indicate past vertical bar //and reset i to zero pas = 1;i = 0;flag = 1; }else{//other char if(pas == 1) {//first char after vertical bar pas = 0; } } } cou++; } if (cou == 0) {// new file without content num[0] = 1; } printf("num[3] = %d\n",num[3]); if((num[num[3]-1])== 9 &&(num[3]< 3)) {//1-98 if(num[3]==1) {//case 9 num[1] = 0; num[0] = 1; num[3] = 2; }else{//num[3] = 2 num[1] = 0; num[0] = num[0]+1; } }else if((num[num[3]-1])!= 9) {//case 1-999 num[num[3]-1] = num[num[3]-1]+1; }else if((num[num[3]-1])== 9 &&(num[3]>= 2) && ((num[num[3]-2])== 9)) {// 2 last digit = 99 if(num[3]== 2) { num[2] = 0; num[1] = 0; num[0] = 1; num[3] = 3; }else if(num[3]== 3) { num[2] = 0; num[1] = 0; num[0] = num[0]+1; } } return num; } void numW(FILE * fs,int * num) { int i; for(i = 0; i < 4; i++) printf("%d ",num[i]); printf("\n"); if(num[3] == 0) {//first rank putc((num[0]+48),fs); }else{ for(i = 0; i < num[3]; i++) { putc((num[i]+48),fs); } } } การตรวจสอบ file pointer ทุกครั้ง จะมีการเปลี่ยนแปลง ตำแหน่งของ pointer ที่ชี้ไปที่จุดใด ๆ ใน file ถ้าจำเป็นต้องเขียน หรือ ตรวจสอบครั้งใหม่ ต้องทำการ reset ตำแหน่งของ file ใหม่ทุกครั้ง ด้วยคำสั่ง rewind เช่นใน code นี้มีการเปลี่ยนตำแหน่งถึงสองครั้ง คือตอนใช้ function หาตำแหน่งของ เลขลำดับคำใน file และตอนที่ให้เขียนคำใหม่ลงไปใน file คำสั่ง rewind จึงต้อง เกิดขึ้นตามการใช้คำสั่งนั้น ๆ เพื่อสามารถรองรับการทำงาน ครั้งใหม่ได้
ขอบันทึกคร่าว ๆ เกี่ยวกับ พื้นฐาน ของ การสร้าง window ขึ้นมาหนึ่งอัน โดยการใช้ window api นะครับ 1. สร้าง windowclass 1 class สามารถใช้ได้ กับ window ที่จะปรากฏในโปรแกรม หลาย ๆ window ตัว properties ที่สำคัญมากของ windowclass มีสองตัวคือ 1.1 window procedure ซึ่งจะทำการรับ pointer to function ของ function ที่จะกำหนด ความเป็นไปของ window หนึ่งอัน เมื่อรับ message จาก Windows system 1.2 class name ซึ่งมาจาก variable ที่เป็น string (char array ที่จบท้ายด้วย null character) ตัวนี้ สำคัญมากเพราะจะเป็นตัวที่ program ใช้สื่อสารกับ handle ของ window นั้น ๆ หลังจากที่ ทำการ create window ขึ้นมาแล้ว 2. เมื่อสร้าง class เสร็จแล้ว ให้ทำการ register class เพื่อบอกกับโปรแกรม ว่าจะใช้ class ใด ในการ create window ในชั้นนี้ ควรทำการทดสอบด้วย if clause เพื่อให้แน่ใจว่า การ register นั้นเสร็จสมบูรณ์ 3. ทำการ create window โดยใช้ class name ที่กำหนดจากข้อ 1. และทำการกำหนดค่า จุดพิกัด ความกว้างยาว ชื่อที่ปรากฏบน title bar style ของ window นั้น ๆ รวมถึง handle ของ parent window handle ของ menu หรือ parameters อื่น ๆ ถ้ามี ผลที่ได้ จะ return handle ของ window นั้น ๆ ซึ่งสำคัญมาก ๆ 4. ทำการ show window ซึ่งจะต้องใช้ handle ของ window ที่ได้จาก ข้อ 3. และข้อมูลเกี่ยวกับ สภาพของ window ที่จะได้รับ จาก ตัวโปรแกรมหลัก ( WinMain) หรืออาจกำหนดสภาพของ window เอง โดยทั่วไป จะเป็นขนาดธรรมดา (ไม่เต็มจอ) ถ้าไม่ก็เป็น เต็มจอ หรือ ซ่อนอยู่ใน task bar ซึ่งขั้นตอนนี้ client area หรือ พื้นที่ ๆ ถูกใช้โดย window นั้น ๆ จะถูกทำการ เตรียมพร้อม ด้วย background brush ซึ่งเป็น propeties ตัวหนึ่ง ที่เรากำหนดไว้ ใน window class 5. ทำการ update window ซึ่งจะต้องใช้ handle ของ window อีกเช่นกัน ขั้นตอนนี้ เป็นขั้นตอน ลงพื้นสี สร้างส่วนประกอบต่าง ๆ ด้วย window procedure ที่เป็น propeties ตัวหนึ่งของ window class จากข้อ 1.1 โดย function update window จะส่ง wm_paint message ไปให้ window procedure ซึ่งจะทำการ จัดการเกี่ยวกับ window ในส่วน block ของ code ที่เป็น case wm_paint หนึ่งใน switch case ของ procedure นั่นเอง
6. เข้ากระบวนการ loop ของการรับส่ง message เนื่องจาก Windows system จะทำการตอบรับ event ต่าง ๆ ที่ user ทำลงไป ไม่ว่าจะการใช้ keyboard หรือ mouse การกระทำแต่ละอย่าง จะถูก Windows system บรรจุไว้ใน message que เพื่อให้ โปรแกรมที่เป็นประเภท WinMain รับไป เพื่อให้ มีการตอบสนองกับ window ที่อยู่ในโปรแกรมนั้น ๆ 6.1 เรียกใช้ Get message function เพื่อรับ message จาก message que โดยจะบรรจุ message นั้นไว้ใน structure msg ซึ่งจะมี variable ที่สำคัญอย่าง handle ของ window จุดพิกัด และ เวลา ที่เกิด event ประเภทของ event และ parameter ของ event ซึ่งจะขึ้นกับ ประเภทของ event 6.2 เมื่อได้ message มาแล้ว หนึ่งอัน ก็ทำการ เรียก function translate message ในส่วนของ message ที่เป็น event ที่เกิดจาก keyboard ให้ function ทำการแปล ความหมายของ event นั้น 6.3 ทำการส่งต่อ message ไปยัง window procedure ด้วย function dispatch message แล้วให้ window procedure เลือกใช้ case จาก switch case ของมัน เพื่อให้เหมาะกับ ประเภทของ message 6.4 เมื่อ control วิ่งกลับมาจาก window procedure หลังจากกระทำการตอบสนองกับ event ที่ได้รับจาก message เรียบร้อย ก็เข้าสู่ การดำเนินการของ loop ในการเรียกรับ message จาก message que ต่อ ๆ ไป จนกว่าจะได้รับ คำสั่ง ประเภท wm_quit ซึ่งจะจบกระบวนการของ loop (การ test ของ while loop ด้วย function Get message คืนค่า 0)
ความสัมพันธ์ ของ องค์ประกอบใน window api 1 WinMain โปรแกรม สามารถมี 1 window class 1 window class มี 1 window procedure 1 window class สามารถมีหลาย window แต่ละ window มี 1 handle ของ window เวลาที่เรา Get message Windows system จะรับรู้ว่า message นี้เกิดจาก window ตัวไหน และเมื่อทำการ dispatch message ไปให้ window procedure ใน message นั้น ๆ ซึ่งจะบรรจุ handle ของ window ที่เกิด event ไปให้ window procedure ด้วย ซึ่งก็หมายความว่า window procedure เดียว สามารถ รองรับ event ที่เกิดจาก หลาย window ได้นั่นเอง
นอกจาก window procedure จะถูกเรียก ด้วย function dispatch message แล้ว ยังสามารถถูกเรียกได้ ในหลายกรณี 1. ถูกเรียกเมื่อ program WinMain เรียก function createwindow ซึ่งในชั้นนี้ จะมีการส่ง message จาก Windows system โดยตรงคือ wm_create 2. และเมื่อมีการเรียก function show window ก็จะส่ง message wm_size และ wm_showwindow 3. ต่อมาเมื่อเรียก function update window ก็จะส่ง message wm_paint ไปให้ 4. นอกจากนี้ ตัว window procedure ยังสามารถส่ง message ให้ตัวเองได้อีกด้วย เรียกว่าการทำ recursive function เช่น 4.1 เมื่อมีการเรียก function update window ภายใน window procedure ก็จะทำการ recursive ส่ง message wm_paint ไปให้ window procedure "ชั้นใน" กระทำการ เมื่อเสร็จจาก window procedure ชั้นในแล้ว ก็จะกลับมาที่ update window เพื่อส่งต่อไปยัง window procedure ชั้นนอก กระทำการต่อไป หรือ 4.2 กรณี ที่เราตั้ง defwindowproc ซึ่งเป็นการตั้งค่า return ของ window procedure ในกรณีที่เรา ไม่ได้ตั้ง switch case ให้ process กับทุก ๆ ประเภทของ message defwindowproc จะรับ message ที่เราไม่ได้เตรียม process ไป process เสียเอง และ defwindowproc ซึ่งเป็น function จัดการ message เช่นเดียว กับ window procedure ก็อาจมีการสร้าง message ใหม่ เพื่อส่งต่อ ไปยัง window procedure "ชั้นใน" เพื่อให้จัดการต่อ และหาก window procedure ชั้นใน ไม่ได้มีการเตรียมรับ message ประเภทนี้อีก ก็จะส่งต่อไปให้ั defwindowproc "ชั้นใน" จัดการอีก จะ recursive เช่นนี้ จนกว่า window procedure ชั้นในสุด จะมี case message ที่ทำการรองรับ และเมื่อชั้นในสุด ดำเนินการเสร็จ ก็จะคืน control กลับมาทีละขั้้น จนกว่าจะถึง window procedure ชั้นนอกสุด แล้วก็จะกลับไปที่ loop get message ต่อไป
ท่าน sugit น่าจะขอ งบบูรณาการเชิงยุทธศาสตร์ ในยุทธศาสตร์ที่ 2 การสร้างความสามารถในการแข่งขัน เรื่อง การพัฒนาเศรษฐกิจดิจิทัล ได้นะครับ
วันนี้ได้ รับรู้เหตุผลที่ีทำไม static int ถึงมีค่า default เป็น 0 แล้ว ซึ่งปกติ ค่า default ของ int จะเป็นอะไรก็ได้ (undefined) เพราะว่า static variable จะต้องคงค่าของมัน เพื่อให้สามารถใช้นอก function ได้ ดังนั้นสถานะของมัน ตอนเริ่มต้น ต้อง free คือไม่ไปเกาะอยู่กับค่าใด ๆ โดยปกติ จึงเลือกที่จะใช้ค่า 0 เป็นค่าเริ่มต้น เช่นเดียวกับ static int * ptr ก็ต้องเริ่มต้น ที่ค่า null เช่นเดียวกัน เพื่อที่จะสามารถ initialize ในค่าใด ๆ ก็ได้ เรื่องแถม low word กับ high word ที่มักใช้ในค่า wparam หรือ lparam ของ message low word คือ ค่า 16 bit แรกของ dword (bit ที่ 0 ถึง 15) ซึ่งเป็น 32 bit integer และ high word คือค่า 16 bit หลัง ซึ่งเรามักจะต้อง ทำการ shift right 16 ( >> 16) เพื่อที่จะได้ค่า high word จากค่าที่มาจาก wparam หรือ lparam เช่น ค่า x จาก low word และ y จาก high word ของ lparam จาก message wm_size (กรณี มีการเปลี่ยนขนาดของ window) หรือ constant sb_thumbtrack จาก low word และ position ของ thumb จาก high word ของ wparam จาก message wm_vscroll หรือ wm_hscroll (กรณี มีการใช้ mouse จิ้มไปที่ thumb หรือแท่งสำหรับเลื่อนขึ้นลง หรือ ซ้ายขวา ใน scroll bar)