分支與循環
這章節我們會講到以下幾個知識:
-
分支語句
if
switch
-
循環語句
while
循環
-for
循環
-do...while
循環
-
goto語句
還是那句話,我只是一個剛入門的小菜鳥,哪有有講的不好,還望大家海涵。
先講一個知識點,在C語言中什么是語句呢?C語言中由一個分號;
隔開的就是一條語句。比如:
#include <stdio.h>
int main ()
{
1+2;//這是一條語句
printf("Hello\n");//這也是一條語句
;//這也是一條語句
return 0;
}
在C語言中所有語句都分為三大類型:
- 順序結構
- 選擇結構
- 循環結構
順序結構很簡單,程序從上至下地執行,中間無任何判斷和跳轉。流程示意圖:
下面的我就沒繼續畫了,大家懂就可以了。而下面重點要講的是分支與循環結構。
分支語句(選擇語句)
分支語句也稱選擇語句,什么叫選擇呢?就好比在大學里,如果好好學習,校招時就拿一個好offer。如果不好好學習,那就回家種田吧。而學不學呢就是一種選擇。
我畫的圖描述的清楚嗎?各位客官?分支語句也分為以下幾種:
if
語句switch
語句
if語句
if語句,if是啥意思?如果的意思。那相對應的是不是還有else否則的意思?那我們看看if語句在C語言中是怎樣描述的呢?
語法形式
if(表達式)
語句;
if(表達式)
語句1;
else
語句2;
//多分支
if(表達式)
語句1;
else if(表達式)
語句2;
//
....
else
語句3;
解釋:就是如果表達式結構為真就執行語句1反之執行語句2,if(表達式)
表達式為真就執行if
后面的語句,如果為假就執行else
后的語句。那有人就問了在C語言中怎樣描述真假的呢?
在C語言中 0代表假,非0代表真。
舉個例子吧,如果if(1>2)
,這個表達式的結果是什么?1會大於2嗎?顯然是不會的所以這個表達式的結果為假也就是0。
用代碼形式來講吧:
#include <stdio.h>
int main()
{
int age = 9;
if(age<18)
{
printf("未成年\n");
}
return 0;
}
//代碼2
int main()
{
int age = 18;
if(age>=18)
{
printf("成年人\n");
}
else
{
printf("未成年\n");
}
return 0;
}
//代碼3
int main()
{
int age = 25;
if(age<18)
{
printf("未成年\n");
}
else if(age>=18&&age<30)
{
printf("青年");
}
else if(age>=30&&age<55)
{
printf("中年\n");
}
else
{
printf("老年人");
}
return 0;
}
看看效果吧:
效果果然跟預想的是一樣的,有人有點好奇為啥要加大括號呢?我們看看下面:
我們看到報錯了,為啥呢?那為啥下面沒錯呢?
if語句默認只能控制一條語句所以程序報錯。因為在C語言中,if和else不加括號默認控制一條語句,我們可以試試:
看到雖然沒有報錯,但是它多打印了一條語句,再換一個:
可以知道確實if語句和else語句默認只能控制一條語句。我們可以加上大括號來防止這些錯誤。
接着我們看代碼3的運行效果:
年齡大家可以自行修改,或者加入一條輸入語句,自己輸入想要判斷的年齡,代碼3是屬於if語句中的多分支語句,也就是有多重選擇,如果怎么怎么樣會怎么怎么樣,又如果怎么怎么樣又會怎么怎么樣,..........最后否則怎么樣。這是屬於if語句的知識點。
我們檢測一下:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if(a==1)
if(b==2)
printf("hello\n");
else
printf("heihei\n");
return 0;
}
想想結果是啥?是hello還是heihei呢?咱們看看運行效果:
啥也沒輸出,為什么呢?這涉及到一個知識點:
if
語句中if
會與最近的else
進行配對
所以這題具體的意思就是:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if(a==1)
if(b==2)
printf("hello\n");
else
printf("heihei\n");
return 0;
}
//也等於
if(a==1)
{
if(b==2)
{
printf("hello\n");
}
else
{
printf("heihei\n");
}
}
//或者想打印heihei這樣改造
int main()
{
int a = 0;
int b = 2;
if(a==1)
{
if(b==2)
{
printf("hello\n");
}
}
else
{
printf("heihei\n");
}
return 0;
}
第一個判斷是否成立呢?0會等於1嘛?肯定不會的,也沒有想匹配的else語句所以啥也不會輸出。所以大家要注意哦,書寫規范還是挺重要的,當然知識點也不能忘掉。我們再看兩段代碼:
#include <stdio.h>
int main()
{
int n = 5;
if(n==7)
{
printf("hello");
}
return 0;
}
//代碼2
int main()
{
int n = 5;
if(7==n)
{
printf("hello");
}
return 0;
}
這兩段代碼各位覺得如何?個人覺得代碼2要更好,更嚴謹,如果我們代碼1的形式少了個=號,會怎么樣?
確實是正常運行的,但是n的值被改變了,這可不是我們 想要的結果呀?我們換下:
如果我們換一種寫法,就直接報錯了,所以這種寫法還是嚴謹的。邏輯更加清晰,不容易出錯。
練習:
- 判斷一個數是否為奇數
- 輸出1~100之間的奇數
#include <stdio.h>
int main()
{
int n = 5;
if(n%2!=)
{
printf("奇數\n");
}
else
{
printf("偶數\n");
}
return 0;
}
//2
int main()
{
int i = 1;
while(i<=100)
{
if(i%2!=0)
{
printf("%d ",i);
}
i++;
}
}
Switch語句
我們再講分支語句中另外一大分支語句 -- Switch語句。Switch語句用於多分支的情況。比如說輸入1~7中的任意數字,然后輸出相對應的日期。
輸入1,輸出星期一
輸入2,輸出星期二
.....
輸入7,輸出星期日
雖然我們使用if..else if ...else if....else
的形式可以完成要求,但是太復雜了,代碼會過於冗余也就是太長了,這個時候我們就可以用Switch語句。
switch (整型表達式)
{
case 整型常量:
語句;
break;
case 整型常量2:
語句;
break;
//.....
default:
語句;
break;
}
我們用上面的例子:
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d",&day);
switch(day)
{
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期日\n");
break;
default:
printf("輸入錯誤\n");
break;
}
return 0;
}
效果如下:
這里用英語的形式是因為鄙人正在學習英語哈,勿怪勿怪。
看了效果后,帶大家理解理解這玩意,Switch(整型表達式)這里只能是整型或者字符型,因為字符型其實也是整型的一種嘛,然后case后面也必須跟的是整型常量值,這是規定,別問我我也不大了解。至於每條語句為啥加了break呢?break是什么意思呢?break在英語中是打破、弄壞、停止運轉的意思,那么在計算機中又是啥意思呢?在初始C語言中,我們也知道這個break是關鍵字,在C語言中break是指跳出,結束的意思。就是跳出程序的意思,我們試着把這個去掉看看:
咋一看全打印出來了,為啥呢?就是因為少了跳出的關鍵字,我們加上一個試試:
我們加上一條break語句后,確實后面的沒有打印了,可是我們輸入的是1呀,應該只會輸出一個呀?因為只有一個break語句,所以我們為了應對要求所以應該在每條case語句后加上break,這樣就可得到之前的效果。注意看我還輸入一條語句就是default語句,這個又是啥意思呢?我們看效果:
輸出的竟然是輸入錯誤,也確實,我們生活中也沒有星期八的存在呀,default是什么意思呢?默認、違約的意思,同樣的在C語言中,default也是默認的意思,就是除了我們給出的幾個常量值,如果我輸入的值不在這幾個之內,那么我們就可以用這個語句提醒用戶輸入是錯誤的。這是一個好的編程習慣,這樣我們可以總結出幾個編程習慣:
- 在case 語句的后面加上一條 break語句。
- 按要求來給每個case語句加break語句。
- 在每個 switch 語句中都放一條default子句是個好習慣,甚至可以在后邊再加一個 break 。
break語句可以把語句列表划分成不同的部分。比如1 ~ 5都是工作日,6 ~ 7是休息日。
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d",&day);
switch(day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6:
case 7:
printf("playday\n");
break;
default:
break;
}
return 0;
}
我們看看效果:
因為break把1 ~ 5分為一部分,無論是輸入1還是其他1~5之間的任何數輸出都是weekday,同樣的把6 ~7也分為一部分。
下面我們來做一道Switch嵌套Switch的練習:
#include <stdio.h>
int main()
{
int n = 1;
int m = 2;
switch(n)
{
case 1:
m++;
case 2:
n++;
case 3:
switch(n)
{
case 1:
n++;
case 2:
m++;
n++;
break;
}
case 4:
m++;
break;
default:
break;
}
printf("m = %d, n = %d",m,n);
return 0;
}
大家思考一下是多少呢?
大家看到m=5,n=3
,為什么呢?解析:
int n = 1; int m = 2; switch(1) { case 1://因為n是1所以執行case1 m++;//m=m+1 = 2+1 = 3 case 2: n++;//n=2 case 3://因為上局case語句沒有break語句所以會繼續執行下面語句 switch(2)//n=2 { case 1: n++; case 2://因為n=2所以執行的是這條語句 m++;//m=3+1=4 n++;//n=2+1=3 break;//遇到跳出語句了所以跳出這個Switch語句 } case 4: m++;//接着上面的數據 m=4+1=5 n = 3 break;//遇到跳出語句所以跳出整個Switch語句后面的代碼不再執行 }
循環語句
循環語句,也就是重復的做某件事情,比如說我需要打印100行“Hello World”,雖然說可以用100條printf
函數打印出來,但是也需要太長的代碼了,所以我們采用循環語句來做。咱們看看流程圖:
在循環語句中又分為3中類型:
- while
- for
- do...while
while循環
C語言中while循環的語法規定:
//while語法結構
while(表達式)
{
循環語句;//循環體
}
int i = 0;//初始化
while(i<10)//條件判斷
{
i++;//循環變量調整
}
我們用流程圖來說明while循環:
畫的不是很好大家諒解諒解。這大概就是while循環的流程圖了。我們來看看實例:
打印1~100的數字
#include <stdio.h>
int main()
{
int i = 1;
while(i<=100)
{
printf("%d ", i);
i = i+1;
}
return 0;
}
效果如下:
循環條件如果恆為真那就死循環了。在while循環也是可以使用break語句跳出循環的,比如說我遇到了20就退出循環:
可以看到確實到了20就已經退出了循環,那我只想不要20這個數字,繼續打印后面的呢?請看下面:
可以看到確實跳過了20直接打印后面的數字了。可是數字1也沒有打印出來,原因是i的值在進入循環后就發生了自增,所以直接跳過了數字1,如果想得到1也很簡單,我們把i的值初始化為0即可,通過觀察break和continue語句的作用,我們得出結論:
在循環中只要遇到break,就停止后期的所有的循環,直接終止循環 。while中的break是用於永久終止循環的 。
continue是用於終止本次循環的,也就是本次循環中continue后邊的代碼不會再執行,而是直接跳轉到while語句的判斷部分。進行下一次循環的入口判斷。
注意:
while循環嵌套continue語句時注意循環變量的增值表達式的位置,也就是上面的
i++;
我們把循環變量的增值表達式放到下面來看看:
20雖然沒有打印,但是后面的21也沒有打印是因為,循環變量的增值表達式放在后面進行的,一旦i等於20,就直接跳過本次循環了,后面的語句就不會執行,所以i==20不會被改變的。注意這一點。
咱們看幾道練習題:
#include <stdio.h>
int main()
{
int c = 0;
while((c=getchar())!=EOF)
{
putchar(c);
}
return 0;
}
//2
int main()
{
int c=0;
while((c=getchar())!=EOF)
{
if(c<'0'||c>'9')
{
continue;
}
putchar(c);
}
}
大家看到這兩段代碼是不是很陌生?getchar
是是啥?getchar
是獲取字符的意思,也可理解為輸入字符的意思,跟輸入語句有點類似,那與之相對應的輸出字符就是putchar
,那EOF
又是啥呢?EOF是end of file的縮寫,也就是文件結束標志,跟字符串的‘\0’
有點相似。我們看它是怎么定義的:
使用define
定義的,實際上是-1。那整段代碼是什么意思呢?getchar讀取失敗的時候會返回EOF,如果讀取字符不等於EOF就進入循環,然后打印字符。
可以看見確實輸出了我們輸入的字符,那我們要怎樣才能結束循環能?Ctrl+Z就可以結束了。那getchar
是怎樣讀取字符的呢?是直接從鍵盤上讀取嗎?其實並不是,在getchar和鍵盤之間有一個緩沖區,程序剛開始,緩沖區是空的,需要輸入數據到緩沖區,比如我們輸入字符A然后回車,其實回車也算字符,回車也就是\n
,所以會把字符A和\n
一起放進緩沖區,然后getchar去讀取字符,讀取字符A放到變量c,然后在屏幕上打印字符A,這時緩沖區只剩下\n
,然后getchar繼續讀取,讀取到\n
就換行了,然后此時緩沖區就空了,需要繼續輸入字符,這也是后面光標在閃的原因。
那代碼2又是什么意思呢?我們知道了代碼1是輸入字符然后再打印字符,那中間加了一個判斷語句,很顯然就是只會打印‘0’~'9'
判斷條件是如果c小於字符0或大於字符9就跳過本次循環,因為有continue關鍵字嘛,所以只有輸入字符0到字符9之間的任意數都可以在屏幕上打印相對應的字符。這是這兩題的解析,那我們知道這種代碼有什么用呢?我們看個實例:
#include <stdio.h>
int main()
{
char password[10]={0};
int c = 0;
printf("請輸入密碼:");
scanf("%s",password);
printf("請確認密碼(Y/N):\n");
c = getchar();
if(c == 'Y')
{
printf("確認成功\n");
}
else
{
printf("確認失敗\n");
}
return 0;
}
運行結果:
我還沒開始輸入確認與否呢,它直接來一句確認失敗。這不玩兒呢?還記得我們之前畫的那張圖嘛?解析:
程序開始我們輸入:
123456
+回車鍵,這就相當於在緩沖區放了123456
+\n
,我們輸入語句scanf會把我們輸入的字符串給拿走放到password里去,但是\n
還在緩沖區,此時getchar就會去緩沖區里讀取\n
所以后面判斷條件是假的。
那我們該咋辦呢?
思路:既然緩沖區還有\n
那我們再用一個getchar來讀取這個字符
emmmm確實是可以,但是如果我們如果輸入其他字符串呢?比如說中間有空格鍵之類的:
這是為啥呢?不就是加了個空格嗎?這跟scanf
函數有關了,scanf
函數碰到空格就不會繼續去字符了,這樣緩沖區就還剩下空格加 123
然后getchar讀取一個字符空格,后面再判斷,判斷條件也會為假,所以程序結果如圖上所示。使用了幾次scanf語句,我來解釋一下scanf的用法:
int i = 0; scanf("%d",&i);//%d以十進制的形式輸入 &i是取i的地址 &是scanf中必須要使用的 上面代碼沒有使用是因為數組名是數組首元素的地址使用可以不加&符號。
那我們怎樣解決呢?
思路:既然getchar語句是一個一個的讀取,而且我們輸入之后都會按回車鍵(\n
),那我們可以使用循環的方式來讀取后面的字符,如果是\n
就跳出循環,然后再判斷是否需要確認密碼。我們把回車給干掉。這樣也相當清理緩沖區。
如果我們在做題的時候可能會碰到類似的題目,當我們碰到這種題的時候要記住還有個緩沖區的存在。
for循環
我們已經知道while循環了,在C語言中還有個常用的另外一個循環 ---- for循環。流程圖如圖所示:
畫的不好大家見諒哈。
首先看for循環的語法:
語法:
for(表達式1;表達式2;表達式3)
{
循環語句;
}
//表達式1為初始化部分,用於初始化循環變量的。
//表達式2為條件判斷部分,用於判斷循環時候終止。
//表達式3為調整部分,用於循環條件的調整。
用法:
#include <stdio.h>
int main()
{
int i = 0;
// 初始化 條件判斷 循環變量調整
for(i = 1; i < 11; i++)
{
printf("%d ",i);
}
return 0;
}
這段代碼會打印出什么呢?
初始化:i = 1,條件判斷:i < 11為真進入循環 循環語句: 打印1 循環變量調整: i++
i = 2 i < 11 打印2
.....
i = 10 i < 11為真 打印10 i++
i = 11 i < 11為假 不進入循環
經過分析代碼,想必大家也知道這段代碼會打印什么了吧。沒錯就是1~10。在for循環中也可以使用break
和continue
語句。作用都是一樣的。但是continue
有點不一樣:
它不會像while循環那樣會出現死循環,因為它調整部分是在外邊進行的,跳過里面的語句返回到上面的調整部分。
continue在for循環中跳過本次循環,后面的語句不執行,直接跳到調整部分。
for循環的循環控制變量
1.不可在for循環體內修改循環變量,防止for循環失控,造成死循環。
2.建議for語句循環控制變量的取值采用“前閉后開區間”寫法。
試下第一條:
這樣就造成了死循環,所以我們在使用for循環是千萬不要再循環體內改變循環變量的值。
第二點是什么意思呢?我們可以使用一個數組來說明一下:
#include <stdio.h>
int main()
{
int arr[10]={0};
int i = 0;
for(i=0;i<10;i++)//這就是前閉后開的寫法 當然也可以寫成for(i=0;i<=9;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
前閉后開,大家應該有聽說過這個數學中有學過前閉后開就是前面等於后面小於或大於的寫法,比如上面的前閉后開在數學的寫法就是[0,10),大於等於0小於10。雖然也可以寫作前閉后閉區間,但是寫成這樣的形式更直觀,一眼就能看出是循環多少次。
看下面代碼結果是什么呢?
#include <stdio.h>
//1
int main()
{
for(;;)
{
printf("hello\n");
}
return 0;
}
//2.
int main()
{
int i,j;
for(i=0,j=0;i<3&&j<5;++i,j++)//++i或者i++都可以不影響
{
printf("hello\n");
}
return 0;
}
代碼1
- 結果是死循環打印hello
- 雖然說for語句中初始化、條件判斷、調整3部分都能省略,但是判斷部分省略會導致程序死循環,因為判斷部分省略會導致判斷部分恆為真。
- 除非特殊要求,否則不要省略。
再換個代碼把上面改一下:
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
for(;i<3;i++)
{
for(;j<3;j++)
{
printf("hello\n");
}
}
return 0;
}
那這樣結果是什么呢?
為什么?
i = 0; i < 3為真 : j = 0; j < 3為真;打印hello;j++ j = 1 ; j < 3為真;打印hello; j++; j = 2;j<3為真 打印hello; j++; j = 3 ;j < 3為假 不進入循環;i++;
i = 1;i < 3為真;j = 3;j < 3為假 不進入循環。i++
....
除非要求需要,不然還是不要輕易省略for語句的三部分的任意部分。
代碼2:
- 結果只打印3次hello
- i = 0;j = 0; i小於3並且j小於5,條件為真進入循環打印hello
- i = 1;j = 1;條件依然為真 打印hello
- i = 2;j = 2;條件依然為真 打印hello
- i = 3;j = 3;條件為假,不打印 跳出循環。
- 初始化、判斷、調整這三部分可以是一條語句也可以是多條語句。
看一道習題:
//請問循環幾次?
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
for(i=0,j=0;j=0;i++,j++)
j++;
return 0;
}
循環幾次呢?其實一眼就能看出,循環0次,為什么呢?因為壓根兒就不會進入循環。條件判斷部分是賦值,把0賦給j,然后j是0,0為假所以不會進入循環。
do...while循環
前兩個循環是不是都要先判斷才能進入循環,這個循環可就不一樣了,它允許先執行然后再判斷是否還需執行,先執行再判斷。流程圖:
語法
do
{
循環語句
}while(表達式);//判斷
特點
循環至少執行一次,使用場景有限制,所以很少看見do...while的使用。
使用
打印1~10:
#include <stdio.h>
int main()
{
int i = 1;
do
{
printf("%d ",i);
i++;
}while(i<11);
return 0;
}
- 該循環無論如何一上來就會打印i,然后再接着判斷是否小於11,是的話就會接着執行循環。
再試試break和continue在do...while循環中是怎么樣的:
#include <stdio.h>
int main()
{
int i = 1;
do
{
if(5 == i)
break;
printf("%d\n", i);
i++;
}while(i<=10);
return 0;
}
//2
int main()
{
int i = 1;
do
{
if(5 == i)
continue;
printf("%d\n", i);
i++;
}while(i<=10);
return 0;
}
代碼1:
正常跳出循環。
代碼2:
死循環了。跟while循環有點類似。
break:跳出break所在的循環體,終止循環。
continue:跳過本次循環continue后面的語句,直接進入下一次循環。
練習題
- 計算 n的階乘。
- 計算 1!+2!+3!+……+10!
- 在一個有序數組中查找具體的某個數字n。 編寫int binsearch(int x, int arr[], int n); 功能:在arr[0]
<=arr[1]<=arr[2]<= ….<=arr[n-1]的數組中查找x. - 編寫代碼,演示多個字符從兩端移動,向中間匯聚。
- 編寫代碼實現,模擬用戶登錄情景,並且只能登錄三次。(只允許輸入三次密碼,如果密碼正確則提示登錄成,如果三次均輸入錯誤,則退出程序 。
計算n的階乘
階乘怎么算?階乘就是 n * n-1 * n-2 * n-3*...*1
,那不就是1乘到n嘛,代碼如下:
#include <stdio.h>
int main()
{
int i = 0;
int n = 0;
scanf("%d", &n);
int t = 1;
for (i = 1; i <= n; i++)
{
t *= i;
}
printf("%d ", t);
return 0;
}
運行結果:
思路:
階乘就是1 * 2 * ....* n。那么我們用循環,弄出1~n的數字,然后再定義一個變量t=1,然后用t去成 1 ~ n的每一個數字,從而得到最后的結果。
計算1!+....+10!
計算1!+~10!,我們之間有計算了n的階乘,那我們在計算n的階乘之上再嵌套一層循環是不是就可以解決了?
#include <stdio.h>
int main()
{
int i = 0;
int t = 1;
int sum = 0;
for(i=1;i<=10;i++)
{
int j = 0;
for(j=1;j<=i;j++)
{
t *= j;
}
sum+=t;
}
printf("%d",sum);
return 0;
}
思路:
要求1 ~ 10的階乘和,那么算出每個數的階乘然后再相加在一起就可以了,首先一層循環控制1 ~ 10的數字,這下數字出來了,然后再定義一個循環,循環的初始化為1,第二個循環用來計算階乘,定義一個變量t,賦值為1,這樣每次相乘都是1去乘j的值,不會出現像計算完2的階乘后,用2的階乘的值再去計算3的階乘,這樣會出錯的,所以在循環內部定義變量t,這樣階乘后在相加在一起,就可以得到1的階乘加到10的階乘了。
這種求法效率比較低,我們可以優化一下它
4! = 4 * 3 * 2 * 1; = 4*3!
3!= 3 * 2 * 1;=3*2!
2!= 2 * 1;=2*1!
通過觀察我們發現n!會等於n * n-1!。那我們可以用一個循環就能搞定:
#include <stdio.h>
int main()
{
int i = 0;
int ret = 1;
int sum = 0;
for(i=1;i<=10;i++)
{
ret *= i;//ret=1*1; ret=1 * 2; ret = 2 * 3; ret = 6 * 4;..........
sum += ret;//sum=0+1;sum=1+2;sum=3+6;.....
}
printf("%d",sum);
return 0;
}
很明顯這種方法效率高。
<span id = “3">查數
在有序數列中查找一個數字,這題挺簡單,我們暴力求解就好了:
#include <stdio.h>
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,10};
int x = 6;
int sz = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for(i=0;i<sz;i++)
{
if(x==arr[i])
{
printf("找到了,下標是%d",i);
break;
}
}
if(sz==i)
{
printf("找不到\n");
}
return 0;
}
思路:
我們在一組數字中找一個數首先我們得知道這組數字有多少個,然后再一個一個的去比對,匹配成功就說明找到了,並且退出循環,如果一組數字全部比對完了都沒有找到那就是找不到了。
其實還有個更優化的算法,既然是有序的一組數字,那么我先拿這個需要被查找的數字跟最中間的那個數字去做比對,這樣就可以省掉一大半的數據去比對了,如果要查找的數字比中間那個數字要大,那左邊的一半數字可以扔掉不要了,繼續在后面的數字中尋找,然后再用這個方法去比對,最終只剩一個數字再比對,如果沒有那就找不到了。這種方法被稱為 二分查找法,寫成代碼又是怎么樣的呢?
#include <stdio.h>
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,10};
int x = 6;
int sz = sizeof(arr)/sizeof(arr[0]);
int left = 0;
int right = sz - 1;
while(left<=right)//只有坐下標小於或者是等於右下標時候循環才能進行,不然找不了
{
int mid = (left+right)/2;
if(x > arr[mid])
{
left = mid+1;
}
else if(x < arr[mid])
{
right = mid -1;
}
else
{
printf("找到了,下標為%d",mid);
break;
}
}
if(left > right)
{
printf("找不到\n");
}
return 0;
}
我們看看實際效果:
是不是就找到了呢?這種方式是不是效率更高了。其實大家也可以拿一張紙,在上面寫上一組有序的數字,然后把紙折半去找,這樣大家會有更深刻的理解。
<span id = “4">多個字符從兩端向中間匯聚
首先我們要明白題目的含義,知道是什么意思。多個字符從兩端向中間匯聚大概就是以下情況:
這樣的話要怎么做呢?我們可以定義兩組數組,一組存#########
另一組存放welcome to China!!!!
,因為我們使用的是字符串,所以需要使用字符串函數求出字符串的長度。然后使用循環,一次把兩端的字符放進##
數組里去,然后打印,然后兩端的下標一加一減,慢慢向中間靠近,就可以打印出這種效果了:
#include <stdio.h>
int main()
{
char arr1[]="####################";
char arr2[]="welcome to China!!!!";
int left = 0;
int right = strlen(arr2)-1;//strlen是求字符串長度 需要引頭文件string.h
while(left<=right)
{
arr1[left]=arr2[left];
arr1[right]=arr2[right];
printf("%s",arr1);
Sleep(1000);//打印完后停留一會,讓我們能更清楚的看是怎樣變換出來的。需要引頭文件Windows.h
system("cls");//清空屏幕
left++;
right--;
}
printf("%s",arr1);
//for循環版
for(left=0,right=strlen(arr2)-1;left<=right;left++,right--)
{
arr1[left]=arr2[left];
arr1[right]=arr2[right];
printf("%s",arr1);
Sleep(1000);//打印完后停留一會,讓我們能更清楚的看是怎樣變換出來的。需要引頭文件Windows.h
system("cls");//清空屏幕
}
return 0;
}
我把所有的都打印出來更直觀點。
模擬登錄
只允許輸入三次密碼,如果密碼正確則提示登錄成功,如果三次均輸入錯誤,則退出程序。
思路:
創建兩個字符數組,用循環加判斷結構
易錯點:字符串相比較不能直接用==,而是要用字符串比較函數strcmp。strcmp(str1,str2),返回值為0就相等。
#include <stdio.h>
int main()
{
char password[]="admin";
char str[10];
int i = 0;
for(i=0;i<3;i++)
{
printf("請輸入密碼:");
scsanf("%s",str);
if((strcmp(str,password))==0)
{
printf("密碼正確,登錄成功\n");
break;
}
else
{
printf("密碼錯誤,請重新輸入,只有3次機會哦\n");
}
}
if(3==i)
{
printf("機會用完,退出程序\n");
exit(0);//exit是退出程序語句
}
return 0;
}
提一下字符串比較函數是怎么比較的:
char str1[]=“abcde”;
char str2[]=“abcdf”
在使用strcmp在比較str1和str2的時候,從左到右依次比較每個字符,比較的是字符的ASCII碼值,前4個都是相同的都相等,但是‘e’ 和‘f’就不一樣,e的ASCII碼值是101,f的ASCII碼值是102,所以str1<str2。strcmp返回的就是小於0的值。
猜數字游戲
學了這么多知識了,我們來玩個游戲吧,猜數字。我們輸入一個數字,程序會提醒我們輸入的數字是大了還是小了,直到我們猜中這個數字。
游戲邏輯:
- 電腦自動生成1~200之間的數字。
- 猜數字
- 猜對了,游戲結束。
- 猜錯了,繼續猜直到猜對為止。
實現程序思路:
- 程序一開始首先打印一個簡單的菜單,讓用戶好選擇。
- 因為程序無論如何都會執行一次,所以用do....while循環,因為有菜單所以要有選擇,用Switch來判斷進入哪個環節
- 游戲開始后,要生成1~200之間的隨機數
- 接着才游戲,然后判斷猜的數字與隨機生成的數字是否相等,可以用個死循環,猜中了就提示游戲結束,退出至菜單頁面。
難點在生成1~200之間的隨機數上。
產生隨機數
生成隨機數的函數是rand函數,頭文件<stdlib.h>,rand函數返回值是整型。rand函數返回的是0~32767的數字,但是在調用rand函數之前需要使用srand函數設置隨機數的生成器,就是在rand函數之前需要使用srand函數,srand函數設置一個隨機的起點,需要的參數是個unsigned int無符號整型數字,而srand函數參數如果是一個定值的話,那隨機數都是一樣的,所以srand函數中要給個變得值,我們可以傳一個可變化的量 ---時間 ,時間傳進去的實際上是時間戳,怎么獲取時間戳呢?用time函數,給time函數傳個NULL空指針,time函數返回的是也是整型,而srand需要的是一個unsigned int所以強制類型轉換成unsigned int類型。time函數需要頭文件<time.h>
srand = ((unsigned int)time(NULL));
,我們整個工程中只需要設置一次隨機數就可以了。因為需要的是1~200的隨機數,所以我們讓rand生成的隨機數%200,因為%200的余數是0 ~ 199,我們加上1,就得到了1~200的隨機數了。
參考代碼:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void menu()
{
printf("******************************************\n");
printf("************ 1. play ***************\n");
printf("************ 0. exit ***************\n");
printf("******************************************\n");
}
void game()
{
int ret = rand()%200+1;
int k = 0;
while(1)
{
printf("請輸入猜的數字:");
scanf("%d",&k);
if(k<ret)
{
printf("數字小了\n");
}
else if(k > ret)
{
printf("數字大了\n");
}
else
{
printf("恭喜你猜中了\n");
break;
}
}
}
int main()
{
int input = 0;
srand((unsigned int) time(NULL));
do
{
menu();
printf("請輸入選擇:");
scanf("%d",&input);
switch(input)
{
case 1:
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("輸入錯誤\n");
}
}while(input);
return 0;
}
代碼效果:
goto語句
C語言中提供了可以隨意濫用的 goto語句和標記跳轉的標號。
從理論上 goto語句是沒有必要的,實踐中沒有goto語句也可以很容易的寫出代碼。
但是某些場合下goto語句還是用得着的,最常見的用法就是終止程序在某些深度嵌套的結構的處理過程,例如一次跳出兩層或多層循環。
這種情況使用break是達不到目的的。它只能從最內層循環退出到上一層的循環。寫個程序舉個例子:
#include <stdio.h>
int main()
{
flag:
printf("hello\n");
printf("world\n");
goto flag;
return 0;
}
執行順序是:首先進入主函數,然后兩端輸出函數,flag不用它,走到goto 語句時,它會跳轉到flag的位置開始執行,然后接着執行到goto語句,就造成了死循環。
再比如寫個關機程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
//關機命令 60秒后自動關機
system("shutdown -s -t 60");//system()執行系統命令函數 引用頭文件<stdlib.h>
char arr[20]={0};
again:
printf("請注意,你的電腦將1分鍾內關機,如果輸入:我是小豬豬,就關機");
scanf("%s",arr);
if((strcmp(arr,"我是小豬豬"))==0)
{
system("shutdown -a");
}
else
{
goto again;
}
return 0;
}
其實我們用循環也可以實現這個功能的:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
//關機命令 60秒后自動關機
system("shutdown -s -t 60");//system()執行系統命令函數 引用頭文件<stdlib.h>
char arr[20]={0};
while(1)
{
printf("請注意,你的電腦將1分鍾內關機,如果輸入:我是小豬豬,就關機");
scanf("%s",arr);
if((strcmp(arr,"我是小豬豬"))==0)
{
system("shutdown -a");
break;
}
}
return 0;
}
實際上goto語句使用的場景是:
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
…
error:
if(disaster)
其實在我們自己寫程序的時候很少會用到goto語句的,大家知道這個語句就可以了,要是真的要使用的話,查一下資料也會的。
數字炸彈游戲
我自己寫了一個數字炸彈游戲不知大家有木有玩過呀?其實就是猜數字和關機程序的結合,學完之后自己也突發奇想之前有玩過這種游戲所以嘗試一下自己寫這個游戲,參考代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void menu()
{
printf("******************************************\n");
printf("***********歡迎來到數字炸彈游戲!!*********\n");
printf("************ 1. play ***************\n");
printf("************ 0. exit ***************\n");
printf("******************************************\n");
}
void game()
{
int ret = rand() % 200 + 1;
int guess = 0;
char str[20] = { 0 };
int i = 0;
for (i = 0; i < 5; i++)
{
printf("請輸入猜的數字:\n");
scanf("%d", &guess);
if (guess > ret)
{
printf("數字大了\n");
}
else if (guess < ret)
{
printf("數字小了\n");
}
else
{
printf("恭喜你猜中了!\n");
}
}
if (5 == i)
{
printf("你輸了!作為懲罰你的電腦將在1分鍾后關機!!!\n");
system("shutdown -s -t 60");
}
printf("如果你想繼續玩耍,就輸入“我是小豬豬”。只有3次機會哦!\n");
for (i = 0; i < 3; i++)
{
printf("請輸入:\n");
scanf("%s", str);
if ((strcmp(str, "我是小豬豬")) == 0)
{
system("shutdown -a");
break;
}
}
if (3 == i)
{
printf("機會用完了哦,1分鍾后關機\n");
system("shutdown -s -t 60");
exit(0);
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("請輸入選擇:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("輸入錯誤\n");
}
} while (input);
return 0;
}
有哪些知識點沒講好的大家見諒哈,菜鳥報道哈哈哈哈。大家一起加油呀!!!謝謝大家的收看!!!阿里嘎多。
荊軻刺秦王