目錄
第四章:程序的循環控制
第一節:do語句
do語句
復合語句(程序塊)中的聲明
讀取一定范圍內的值
邏輯非運算符 · 德摩根定律
德摩根定律
求多個整數的和及平均值
復合賦值運算符
后置遞增運算符和后置遞減運算符
練習4-1
練習4-2
第二節:while語句
while語句
用遞減運算符簡化程序代碼
數據遞增
限定次數的循環操作
字符常量和putchar()函數
do語句和while語句
前置遞增運算符和前置遞減運算符
do語句的顯示
逆向顯示整數值
練習4-3
練習4-4
練習4-5
練習4-6
練習4-7
練習4-8
練習4-9
練習4-10
練習4-11
練習4-12
第四章:程序的循環控制
第一節:do語句
📖讀書筆記
-
do語句
C語言中提供了三種循環語句,分別是do語句、while語句、for語句,每種都有自己的使用場景,下面使用do語句編寫一個程序,來判斷輸入的數字是奇數還是偶數。
//輸入一個整數,判斷是奇數還是偶數
//這個代碼在書中的代碼基礎上做了一點點修改,比如使用更加熟悉的printf()函數來輸出
#include <stdio.h>
int main()
{int retry = 1, no = 0;//將retry初始化為1,是為了不與循環條件retry == 0沖突do{printf("請輸入一個整數\n");scanf("%d", &no);if (no % 2) //這里用來判斷奇數還是偶數printf("這個數字是奇數\n");elseprintf("這個數字是偶數\n");printf("想要重來一次嗎(Yes輸入0,NO輸入其他整數)?\n");scanf("%d", &retry);} while (retry == 0); //如果retry結果為0,表示繼續循環,這里相當于判斷條件return 0;
}
上面代碼的運行結果如下:
那么do-while循環的執行過程是怎樣的呢?輸出結果首先就讓我們“輸入一個整數”,可見并不是先判斷循環條件(因為最開始retry為1,判斷的條件為假),這就是do-while語句的特點,即無論循環條件是否為真,都會先執行一次循環,這次循環執行結束之后,再判斷循環條件,如果為真就繼續循環,為假就跳出循環。
上面的程序我們可以再次修改一下,寫成更加符合我們習慣的代碼。
//程序修改
int main()
{int num = 0;do{printf("請輸入一個整數\n");scanf("%d", &num);if (num % 2 == 1) //這里用來判斷奇數還是偶數printf("這個數字是奇數\n");elseprintf("這個數字是偶數\n");printf("繼續請輸入1,退出請輸入0\n");scanf("%d", &num);if (num == 0)break;} while (1); //將循環條件設置恒為真,此時需要搭配break語句跳出循環return 0;
}
-
復合語句(程序塊)中的聲明
在上面的代碼中,no變量只在循環內部使用,應盡量聲明在循環內部,這就是復合語句中的聲明,但如果在循環外部聲明,也沒有什么問題。
-
讀取一定范圍內的值
下面代碼應該是個“猜”拳游戲,只不過猜的不是誰贏,而是出了什么。
//“猜”拳游戲(有一定的更改)
//規定0代表石頭,1代表剪刀,2代表布
int main()
{int hand = 0;do{printf("請出拳【0-石頭、1-剪刀、2-布】\n");scanf("%d", &hand);} while (hand < 0 || hand > 2);//如果沒有0、1、2這三個值其中之一,就繼續循環//選擇了正確的出拳//下面將書中的switch語句換成了if語句,原因是if語句更加清晰,而且使用頻率也比switch高if (hand == 0)printf("石頭\n");else if (hand == 1)printf("剪刀\n");else if (hand == 2)printf("布\n");return 0;
}
上面的程序中,通過do-while循環的循環條件,實現了讀取[0, 2]范圍內的整數值。
-
邏輯非運算符 · 德摩根定律
通過邏輯非運算符(!),就可以實現真變假,假變真,比如上面程序中的“hand < 0 || hand > 2”就可以改成“!(hand >= 0 &&?hand <= 2)”。
在C語言中,0表示假,一切非0表示真,所以!a的含義是:如果a為0(假),那么!a就是非0(真);如果a為非0(真),那么!a就是0(假)。
-
德摩根定律
下面兩個表達式稱為德摩根定律:
- (x && y) == !(!x || !y),即用x && y做判斷條件,與!(!x || !y)做判斷條件相同。
- (x ||?y) == !(!x &&?!y),這兩個表達式就是通過邏輯非運算符聯系在一起的。
-
求多個整數的和及平均值
不停的輸入整數,顯示其和及其平均值,代碼如下:
//不停的輸入整數,顯示其和及其平均值(稍加修改)
int main()
{int num = 0, sum = 0, count = 0;//num表示輸入的整數,sum是和,count是數字個數float avg = 0.0;//avg是平均值do{printf("請輸入一個整數\n");scanf("%d", &num);sum += num;//求和count++;//統計個數avg = (float)sum / count;//求平均值,由于sum是整形,所以要強轉成浮點型之后計算printf("和為%d,平均值為%f\n", sum, avg);//%f用來輸出浮點數printf("繼續請輸入1,退出請輸入0\n");scanf("%d", &num);//這里借用了num變量來判斷是否繼續執行循環if (num == 0)break;} while (1);return 0;
}
-
復合賦值運算符
復合賦值運算符有:+=(加等)、-=(減等)、*=(乘等)、/=(除等)、%=(取模等)、&=(按位與等)、|=(按位或等)、^=(按位異或等)、<<=(左移等)、>>=(右移等)。
這些運算符相當于簡寫,比如“a += b”和“a = a + b”是一樣的,都是將a+b的值再賦給a。
-
后置遞增運算符和后置遞減運算符
如果是++,表示遞增運算符,--表示遞減運算符。如果寫在變量或表達式后面,就是后置遞增或遞減,寫在前面就是前置遞增或遞減。
a++;//后置++
a--;//后置--
++a;//前置++
--a;//前置--
對于后置++或--,由于運算符在后面,所以運算順序是先賦值,再++或--;如果是前置++或--,則恰好相反。當然,如果沒有賦值這一操作,前置和后置++或--其實沒什么區別。
a = (b++);//先把b的值給a,之后b的值加1,此時a的值是b,b的值是b+1
a = (--b);//先讓b減1,之后把b-1的值給a,此時a的值是b-1,b的值也是b-1//這里需要注意的是,++和--運算符是能夠改變b本身的值的,這與+和-運算符不同
a = b + 1;//a的值是b+1,b的值不變
📝課后習題
-
練習4-1
將下面代碼改成可以輸入、顯示任意次。
int main() {int num = 0;printf("請輸入一個整數\n");scanf("%d", &num);if (num == 0)printf("該整數為0\n");else if (num > 0)printf("該整數為正數\n");elseprintf("該整數為負數\n");return 0; }
??解答:
//練習4-1
int main()
{int num = 0;do{printf("請輸入一個整數\n");scanf("%d", &num);if (num == 0)printf("該整數為0\n");else if (num > 0)printf("該整數為正數\n");elseprintf("該整數為負數\n");printf("繼續請輸入1,退出請輸入0\n");scanf("%d", &num);if (num == 0)break;} while (1);return 0;
}
-
練習4-2
編寫一段程序,像下面一樣讀取兩個數的值,然后計算出它們之間所有整數的和。
請輸入兩個整數
整數 a:37
整數 b:28
大于等于28,小于等于37的所有整數的和是325
??解答:
//練習4-2
#include <stdlib.h>
int main()
{do{//由于每一次循環,都需要將sum和count置為0,所以不如放在循環內部聲明int a = 0, b = 0, sum = 0, count = 0;//sum求和,count記錄a和b之間的個數printf("請輸入兩個整數\n");scanf("%d %d", &a, &b);//這樣寫的話,scanf()函數可以讀取兩個值,中間用空格分隔//由于不知道a和b的大小關系,所以使用到絕對值函數abs(),需要包含頭文件<stdlib.h>count = abs(a - b) - 1;//計算a和b之間的數字個數(不包括a和b)//這里使用while循環求和do {sum += ((a < b ? a : b) + count);count--;} while (count > 0);sum += (a + b);//加上端點值//這樣打印是考慮到a和b的大小關系printf("大于等于%d,小于等于%d的所有整數的和是%d\n", (a < b ? a : b), (a > b ? a : b), sum);printf("繼續請輸入1,退出請輸入0\n");scanf("%d", &a);//借用a變量判斷是否繼續循環if (a == 0)break;//這里需要注意的是,借用的變量不能亂借,要借能夠重新賦值的變量,不然會造成結果出錯} while (1);return 0;
}
注意,上面的代碼在計算count變量時,如果不想使用abs()函數,可以這樣寫:
count = ((a > b ? a : b) - (a < b ? a : b)) - 1;
第二節:while語句
-
while語句
while語句再循環結構中使用的頻率很高,它幾乎與do-while語句相同,只不過要先判斷循環條件的真假,再進行循環,而do-while循環至少執行一次循環,其他地方二者都是一樣的。
下面看一個程序,從輸入的整數開始倒數到0。
//從輸入的整數開始倒數到0(有修改)
int main()
{int num = 0;printf("請輸入一個正整數\n");scanf("%d", &num);while (num >= 0)printf("%d ", num--);//先打印num的值,之后--return 0;
}
-
用遞減運算符簡化程序代碼
這個簡化后的版本就是我上面寫的代碼,也就是用“printf("%d ", num--)”代替“printf("%d ", num)和num--”。
-
數據遞增
編寫一個程序,使其從0開始遞增(減)到輸入的整數。
//從0開始遞增(減)到輸入的整數(有修改)
int main()
{int num = 0;printf("請輸入一個整數\n");scanf("%d", &num);int i = num;if (num > 0){while (i >= 0)printf("%d ", num - (i--));}else if (num < 0){while (i <= 0)printf("%d ", num - (i++));}elseprintf("%d ", num);return 0;
}
-
限定次數的循環操作
這個早就接觸過了,也就像上面循環條件中的“i >= 0”這樣,通過i的改變,導致循環次數的改變,比如i如果是10,該循環就執行11次。這個循環條件可以是變量、常量、表達式、函數調用等。
-
字符常量和putchar()函數
用英文單引號括起來的字符稱為字符常量(只能括一個字符),比如'a'、'?'、'&'等。用英文雙引號括起來的稱為字符串(無論個數多少),比如""(空字符串)、" "(空格字符串)、"abc"等。
putchar()函數是用來顯示一個字符的,也可以使用printf()函數替換。
//putchar()函數
int main()
{char a = '@';putchar('?');putchar(a);printf("%c\n", '?');//%c用來打印字符printf("%c\n", a);return 0;
}
可以看出,putchar()函數也沒有換行,個人認為printf()函數更好用一些。
-
do語句和while語句
關于兩者的對比,上面已經說過了,這里不再重復了。
-
前置遞增運算符和前置遞減運算符
上面講后置的時候已經說過了。
-
do語句的顯示
作者說do-while語句和while語句中都含有while,因此有時很難區分,其實當我們看見do的時候,找到它下面的花括號({}),之后花括號后面的第一個while就是do-while語句中的while。不好區分很大原因是代碼寫的凌亂(記住一點,do-while循環和while循環一樣,如果循環內部只有一行代碼,可以不寫{},但是那樣就不好區分,因此我都是無論do-while循環內部有多少代碼,都寫上{}加以區分)。
-
逆向顯示整數值
下面看這個程序,輸入一個非負整數,進行逆向顯示,比如輸入1963,輸出結果為3691。
//逆向顯示整數值
int main()
{int num = 0;printf("請輸入一個正整數\n");scanf("%d", &num);while (num > 0){printf("%d", num % 10);//通過模10操作,拿到num的最后一位num /= 10;//去掉num的最后一位}//模擬輸出結果,假設輸入1963//第一次循環:num == 1963, num % 10 == 3(此時輸出3), num / 10 == 196//第二次循環:num == 196, num % 10 == 6(此時輸出6), num / 10 == 19//第三次循環:num == 19, num % 10 == 9(此時輸出9), num / 10 == 1//第四次循環:num == 1, num % 10 == 1(此時輸出1), num / 10 == 0, 退出循環return 0;
}
我們可以通過模10再除等10的操作,拿到一個數字的每一位(從個位開始)。
📝課后習題
-
練習4-3
對第二節while語句中第一個代碼(從輸入的整數開始倒數到0)進行修改,當輸入的值為負數的時候不執行換行操作。
注意:由于我對代碼進行了一些修改,所以當輸入負數時,結果不進入循環,程序直接結束。因此不如將練習4-3改為輸入負數時,從該負數開始輸出到0。
??解答:
//練習4-3
int main()
{int num = 0;printf("請輸入一個整數\n");scanf("%d", &num);if (num > 0){while (num >= 0)printf("%d ", num--);}else if (num < 0){while (num <= 0)printf("%d ", num++);}elseprintf("%d ", num);return 0;
}
-
練習4-4
對練習4-3的代碼進行修改,使其遞(增)減到1。
??解答:
將while循環中的“num >= 0”和“num <= 0”改成“num >= 1”和“num <= 1”即可。
-
練習4-5
對“從0開始遞增(減)到輸入的整數”的程序進行修改,使其從1(-1)開始遞增(減)。
??解答:
//練習4-5
int main()
{int num = 0;printf("請輸入一個整數\n");scanf("%d", &num);int i = num;if (num > 0){while (i > 0)printf("%d ", num - (i--) + 1);}else if (num < 0){while (i < 0)printf("%d ", num - (i++) - 1);}elseprintf("%d ", num);return 0;
}
-
練習4-6
編寫一個程序,像下面這樣按照升序顯示出小于輸入值的所有正偶數。
請輸入一個整數:19
2 4 6 8 10 12 14 16 18
??解答:
//練習4-6
int main()
{int num = 0;printf("請輸入一個整數\n");scanf("%d", &num);int i = num;while (num > 1)printf("%d ", i - (num -= 2));return 0;
}
-
練習4-7
編寫一個程序,像下面這樣顯示出小于輸入的整數的所有2的乘方。
請輸入一個整數:19
2 4?8 16
??解答:
//練習4-7
int main()
{int num = 0, i = 1;printf("請輸入一個整數\n");scanf("%d", &num);while (num - (i * 2) > 0)printf("%d ", i *= 2);return 0;
}
-
練習4-8
編寫一個程序,使得輸入一個整數,連續顯示出該整數個'*',之后輸出換行符,當輸入的值小于1時,不輸出換行符。
??解答:
//練習4-8
int main()
{int num = 0;printf("請輸入一個整數\n");scanf("%d", &num);if (num > 0){while (num--)printf("%c", '*');printf("\n");}return 0;
}
-
練習4-9
編寫一個程序,像下面這樣交替顯示+和-,總個數等于所輸入的整數值。另外,當輸入0以下的整數時,什么也不顯示。
正整數:13
+-+-+-+-+-+-+
??解答:
//練習4-9
int main()
{int num = 0;printf("請輸入一個整數\n");scanf("%d", &num);if (num % 2 == 1){printf("+");num -= 1;}while (num > 0){printf("-+");num -= 2;}return 0;
}
-
練習4-10
編寫一個程序,像下面這樣連續顯示*,總個數等于所輸入的整數值,當輸入0以下的整數時,什么也不顯示。
正整數:3
*
*
*
??解答:
//練習4-10
int main()
{int num = 0;printf("請輸入一個整數\n");scanf("%d", &num);while ((num--) > 0)printf("*\n");return 0;
}
-
練習4-11
對上面逆向顯示整數值的代碼進行修改,使其像下面這樣,在顯示輸出結果的同時,顯示輸入的整數值。
請輸入一個正整數:1963
1963的逆向顯示的結果是3691。
??解答:
只需要在上面代碼的while循環的上一行加上“printf("%d逆向顯示的結果是", num);”即可。
-
練習4-12
編寫一個程序,像下面這樣,讀取一個正整數,顯示其位數。
請輸入一個正整數:1963
1963的位數是4。
??解答:
注意到while循環的循環次數就是num的位數,所以只要再定義一個變量count用來記錄循環次數即可。
//練習4-11
int main()
{int num = 0, count = 0;//count用來記錄循環次數printf("請輸入一個正整數\n");scanf("%d", &num);int i = num;//記錄原來的數字while (num > 0){num /= 10;//由于不需要輸出末位數,所以直接執行num/10即可count++;}printf("%d的位數是%d", i, count);return 0;
}
補充練習:
編寫一個程序,輸入一個整數,正向拿取到該整數的每一位,中間用空格分隔,比如:
請輸入一個整數:1963。
輸出結果:1 9 6 3。
??解答:
//補充練習 //由于沒有學習數組和函數遞歸,因此采用下面的方法 #include <math.h> int main() {int num = 0, count = 0;//count記錄循環次數,也就是num的位數printf("請輸入一個正整數\n");scanf("%d", &num);int i = num;//統計num的位數while (i > 0){i /= 10;count++;}while (count)printf("%d ", (num / (int)pow(10, --count)) % 10);//pow(double x, double y)是C語言的庫函數,用來計算x的y次冪,頭文件為<math.h>//pow()函數的返回值是浮點型,所以強轉為int類型//模擬輸出結果:已知num == 1963, count == 4//第一次循環,pow() == 10^3,num / 1000 == 1,1 % 10 == 1,count變成3//第二次循環,pow() == 10^2,num / 100 == 19,19 % 10 == 9,count變成2//第三次循環,pow() == 10^1,num / 10 == 196,196 % 10 == 6,count變成1//第四次循環,pow() == 10^0,num / 1 == 1963,1963 % 10 == 3,count變成0,退出循環return 0; }