【C language part 3】条件&回圈

条件判断 Decision Control Statement

  • 为了应付程序可能遇到的各种状况,C 提供了 if 条件判断陈述。
  • 情况、情境、状态或条件的陈述,在程序执行时,该陈述只有正确错误成立不成立等非常明确的「正面或负面」两种状况。
  • 可能的运算结果只能有「true (真)」、「false (假)」这两种布林值(Boolean Value)

布林代数 Boolean algebra

布林代数(Boolean algebra),具有真 (true) 与**伪 (false) **两种值,可用来表示是非对错的概念。
在程序语言中,程序流程控制的语法,例如 ifforwhile 等,所使用的条件陈述都需要用布林代数来描述。而有趣的是, C 与 C++因为语言特性和发展时机的不同,对於该怎麽实现布林代数的想法也有所不同。

C89 中的布林型态

  • 虽然布林代数对於流程控制至为重要,但在 C89 中并没有替布林代数设计一个专有的资料型态。在 C89 中,布林代数是隐约地在多个不同的型态、不同的运算与不同的语法中实现。
  • C89 替所有内建的资料型态做了统一规定:非 0 为真、0 为伪
  • 此外,C89 对於关系运算子与逻辑 运算子的运算结果也做了规定。
  • 当运算结果为时会算出 int 型态的 1,当运算结果为时会算出 int 型态的 0。
  • 这造成在 C89 容易觉得布林代数就是用 int 型态来表示。
  • 不过这是个误解,下一页我们来看看例子。


上面的程序码,因为 C89 的规定,所以都会被判定为 false。不会印出任何结果。

  • 0.1 直接作为布林值判定时,因为非 0 所以为真。但如果将其值转型成 int 型态,因为值变为 0 所以判定为非。由此可知 int 型态不能完全表达布林代数的结果。
  • C89 没有替布林代数设计专用的资料型态还造成了其他问题。例如之前提到的,因为关系与逻辑运算子的运算结果是以 int 的 1 与 0 来表示,因而产生了一些结果不直观的程序码。
  • 开始,型别名字为_Bool
  • 另外,C99 为了让 C 和 C++ 更相容,增加了一个标头档案 stdbool.h。里面定义了 booltruefalse,让我们可以像C++ 一样的定义布林型别。
  • _Bool型别不需要引用其他库函式,直接就可以使用。而 bool 型别,则需要 #include <stdbool.h>

switch statement

  • switch 是 C 语言提供的另一种条件判断方式,本身只能比较数值或是字元
  • 但是,使用得当的话,或许会比 if 判断式还要来得有效率。
  • Syntax:
switch (expression)
{
    case constant1:
      statements1;
      break;
    case constant2:
      statements2
      break;
	…
    default: 
      default statements;
}

首先,会先看看 switch 括号里的 expression,取得 expression 的数值(或字元)之後,会一一与 case 设定的数字(或字元)比较,若符合,就会执行以下之 statement,直到遇到 breaks 後离开 switch 区块(block)。若都没有符合得数值(或字元),就会执行 default 里的 statementsdefault 可省略。

switch statement flow chart

范例

范例一:BMI 计算

使用者输入身高、体重,计算 BMI 值。
https://chart.googleapis.com/chart?cht=tx&chl=%5Ctext%7BBMI%7D%20%3D%20%5Cfrac%7B%5Ctext%7Bweight(kg)%7D%7D%7B%5Ctext%7Bheight(m)%7D%5E2%7D
若 BMI 小於 18.5,显示***过轻***。
若 BMI 介於 18.5 到 24 之间,显示***体重正常***。
大於 24,则显示***过胖***。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    float bmi, w, h; // declare in floating type

    printf("Enter your weight(kg) and height(cm):\n");
    scanf("%f %f", &w, &h);

    bmi = w / ((h/100)*(h/100));
    printf("BMI = %f\n\n", bmi);

    if(bmi<=18.5){
        printf("Underweight.\n");
    }else if(bmi>18.5 && bmi <= 24){
        printf("Normal!\n");
    }else{
        printf("Overweight!\n");
    }

    system("PAUSE");
    return 0;
}

范例二:生肖

已知 2021 年为牛年,设计一程序,从今年推算输入年分之生肖为何?

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int year;

    printf("Please input a year: ");
    scanf("%d", &year);

    int this_year = 2021 % 12; // 2021 % 12 为牛年

    switch((year%12)-this_year){
        case 0:
            printf("%d 年为牛年", year);
            break;
        case 1:
            printf("%d 年为虎年", year);
            break;
        case 2:
            printf("%d 年为兔年", year);
            break;
        case 3:
            printf("%d 年为龙年", year);
            break;
        case 4:
            printf("%d 年为蛇年", year);
            break;
        case 5:
            printf("%d 年为马年", year);
            break;
        case 6:
            printf("%d 年为羊年", year);
            break;
        case -5:
            printf("%d 年为猴年", year);
            break;
        case -4:
            printf("%d 年为鸡年", year);
            break;
        case -3:
            printf("%d 年为狗年", year);
            break;
        case -2:
            printf("%d 年为猪年", year);
            break;
        case -1:
            printf("%d 年为鼠年", year);
            break;
    }
    printf("\n\n");
    system("PAUSE");
    return 0;
}

