C語言程序設計第四次作業
標簽(空格分隔): C語言
在新的一周的9:49,我們親愛的老師又把新一周的作業發表了。 〒▽〒,知道這一刻總是要到來的,但還是讓人傷心 ╥﹏╥...。
(一)改錯題
這一題讓我們判斷輸入的三個數能否構成一個三角形,可以的話就輸出周長和面積,不可以的話就輸出錯誤信息。
錯誤源代碼如下:
#include <stdio.h>
#include <math.h>
int main(void)
{
double a,b,c;
double area,perimeter,s;
printf("Enter 3 sides of the triangle: ");
scanf( "%lf%lf%lf",&a,&b,&c );
if( a + b > c || b + c > a || a + c > b)
s = ( a + b + c ) / 2;
area = sqrt( s * ( s - a ) * ( s - b ) * ( s - c );
perimeter = a + b + c;
printf("area = %.2f; perimeter = %.2f\n",area,perimeter);
else
printf("These sides do not correspond to a valid triangle\n");
return 0;
}
(第一次修改)我們首先運行一下這個源程序,錯誤信息如下:
--------------------配置: mingw5 - CUI Debug, 編譯器類型: MinGW--------------------
檢查文件依賴性...
正在編譯 E:\Begining C\測試\main.c...
[Error] E:\Begining C\測試\main.c:14: error: syntax error before ';' token(定位於第14行)
[Error] E:\Begining C\測試\main.c:18: error: syntax error before "else"(定位於第18行)
構建中止 main: 2 個錯誤, 0 個警告
我們根據編譯器的提示信息檢查第14行和第18行發現小括號的對應不正確,缺少一個“)”。再次聯想if else語句的括號可能出現問題再次檢查果然發現if( a + b > c || b + c > a || a + c > b)
后所跟語句並沒有加{}
if語句后只能跟一段語句,而”{}“是為了讓幾個語句判斷為一個語句。但不加”{}“后不僅if語句不能執行,連else也不能正確與if配對。
修改意見:將第13行至第16行包含至一個大括號中,並且在第13行分號之前加一個“)”
修改后代碼:
#include <stdio.h>
#include <math.h>
int main(void)
{
double a,b,c;
double area,perimeter,s;
printf("Enter 3 sides of the triangle: ");
scanf( "%lf%lf%lf",&a,&b,&c );
if( a + b > c || b + c > a || a + c > b)
{
s = ( a + b + c ) / 2;
area = sqrt( s * ( s - a ) * ( s - b ) * ( s - c ));
perimeter = a + b + c;
printf("area = %.2f; perimeter = %.2f\n",area,perimeter);
}
else
printf("These sides do not correspond to a valid triangle\n");
return 0;
}
(第二次修改)再次編譯發現已經編譯成功,進行案例測試的結果如下:
輸入5 5 3:
輸入1 4 1:
可以看到在輸入5 5 3時程序給出的答案正確,在輸入1 4 1這組不能構成三角形的數據時我們我們看到area的結果錯誤推斷是垃圾值,我們將定義的變量初始化檢測一下:
結果證明這並不是垃圾值,我們根據這個可以去嘗試檢查輸出語句和輸入語句甚至需要檢查我們的賦值語句。
經檢查,並未檢查出明顯錯誤。推斷是因為根號中的數不能為負數, 而要使sqrt語句中不會是負數只能完善
判斷語句。將判斷語句中的或都改為且。即將if( a + b > c || b + c > a || a + c > b)
改為 if( a + b > c && b + c > a && a + c > b)
嘗試運行結果如下:
輸入5 5 3:
輸入1 4 1:
發現程序已經可以正確識別不能構成三角形的數據說明我們的本次修改正確。
(第三次修改)我們的程序已經滿足題目所給的案例的條件。但很可能還有問題,我再次全篇檢查發現幾個小問題如下:
第14行的s = ( a + b + c ) / 2;
語句處以2不嚴謹,雖然/是二元運算符(就sizeof,+,-,!,++,--,~是一元運算符,說這有湊字嫌疑。)會自動轉化為double型但把2改為2.0或者2.都更有益於自己或者其他人的理解。
修改意見:將 s = ( a + b + c ) / 2;
改為s = ( a + b + c ) / 2.0
修改完成,最終源程序如下:
#include <stdio.h>
#include <math.h>
int main(void)
{
double a = 0.0,b = 0.0,c = 0.0;
double area = 0.0,perimeter = 0.0,s = 0.0;
printf("Enter 3 sides of the triangle: ");
scanf( "%lf%lf%lf",&a,&b,&c );
if( a + b > c && b + c > a && a + c > b)
{
s = ( a + b + c ) / 2;
area = sqrt( s * ( s - a ) * ( s - b ) * ( s - c ));
perimeter = a + b + c;
printf("area = %.2f; perimeter = %.2f\n",area,perimeter);
}
else
printf("These sides do not correspond to a valid triangle\n");
return 0;
}
最終運行結果如下:
輸入5 5 3:
輸入1 4 1:
輸入3 4 5:
到此,這個程序已經可以交貨了。(o)/
(二)學習總結
這一周我們學習的主題就是條件判斷,條件判斷所用的語句主要分為基本的if語句,擴展if語句:if-else,嵌套的if語句,多項選擇:else if語句,goto語句,按位運算符:op=。這6類,而其中4種都是需要用到if和擴展if的。而if又需要邏輯運算符和邏輯非運算符。
而關於我們對if、if-else、if連用、switch的選擇我認為主要區分以下幾點:
- 當只有一個簡單的判斷時只需要if甚至if后只有一條語句。
- 當幾個條件有比較強的聯系時一般是用if-else語句。
- 當多個條件需要判斷,但這幾個條件之間的關聯並不多時,也就是這幾個條件的判斷互不影響時,if連用的情況會更多。
- 而switch的應用范圍就比較小了,它只能判斷整數是否符合條件。但switch語句的穿透性的用法非常有趣,如果應用得當可以極大的簡化程序。同時,在對較多數據的判斷時,switch語句真的會更適應。
關於條件判斷語句:我們學習條件判斷語句需要注意許多細節的點如else會和哪一個if語句配對?if()后不能加分號,if只能涵蓋一條語句但{}可以將幾條語句合為一個代碼塊來歸到一個if后面。要養成在每一個if、else if、else后加代碼塊,無論有幾條語句的習慣。
關於邏輯運算符和邏輯非運算符:最簡單卻又最無奈的一點是=
和==
的區別,別看這個點很簡單,它卻很容易造成很嚴重的后果。以至於《C缺陷與陷阱》的1.1就是=不同於==,足以窺見這個錯誤有多么常見。
第二點就是我們一定要搞清或且非這三種邏輯的異同點。什么時候用或什么使用用且,這些事我們高中數學非常熟練運用過的東西,在大學我們更不能犯這種錯誤了。
在這一周里我們還學習了switch語句(開關語句),雖然它能做到的if語句都能做到,但它卻能讓一些問題簡化。
C語言雖然沒有限制 if else 能夠處理的分支數量,但當分支過多時,用 if else 處理會不太方便,而且容易出現 if else 配對出錯的情況。-來自網絡
在使用開關語句是我們需要注意的幾個問題分別是:
- switch只能判斷整數,不能判斷小數。
- switch后的括號內是要與后面case語句的常量比較的數。
- 開關語句具有穿透性,注意break的合理使用。有些情況下也會利用開關語句的穿透性簡單的解決問題。
- default后並不需要break。
- 開關語句中可以沒有default。
我們甚至還在課堂上擴展了rand和srand語句的使用來讓我們能夠有隨機數來參與到我們的程序中來。其中還告訴了我們用時間作為種子是最簡單的方法,且需要調用系統時間就需要標准庫頭文件:stdlib.h
的支持。我們在課堂上說了一種隨機數的實際應用:給熊孩子出簡單的四則運算題( ̄へ ̄ )。可以說是課堂作業吧,我也把這個有趣的作業做完了,在這里提交:
//熊孩子,我送你一整套53好不好啊?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int a = 0,b = 0,t = 0,x = 0;
double y1 = 0.0,y2 = 0.0;
srand((unsigned)time(NULL));
x = rand()%4;
a = rand()%10;
b = rand()%10;
if(a < b)
{
t = a;
a = b;
b = t;
}
if(x = 0)
{
y1 = a + b;
printf("請計算%d+%d的結果為:",a,b);
scanf("%lf",&y2);
if(y1 == y2)
{
printf("答案正確!");
}
else
{
printf("答案錯誤,正確答案為:%f",y1);
}
}
else if(x = 1)
{
y1 = a - b;
printf("請計算%d-%d的結果為:",a,b);
scanf("%lf",&y2);
if(y1 == y2)
{
printf("答案正確!");
}
else
{
printf("答案錯誤,正確答案為:%f",y1);
}
}
else if(x = 2)
{
y1 = a * b;
printf("請計算%d*%d的結果為:",a,b);
scanf("%lf",&y2);
if(y1 == y2)
{
printf("答案正確!");
}
else
{
printf("答案錯誤,正確答案為:%f",y1);
}
}
else
{
y1 = (double)a / (double)b;
printf("請計算%d/%d的結果為:",a,b);
scanf("%lf",&y2);
if(y1 == y2)
{
printf("答案正確!");
}
else
{
printf("答案錯誤,正確答案為:%f",y1);
}
}
return 0;
}
運行結果如下:
等到我們學習循環語句后我們就可以設定生成幾次題了,或者在現在想要生成幾道題就把部分語句復制粘貼幾次。
(三)實驗總結
我的學號是5,需要總結的題是1、5、7、9題,那么就讓我們開始吧。
1.成績轉換
本題要求編寫程序將一個百分制成績轉換為五分制成績。
- 大於等於90分為A;
- 小於90且大於等於80為B;
- 小於80且大於等於70為C;
- 小於70且大於等於60為D;
- 小於60為E。
Raptor流程圖如下:
源代碼如下:
//1.成績轉換
#include <stdio.h>
int main(void)
{
long long x = 0ll;
//printf("請輸入你的成績。\n");
scanf("%lld",&x);
x = x / 10;
switch(x)
{
case 10:
printf("A\n");
break;
case 9:
printf("A\n");
break;
case 8:
printf("B\n");
break;
case 7:
printf("C\n");
break;
case 6:
printf("D\n");
break;
default:
printf("E\n");
}
return 0;
}
這道題既可以用if-else語句來寫也可以用swithch語句(開關語句)來寫,但是題目要求我們練習開關語句的使用,而這道題的難易度也是為了讓我們來適應新語句而設置的。
而我們需要注意的一點就是switch語句只能用於整數型變量,也就是聲明時需要交給switch判斷的變量需要聲明成(unsigned)char,(unsigned)short,(unsigned)int,(unsigned)long,(unsigned)long long中的一種,否則編譯器會產生錯誤信息。
其次,我們一定要記得在每一行語句后加上break否則我們會得到所應得到的等級和它以下的每個等級。
還有一點,這道題我們可以不使用default。或者將E等級不用case而用default來實現表達。
我的PTA提交列表:
5.三角形判斷
給定平面上任意三個點的坐標(x1,y1)、(x2,y2)、(x3,y3),檢驗它們能否構成三角形。
Raptor流程圖如下:
這道題的源代碼如下:
//5.三角形判斷
#include <stdio.h>
#include <math.h>
int main(void)
{
double x_1 = 0.0,x_2 = 0.0,x_3 = 0.0;
double y_1 = 0.0,y_2 = 0.0,y_3 = 0.0;
double distance_ab = 0.0,distance_ac = 0.0,distance_bc = 0.0,A = 0.0,L = 0.0;
//printf("三個點的坐標x1、y1、x2、y2、x3、y3。中間用空格隔開。");
scanf("%lf%lf%lf%lf%lf%lf",&x_1,&y_1,&x_2,&y_2,&x_3,&y_3);
distance_ab = sqrt(pow(x_1-x_2,2)+pow(y_1-y_2,2));
distance_ac = sqrt(pow(x_1-x_3,2)+pow(y_1-y_3,2));
distance_bc = sqrt(pow(x_2-x_3,2)+pow(y_2-y_3,2));
if(distance_bc < distance_ac+distance_ab&&distance_ac < distance_bc+distance_ab&&distance_ab < distance_bc+distance_ac)
{
L = distance_ab + distance_ac + distance_bc;
A = sqrt(L/2.0*(L/2.0 - distance_ab)*(L/2.0 - distance_ac)*(L/2.0 - distance_bc));
printf("L = %.2f, A = %.2f\n",L,A);
}
else
{
printf("Impossible\n");
}
return 0;
}
實驗總結:
- 第一點:這道題其實可以將x1,x2,x3,y1,y2,y3,都定義為int類型的,這樣就可以一定程度的簡化思維和程序優化,例如:%lf就可以用我們最常用又不容易出錯的%d代替了。
- 第二點:這道題要記得在添加頭文件的時候加上數學頭文件(math.h),這道題是一定會使用一些數學函數的,例如求根號sqrt。不僅如此,使用數學增加代碼的可讀性,例如平方可以用pow代替連乘。
現在我們已經學會使用不少頭文件了,現在讓我們整理一下吧:
- stdio.h:我們最常用的,定義了用於輸入輸出的宏和函數,從鍵盤上讀取數據,將輸出寫入命令行上時,需要包含這個頭文件。
- float.h:它定義的宏設置了浮點數的限制和屬性。
- limits.h:它定義的宏擴展了一些值,以定義標准整數類型的限值。
- math.h:定義了用於常見數學操作的函數。
- stdlib.h:定義了許多一般用途的函數和宏。它包含了將字符串轉化為數值的函數,生成偽隨機數的rand()函數,給數據動態分配和釋放內存的函數,搜索和排序例程,整數算術函數,以及轉換多字節和寬字符串的函數。(真是一個高深的函數)
- time.h:它定義的宏和函數支持日期和時間操作。包括在程序執行過程中確定過去了多長時間。
我應該多復習以前的知識,也許現在有些看着不是那么重要,但今后一定會在很重要的地方幫上我們的。
PTA此題提交列表:
因為是在實驗課上時做的,所以一次性的就通過了。
7.兩個數的簡單計算器
本題要求編寫一個簡單計算器程序,可根據輸入的運算符,對2個整數進行加、減、乘、除或求余運算。題目保證輸入和輸出均不超過整型范圍。
Rapor流程圖如下:
此題源代碼如下:
//7. 兩個數的簡單計算器
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int a = 0,b = 0,result = 0;
char c = ' ';
//printf("輸入在一行中依次輸入操作數1、運算符、操作數2,其間以1個空格分隔。操作數的數據類型為整型,且保證除法和求余的分母非零。");
scanf("%d %c%d",&a,&c,&b);
switch(c)
{
case '+':result = a + b;break;
case '-':result = a - b;break;
case '*':result = a * b;break;
case '%':
if(b == 0)
{
printf("ERROR");
exit(0);
}
else
{
result = a % b;
}
break;
case '/':
if(b == 0)
{
printf("ERROR");
exit(0);
}
else
{
result = a / b;
}
break;
default:printf("ERROR\n");exit(0);
}
printf("%d\n",result);
return 0;
}
實驗總結:
- 這道題相比之前的題新增了開關語句(switch語句)的知識點,這道題同時也可以用if語句來寫,但老師為了讓我們熟悉新知識而讓我們用switch語句來寫。
- 注意這道題的除法中被除數不能為零,雖然PTA中的題目沒有做此要求,但在今后的實際應用時是必須考慮到各種各樣的問題的。我這道題的Raptor沒有將被除數不能為零判斷出來,謝謝同學們的指出:現修改為:
- 其實這道題的resault聲明為浮點數並修改之后的程序會更加適應實際情況,但題目已經說明會給出的測試數據會符合整數類型,所以我們用int來聲明就已經足夠。
再此,我再補上這道題用判斷語句的源代碼:
//7. 兩個數的簡單計算器
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int a = 0,b = 0,result = 0;
char c = ' ';
//printf("輸入在一行中依次輸入操作數1、運算符、操作數2,其間以1個空格分隔。操作數的數據類型為整型,且保證除法和求余的分母非零。");
scanf("%d %c%d",&a,&c,&b);
if(c == '+' || c == '-' || c == '*' || c == '/' || c == '%')
{
if(c == '+')
{result = a + b;}
if(c == '-')
{result = a - b;}
if(c == '*')
{result = a * b;}
if(c == '/')
{
if(b == 0)
{printf("ERROR");}
else
{result = a / b;}
}
if(c == '%')
{result = a % b;}
}
else
{printf("ERROR");}
printf("%d\n",result);
return 0;
}
PTA提交列表:
9.求一元二次方程的根
本題目要求一元二次方程的根,結果保留2位小數。
本題Raptor流程圖如下:
本題源代碼如下:
// 9.求一元二次方程的根
#include <stdio.h>
#include <math.h>
int main(void)
{
double a = 0.0, b = 0.0, c = 0.0, x1 = 0.0, x2 = 0.0,t = 0.0,q = 0.0,real = 0.0,imaginary = 0.0;
//printf("請輸入函數的系數。");
scanf("%lf%lf%lf", &a, &b, &c);
t = pow(b, 2) - (4 * a * c); //Δ在判斷和賦值語句中經常使用,賦值在t中簡化程序。
if (a == 0 && b == 0 && c != 0)
{
printf("Not An Equation");
}
if (a == 0 && b == 0 && c == 0)
{
printf("Zero Equation");
}
if (t >= 0 && a != 0) //Δ大於等於0,有連根。
{
q = sqrt(t);
x1 = (-b + q) / (2 * a);
x2 = (-b - q) / (2 * a);
if (x1 == x2) //兩相等的實數根。
{
printf("%.2f", x1);
}
else if (x1 > x2)
{
printf("%.2f\n%.2f", x1, x2);
}
else
{
printf("%.2f\n%.2f", x2, x1); //使輸出的兩根大根在前,小根在后。
}
}
if (t < 0 && a != 0 && b != 0)
{
real = b / (2 * a);
imaginary = sqrt((-t) / (2 * a));
printf("%.2f+%.2fi\n%.2f+%.2fi", real, imaginary, real, imaginary);
}
else if (a == 0 && b != 0)
{
x1 = -c / b;
printf("%.2f", x1);
}
else if (t < 0 && b == 0 && a != 0) //實根為0.
{
t = fabs(t);
x1 = sqrt(t) / 2;
printf("0.00+%.2fi\n", x1);
printf("0.00-%.2fi", x1);
}
return 0;
}
實驗總結:
看到這道題的時候我的心里是崩潰的,感想是無奈的,看不懂啊。我回憶高中的數學知識后認為我們並沒有將一元二次方程和虛數一起講過。在我那小的可憐的數學知識體系中,一元二次方程的解法和虛數是完全的兩個部分,也許是我們高中數學老師壓根沒講也有可能是我在三個月的暑假中把老師給我的知識統統都還給了老師。於是對這道題的琢磨完全就變成了對數學問題的掙扎。索性我們的度娘很可靠,讓我基本的將實部和虛部弄懂了。對虛部的簡單講解。
- 善用網絡和百度,有些高中並沒有細講一元二次方程求復數解。
- 這道題先做Raptor的流程圖會更容易讓我們寫C語言一些,一元二次方程的求根的分類太多了,很容易造成我們的思維混亂,但先寫Raptor卻能理清我們的思維,讓我們在寫C時不必再過多的考慮數學問題。
- 寫完Raptor的流程圖我們就能發現其實C語言可以將這些寫在一個if-else語句中,當然else if會很多,我們的思維只要不亂就能寫完它。
- 難點並沒有在C語言的運用上而是在高中數學上。
PTA提交列表:
(四)博客互評
對“怪人”的博客作業的小建議:
- 同學你對if-else語句的理解和解釋對我很有幫助,直觀清晰的把語句運行的邏輯表達了出來。
- 可以把PTA的每一個小題的提交列表附在每道題的總結后,這樣會更加規整易看一些。而不是四道題都做完了,總結完了再把所有的題的提交列表給出,這樣會讓我們唯一的助教需要去找每一道題的提交的。
- 一些題的實驗分析直接寫沒有問題還是有些顯得內容不充實的,我認為可以加上自己對這道題易錯點的分析來使的內容更充實的。
- 注釋其實還可以用
//注釋內容
的形式來寫,這樣在一些情況下可能會比/*注釋內容*/
更加簡潔易讀。
對程曉磊的博客作業的建議:
- 和對“怪人”同學的建議一樣,你的PTA的提交列表和老師給的模板不太一樣。
- 你的博客里太多的無序列表了,幾乎沒一行之前都會跟着一個黑圓點,可以去掉一些無序列表來讓博客看上去更美觀一些。
- 可以在一些地方加一些分割線來讓文章更易於觀看。尤其是讓我們唯一的助教更易於觀看。
對“永恆破碎”同學的博客作業的一些小建議:
- 每道題后最好加上實驗總結,我認為這部分是咱們這么大規模的作業中最精華,最對咱們有用的一個部分了,它可以讓我們反思,復習我們前一周的代碼的邏輯和更加的熟悉我們隨學的主體代碼。
- 學習總結可以再多一些,多一些自己的見解而不是已有的理論,豐富自己的博客內容。
- markdown的格式過少,markdown的功能非常強大,尤其是對我們這些過1024節的人來說。它插入代碼的操作別其他的博客語言簡單太多了。多一些博客格式沒准就可以讓咱們唯一的助教看的更加舒心。
沒准就給咱們多一點分了你說對不對?
在我提交博客園作業前只有三位同學提交了博客園的作業,我也只能暫時只評論這三位同學的博客園了,如果我以后有機會會再加入其它同學的,謝謝以上3位同學更早的提交作業了。兩位被我評論對不起了。程曉磊就算了