C言語プログラミング(五)選択制御構造

29790 ワード

第五章選択制御構造
分治策略:タスク分解細分化
プログラム設計言語:コンピュータに高度な言語で書かれたプログラム命令を実行させるためには、これらの命令を高度な言語形式からコンピュータが理解できる機械言語形式に変換しなければならない.この変換はコンパイラによって行われる.
 
アルゴリズム:特定の問題を解決するための決定、有限、秩序化、実行可能な操作手順
 
データ構造+アルゴリズム=プログラム(この式はプロセスに面した言語のみで成り立つ)
データ構造とは、コンピュータがデータを格納、組織する方法であり、互いに1つ以上の特定の関係が存在するデータ要素の集合を指す.
アルゴリズムは操作や行為の記述であり、アルゴリズムはシステムの方法で問題を解決する戦略を記述することを表す.
 
アルゴリズムの説明:
  • 自然言語記述(分かりやすい;冗長でプログラムに直接変換しにくい)
  • フローチャートの説明(イメージが直感的で、一目瞭然で、プログラムに存在するエラーを理解しやすく、発見しやすい;占める幅が大きい)
  • NS構造化フローチャート説明
  • 疑似符号記述
  •  
    関係演算子

  • <=
  • >=
  • ==
  • !=

  • 注意:==は比較、=は付与された値であり、"=="を"="と誤記することはC言語の文法上許され、エラーは提示されないが、エラーの実行結果を招く
     
    条件式:0以外の値で「真」、0の値で「偽」
     
    ///L 5-1(シングルブランチ制御)
    #include  
    main()
    {
        int a, b, max;
        printf("Input a, b:");
        scanf("%d,%d", &a, &b);
        if (a > b)  max = a;
        if (a <= b) max = b;
        printf("max = %d
    ", max); }
    //    
    Input a, b:3,5
    max = 5

    ///L 5-2(ダブルブランチ制御)
    #include  
    main()
    {
        int a, b, max;
        printf("Input a, b:");
        scanf("%d,%d", &a, &b);
        if (a > b)  max = a;
        else    max = b;      /*    a<=b    */ printf("max = %d
    ", max); }

    ///L 5-3(条件演算子)
    #include  
    main()
    {
        int a, b, max;
        printf("Input a, b:");
        scanf("%d,%d", &a, &b);
        max = a > b ? a : b;/*                */
        printf("max = %d
    ", max); }

     
    論理的に関連する文のセットをカッコで囲んで複合文と呼びます(Verilogのbegin endに似ています)
    //L5-4
    #include  
    #include  
    #include  
    #define   EPS 1e-6
    main()
    { 
        float  a, b, c, disc, p, q;
        printf("Please enter the coefficients a,b,c:"); 
        scanf("%f,%f,%f", &a, &b, &c);    
        if (fabs(a) <= EPS)         /* a=0 ,  "      " */
        //             ,            ,                0    
        {
            printf("It is not a quadratic equation!
    "); exit(0);//exit C , , // exit() } disc = b * b - 4 * a * c; /* */ p = - b / (2 * a); q = sqrt(fabs(disc)) / (2 * a); if (fabs(disc) <= EPS) /* 0 , */ { printf("x1 = x2 = %.2f
    ", p); } else { if (disc > EPS) /* 0 , */ { printf("x1 = %.2f, x2 = %.2f
    ", p+q, p-q); } else /* 0 , */ { printf("x1 = %.2f+%.2fi, ", p, q); printf("x2 = %.2f-%.2fi
    ", p, q); } } }

     
    多重選択用switch文(Verilogのcase文と同様)
    //L5-5
    #include  
    main()
    {
        int    data1, data2;          
        char  op;                      
        printf("Please enter an expression:");
        scanf("%d%c%d", &data1, &op, &data2);  /*         */
        switch (op)                        /*                 */
        {
        case '+':                         /*      */
            printf("%d + %d = %d 
    ", data1, data2, data1 + data2); break;// switch break , case '-': /* */ printf("%d - %d = %d
    ", data1, data2, data1 - data2); break; case '*': /* */ printf("%d * %d = %d
    ", data1, data2, data1 * data2); break; case '/': /* */ if (0 == data2) /* 0 , 0 */ printf("Division by zero!
    "); else printf("%d / %d = %d
    ", data1, data2, data1 / data2); break; default: /* */ printf("Invalid operator!
    "); } }

     
    各case後定数の順序が変更された場合、プログラムの実行結果には影響しませんが、重複するcaseは発生しません.
    実行効率の観点から,発生頻度が高い場合を前面に置くのが一般的である.
     
    //L5-6
    #include  
    #include  
    main()
    {
        float  data1, data2;            
        char   op;                        
        printf("Please enter the expression:
    "); scanf("%f %c%f", &data1, &op, &data2); /* %c */ switch (op) { case '+': printf("%f + %f = %f
    ", data1, data2, data1 + data2); break; case '-': printf("%f - %f = %f
    ", data1, data2, data1 - data2); break; case '*': case 'x': case 'X': printf("%f * %f = %f
    ", data1, data2, data1 * data2); break; case '/': if (fabs(data2) <= 1e-7) /* 0 */ printf("Division by zero!
    "); else printf("%f / %f = %f
    ", data1, data2, data1 / data2); break; default: printf("Invalid operator!
    "); } }

     
    論理演算子
  • !:論理非
  • &&:論理と
  • |:論理または
  • 演算子&&||はいずれも「ショート」特性を有する
     
    プログラムテスト:
  • プログラムテストはプログラム品質を確保する有効な手段である
  • 窮尽試験、サンプリング検査
  • プログラムテストはプログラムが間違っていることを証明することしかできず、プログラムが間違っていないことを証明することはできない
  • プログラムテストの目的は、プログラム中のエラー
  • をできるだけ多く発見することである.
    テスト例:
  • ホワイトボックステスト(構造テスト):テストプログラムの内部構造は既知であり、プログラムの内部論理設計によるテスト例
  • ブラックボックステスト(機能テスト):プログラム内部の論理構造と処理過程を考慮せず、プログラムの機能がその機能説明
  • に合致するかどうかを検査する.
  • 併用:限られた数の重要な経路を選択してホワイトボックステストを行い、重要な機能ニーズに対してブラックボックステスト
  • を行う.
     
    //L5-7
    #include 
    #include 
    #define EPS  1e-1
    main()
    {
        float a, b, c;
        int flag = 1;                            
        printf("Input a,b,c:");
        scanf("%f,%f,%f", &a, &b, &c);                                
        if (a+b>c && b+c>a && a+c>b)          
        {
            if (fabs(a-b)<=EPS && fabs(b-c)<=EPS && fabs(c-a)<=EPS) 
            {
                printf("  ");                  /*    */
                flag = 0;                       /*      flag 0  */
            }
            else if (fabs(a-b)<=EPS || fabs(b-c)<=EPS|| fabs(c-a)<=EPS)
            {
                printf("  ");                /*    */
                flag = 0;                       /*      flag 0  */
            }        
            if (fabs(a*a+b*b-c*c)<=EPS    || fabs(a*a+c*c-b*b)<=EPS
                || fabs(c*c+b*b-a*a)<=EPS)      
            {
                printf("  ");
                flag = 0;                 
            }
            if (flag)                       
            {
                printf("  ");
            }
            printf("   
    "); } else { printf("
    "); } }
    //    
    Input a,b,c:3,4,5
         
    Input a,b,c:4,4,5
         
    Input a,b,c:,3,4,6
         
    Input a,b,c:3,4,9
         
    Input a,b,c:10,10,14.14
           
    Input a,b,c:4,4,4      

     
     
    境界テスト:
    試験例を選択する際、合理的な入力データだけでなく、不合理な入力データや臨界点を選択し、プログラムをテストし、境界テストと呼ぶ.
    //L5-8
    #include 
    main()
    {
        int score, mark; 
        printf("Please enter score:");
        scanf("%d", &score);
        if (score<0 || score>100)
        {
            mark = -1;
        }
        else
        {
            mark = score / 10;    
        }
        switch (mark)    
        {
        case 10:
        case 9:  
            printf("%d--A
    ", score); break; case 8: printf("%d--B
    ", score); break; case 7: printf("%d--C
    ", score); break; case 6: printf("%d--D
    ", score); break; case 5: case 4: case 3: case 2: case 1: case 0: printf("%d--E
    ", score); break; default: printf("Input error!
    "); } }

    //5-9
    #include  
    main()
    {
        int a, b, max;
        printf("Input a, b:");
        scanf("%d,%d", &a, &b);
        max = a > b ? a : b;
        printf("max = %d
    ", max); }

    //5-10
    #include  
    main()
    {
        int a, b, max, ret;
        printf("Input a, b:");
        ret = scanf("%d,%d",&a, &b); /*   scanf       */
        if (ret != 2)  /*   scanf     ,                 */
        {
            printf("Input data quantity or format error!
    "); while(getchar() != '
    '); /* */ } else /* */ { max = a > b ? a : b; printf("max = %d
    ", max); } }

     
    関数scanf()はパラメータタイプマッチングチェックを行わないが、scanf()の戻り値が読み込むべきデータ項目数であるか否かをチェックすることで、入力されたデータ項目数とフォーマット(入力フォーマットエラー、不正文字の存在、データ読み取りなしなどを含む)が正しいか否かを判断することができる
     
    ビット演算子:
  • ビット演算とは、バイトまたはワード内のバイナリビットをテスト、抽出、設定またはシフトする動作
  • である.
  • その操作対象はfloat、double、long doubleなどの他のデータ型ではなくcharおよびint型
  • のみである.
     
  • ~:ビット別逆
  • <>:左シフト、右シフト(左シフトでも右シフトでも一端から移動したビットは他端に移動せず、移動したビットの情報は失われる)
  • .
  • 算術シフト
  • 論理シフト
  • &:ビット単位で、バイト内のビットをゼロにするために使用できる
  • ^:ビット別または、バイト内の位置1
  • に使用できます.
  • |:ビットまたは
  • 注意:ビットによって逆を取るのは補符号で計算されます.
    12が00000000000,1100である場合、逆符号化1111111110011、逆符号化1111111110010、原符号化10000000,1101、すなわち-13が逆符号化される
     
    //L5-11
    #include  
    main()
    {
        int  x=12, y=8;
        printf("
    %5d%5d%5d", !x, x||y, x&&y); printf("
    %5u%5d%5d", ~x, x|y, x&y); printf("
    %5d%5d%5d
    ", ~x, x|y, x&y); }
    //    
        0    1    1
    4294967283   12    8
      -13   12    8