范例三:计算折扣

某百货正在举行周年庆。顾客购物金额折扣如下:

  • 1,000(含) 以上:打 9 折
  • 5,000(含) 以上:打 8 折
  • 8,000(含) 以上:打 65 折
  • 10,000(含) 以上:打 3 折

请设计一程序,使用者输入购物金额,并在萤幕显示出

  1. 可享的折扣
  2. 折扣後的金额
  3. 总共省下多少钱
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int price, account;

    printf("Enter a price: ");
    scanf("%d", &price);

    if(price >= 10000){
        account = price * 30 / 100;
        printf("打 3 折\n");
    }else if(price >= 8000){
        account = price * 0.65;
        printf("打 65 折\n");
    }else if(price >= 5000){
        account = price * 0.8;
        printf("打 8 折\n");
    }else if(price >= 1000){
        account = price * 0.9;
        printf("打 9 折\n");
    }else{
        account = price;
        printf("没有折扣\n");
    }
    printf("折扣後的金额为 %d\n", account);
    printf("总共省下 %d\n\n", price-account);

    system("PAUSE");
    return 0;

}

回圈叙述句 Loop Statement

  • 在程序里,回圈(loop)是用来重复执行同一个区块(block)。
  • 会在某一个特别设定的条件下跳出回圈。
  • 在 C语言里,总共有三种不同的回圈叙述句,分别为:
    1. for loop (前测式)
    2. while loop (前测式)
    3. do…while loop (後测式)
  • 之後,我们会分别介绍这三种不同的 statement。

for loop flow chart


for loop 步骤:

  1. 执行 initialization statement,而且只会执行一次,此步骤为设定变数初值。
  2. 执行 test expression,判定变数是否为真。
  3. 如果为真 True,程序会进入回圈里的 statement,之後会执行 update statement,更新参数。
  4. 开始重复第二步骤。直到判定为否 False。
  5. 判定为否,则会跳出回圈区块,执行回圈外的 statement。
for(initialization_Statement; test_Expression; update_State){
    // statements inside the body of loop
}

while loop flow chart


while loop 执行步骤:

  1. 执行 test expression,判定变数是否为真。
  2. 如果判定为否,则跳过回圈里的 statement,直接结束回圈。
  3. 如果为真,执行回圈里的 statement。
  4. 执行完毕,跳回第一步,直到判定变数为否。
while(test_Expression){
    // the body of loop
}

do...while loop flow chart

两种不同 while 回圈

  • 这两种 while loop 差别在於前测式与後测式回圈:
    • 前测式回圈:先测试回圈变数是否符合回圈终止条件。
    • 後测式回圈:先执行回圈本体一次,再测试回圈变数是否符合终止条件。
  • 两者的差异在於 do…while 後测式回圈至少执行一次。
  • 要使用哪一种结构是看程序功能需求,如:帐号密码登入功能至少要让使用者输入一次帐号密码,再确认帐号密码是否正确,就可以使用後测式回圈结构。

ref.

范例

范例四:总和计算

请设计一程序,使用者输入一正整数,并显示 1 + 2 + … + N 的总和。
如:输入 9 ,结果为 1~9 的总和。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int n, sum=0;

    printf("Enter a number: ");
    scanf("%d", &n);

    for(int i=1; i <= n; i++){
        sum += i;
    }
    printf("Summary of 1 ~ %d = %d\n\n", n, sum);

    system("PAUSE");
    return 0;
}

范例五:猜大小

猜大小游戏(high or low),由电脑想定一个数字(1-1000),人来猜电脑想定的数。每人每次猜一个数,电脑则会回答:<答对> <太大> <太小>。
答对後在萤幕显示猜了几次才猜到正确答案。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void main()
{
    int answer, n, count = 0;

    srand( time(NULL) ); // 设定乱数种子

    answer = (rand() % 1000) + 1; // 比较常写成这种模式 (rand()%(max-min+1))+min

    /**while 写法**/
    /*while(1){ // while(1) 代表无限回圈,但是切记,一定要设停止回圈的机制
        printf("Enter your Answer: ");
        scanf("%d", &n);
        count++;

        if(n == answer){
            printf("CONGRA!\n\n");
            break;  // 跳出回圈,也就是停止无限回圈的机制
        }else if(n < answer){
            printf("Higher\n\n");
        }else{
            printf("Lower\n\n");
        }
    }*/
    /**do while 写法**/
    do{
        printf("Enter your answer: ");
        scanf("%d", &n);
        count++;

        if(n < answer){
            printf("Higher\n\n");
        }else if(n > answer){
            printf("Lower\n\n");
        }else{
            printf("Congra!\n\n");
        }
    }while(n != answer);

    printf("第 %d 次猜到结果\n", count);
}

