กรณีศึกษา C programming by สุกิต

กระทู้ใน 'ห้องนั่งเล่น' โดย sugit, 10 Mar 2016

  1. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    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() ตรงนี้รองรับ
     
    Alamos และ อาวุโสโอเค ถูกใจ.
  2. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    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 เพื่อผ่านไปยังจุดสรุป ซึ่งไม่ควรจะมี
     
  3. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    จากการได้อ่านข้อมูล เพิ่มเติม

    ทำให้ผมลองมาสรุปผลใหม่ ว่า

    จากการอ่าน 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 ให้หมด
     
  4. Alamos

    Alamos อำมาตย์น้อย

    สมัคร:
    13 ต.ค. 2014
    คะแนนถูกใจ:
    7,052
    จำได้ว่าสมัยก่อน เคยเรียนเขียนภาษา c แต่ไม่เคยรันผ่านซักที ขนาดตามหนังสือทุกตัวอักษรแล้วนะ
     
  5. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    วิธี 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" นั่นเอง
     
  6. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    ต่อไปเมื่อต้องการเปลี่ยนค่า 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
     
  7. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    สรุปเรื่อง 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 ออกมา
     
    Last edited: 10 Jul 2016
  8. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    เพิ่มเติม strncpy เดิมเข้าใจว่า ไม่เติม null character ในทุกกรณี

    แต่จริง ๆ มีอยู่กรณีหนึ่ง ที่จะเติมให้คือ

    ถ้าข้อมูล จาก source สั้นกว่า จำนวน character ที่ตั้งใจให้ copy เช่น

    char word[7] = "String";

    strncpy(word,"Star", 5 );

    ถ้าแบบนี้ ตัวที่ 5 ที่ copy จะเป็น null character

    ดังนั้นสรุปได้ว่า อยู่ที่ n character ที่จะใส่ลงไป ถ้ามากกว่า source

    จะเติม null แต่ถ้าไม่มากกว่า (พอดี หรือ น้อยกว่า) จะไม่เติม null

    ต้องเติมเอาเอง ถ้า target เดิมไม่มีข้อมูลอยู่
     
  9. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    วันนี้มีเรื่องให้ปวดหัว

    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 ครับ มันถึงขนาด ก่อกวนข้อมูลตัวอื่นทันที
     
  10. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    วันนี้ ได้ศึกษาเกี่ยวกับ การต่อ 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 ของ คำ ๆ นั้นเสมอ
     
  11. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    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 จึงต้อง

    เกิดขึ้นตามการใช้คำสั่งนั้น ๆ เพื่อสามารถรองรับการทำงาน

    ครั้งใหม่ได้
     
  12. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    ขอบันทึกคร่าว ๆ เกี่ยวกับ พื้นฐาน

    ของ การสร้าง 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 นั่นเอง
     
  13. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    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)
     
  14. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    ความสัมพันธ์ ของ องค์ประกอบใน 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 ได้นั่นเอง
     
  15. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    นอกจาก 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 ต่อไป
     
  16. อาวุโสโอเค

    อาวุโสโอเค อำมาตย์น้อย

    สมัคร:
    1 ต.ค. 2014
    คะแนนถูกใจ:
    8,527
    ท่าน sugit น่าจะขอ งบบูรณาการเชิงยุทธศาสตร์ ในยุทธศาสตร์ที่ 2

    การสร้างความสามารถในการแข่งขัน เรื่อง การพัฒนาเศรษฐกิจดิจิทัล ได้นะครับ
     
  17. sugit

    sugit อำมาตย์ฝึกงาน

    สมัคร:
    10 Mar 2015
    คะแนนถูกใจ:
    1,111
    วันนี้ได้ รับรู้เหตุผลที่ีทำไม 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)
     

Share This Page