补充:产生乱数

C 语言的 stdlib.h 函式库里提供了 rand() 函式来产生乱数。
不过因为 rand() 函式是由它上一个数值来产出下一个乱数,如果不做任何设定,初值系统都预设为 0。
因此这个时候我们可以用 srand() 函式来改变一开始的乱数值(一样定义在 stdlib.h),srand() 需要一个参数作为 seed(种子)。
通常我们会已目前的时间当作乱数种子,使用 time() 函式,而 time() 定义在 time.h 函式库里。

可以参考以下范例

#include <stdio.h>
#include <stdlib.h> //乱数相关函数
#include <time.h>

void main() // 把 main function 改成 void 就可以省掉最後的 return 0
{
    /** 第一种乱数范例 **/

    // 先设定乱数种子
    srand( time(NULL) ); //srand定义在stdlib里,用现在时间当作乱数种子

    // 产生乱数
    int x = rand();
    printf("产生乱数 (基本)\n");
    printf("x= %d\n", x);
    /* 这个范例产生的乱数是一个整数,值介於 0~RAND_MAX 之间
    不同系统定义的 RAND_MAX 有不同大小 */

    printf("======\n");
    system("PAUSE");
    printf("\n");

    // show RAND_MAX
    printf("RAND_MAX = %d\n", RAND_MAX); // RAND_MAX 32767
    /* This is completely useless to us. */

    printf("======\n");
    system("PAUSE");
    printf("\n");

    /** 第二种乱数范例 - 特定范围整数乱数 **/

    // 指定乱数范围
    int min = 1;
    int max = 100;

    // 产生 [min, max] 的整数乱数, min <= x <= max
    int y = rand() % (max - min + 1) + min;
    printf("产生特定范围整数乱数,范围:[%d,%d]\n", min, max);
    printf("y = %d\n", y);

    printf("======\n");
    system("PAUSE");
    printf("\n");

    /** 第三种乱数范例 - [0,1) 浮点数乱数, 0 <= x < 1 **/
    double z = (double) rand() / (RAND_MAX + 1.0);
    printf("产生 [0,1) 范围浮点数乱数\n");
    printf("z = %f\n", z);

    printf("======\n");
    system("PAUSE");
    printf("\n");

    /** 第四种乱数范例 - [min,max) 浮点数乱数, min <= x < max **/
    double min_float = 3.6;
    double max_float = 8.7;

    double w = (max_float-min_float) * rand() / (RAND_MAX + 1.0) + min;
    printf("产生特定范围浮点数乱数,范围:[%.2f,%.2f)\n", min_float, max_float);
    printf("w = %f\n", w);

    printf("======\n");
    system("PAUSE");
    printf("\n");
}
产生乱数 (基本)
x= 26378
======
请按任意键继续 . . .

RAND_MAX = 32767
======
请按任意键继续 . . .

产生特定范围整数乱数,范围:[1,100]
y = 84
======
请按任意键继续 . . .

产生 [0,1) 范围浮点数乱数
z = 0.412872
======
请按任意键继续 . . .

产生特定范围浮点数乱数,范围:[3.60,8.70)
w = 1.801700
======
请按任意键继续 . . .


Process returned 10 (0xA)   execution time : 4.756 s
Press any key to continue.

范例六:作图

请设计一程序,由使用者输入一整数,萤幕显示如下:

Input
4
Output
*
**
***
****
Input
5
Output
*
**
***
****
*****

以此类推

#include <stdio.h>
#include <stdlib.h>

void main()
{
    int i, j, num;
    scanf("%d", &num);
    
    printf("\n");
    for(i = 0; i < num; i++){
        for(j = 0; j <= i; j++){
            printf("*");
        }
        printf("\n");
    }
}

<<:  从听明牌,学习投资

>>:  那些被忽略但很好用的 Web API / Battery

Day29 实作信件发送功能(2)

昨天我们已经做好事前准备了,那我们今天就回到views里面,来撰写我们的程序吧! 而我们这次使用的函...

17.unity显示/隐藏物件(SetActive)

想要制作一个假背包,利用按钮显示背包,再按下按钮关闭背包。 要使用GameObject.SetAct...

[Day25] SLI , SLO , SLA

今天来介绍云端的管理,常常出现的三个名词,在先前的文章中,我应该也有使用过了一部分。这三个名词长的很...

object-fit

什麽是 object-fit object-fit 是一个 CSS 属性,用於决定一个可替换 res...

Day 28 Compose UI ModalBottomSheetLayout

今年的疫情蛮严重的,希望大家都过得安好,今天疫情已经降级, 希望疫情快点过去,能回到一些线下技术聚会...