88
看似比較簡單的推坑教學 ~~C語言崩潰到崩潰(一)~~ 製作者:lian0123 學歷:南大資工學生 大安高工畢業 更新日期:2016/10/03 維護期限:2017/09/01 版本編號1.1.5++

看似比較簡單堆推坑教學 C語言崩潰到崩潰(一)

  • Upload
    -

  • View
    327

  • Download
    0

Embed Size (px)

Citation preview

看似比較簡單的推坑教學~~C語言崩潰到崩潰(一)~~

製作者:lian0123學歷:南大資工學生   大安高工畢業

更新日期:2016/10/03 維護期限:2017/09/01版本編號1.1.5++

前言

•本人的歷鍊還不夠,所以文章有地方錯,歡迎各位大神指出,感謝

•我會嘗試以最簡單方式去進行教學,讓大部分的人能聽的懂。所以拜託大神們在打臉我時,請不要打的太大力

筆者所用的工具

•Linux Mint系統:•屬於自由軟體的作業系統•使用虛擬終端機,所以沒有"system('PAUSE');"

•Vim:•世界上兩大最有勢力的文字編輯器之一•簡潔、快速、符合UNIX美學

•Gcc:•理查.斯托曼所開發屬於自由軟體的C語言編譯器

給Visual Studio 的使用者•基於筆者使用Linux虛擬終端機,所以不需要用

system("PAUSE") 與scanf_s

•因為scanf_s是是Visual Studio專有的指令,而終端機是屬於將輸入鏡射到螢幕上,所以也沒有system("PAUSE")的指令

•若Visual Studio 的使用者要撰寫程式時,請注意這些差別

Class 0 新手們所欠缺的基礎

經驗

•所有程式,如果在不危害電腦的情況下,都應該手做測試去了解這個程式的原理

•只讀不去真正體會程式,是無法進步的

學習C語言的門檻1.資料型態與基本輸入輸出--->初學者的障礙2.基本敘述式與邏輯運算子--->基本門檻3.記憶體與指標--->感覺很多人會掛在這邊4.副程式與其應用--->大概在這也會有很多人4.檔案存取--->筆者正在這裡5.其他函數--->基於函數太多,所以需花時間去了解各函數的用途6.編譯器原理--->應該很難7.自己寫出一個專案--->找一個大四的學長也不一定能寫出來8.操控自如--->此類人士稱作"大神"

新手常犯的錯誤:分號沒加

• 分號是在大部分程式語言中會出現的符號,除了VB與python

• 分號的用途用在每一行程式碼的結束符號,除了"include"、"define"、"基本的 主程式{}/副程式{}/函式{}"

• 在分號沒加的情況下,編譯跳錯誤時會顯示行號,檢查那一行的

新手常犯的錯誤:打錯字

• 打錯字算是滿正常的,但在visual Studio的環境下應該不太容易打錯

• 如果你是用其他編輯器,要多注意這個問題

• 但基本上編譯器會告訴你打的錯字沒有找到之類的

新手常犯的錯誤:誤刪括號/多打括號• 以下的所提的括號包含:()、[]、{}

• 有時候delete與backspace時,太順手可能會把其他行的括號刪除,或是多打一個括號

• 基本上,Visual Studio會在括號以紅色波浪的底線去提醒程式設計者這括號有問題,所以依照這個線索去找出是哪一個括號出了問題

新手常犯的錯誤:大小寫錯誤

• 基於程式語言基本上都有分大小寫的,請注意

• int A與int a是不同的東西

新手常犯的錯誤:運算子優先權判斷

•大多數程式語言都有運算子的優先順序,所以程式設計者需配合程式的規範

•解決方法,用背的

新手常犯的錯誤:宣告錯誤

• 變數宣告時:保留字不能拿來宣告成變數(int return = 0;)、不能以數字開頭(int 1a =0;)、不能用特殊符號(int ^% =0;)、不能有空白(int a pple =0;)

• 而我們經常會用"_"來連接變數的單詞,如:int a_pple =0;

• 基本上系統在編譯時,會提醒程式設計者這有這個問題

新手常犯的錯誤:邏輯錯誤

• 這基本上是程式設計者本身設計的想法與實際執行情形不同,而產生結果上的差距

• 遇到這情況,最佳的解決辦法就是多練習,以增加自己對程式認知

新手常犯的錯誤:函數錯誤

• 也是同一句話:遇到這情況,最佳的解決辦法就是多練習,以增加自己對程式認知

新手常犯的錯誤:副程式未定義

•通常我們第一個執行的程式是叫main,但我們能在main中呼叫其他的副程式,但是需要先宣告副程式

目前我所知的一些開源網路資源:

金門大學一位教授所寫的C語言教學(雖然說他是用javastript去解釋C語言):https://www.gitbook.com/book/shengwen1997/program_with_c/details

一個基本程式教學的網站:http://pydoing.blogspot.tw/2012/10/c-tutorial.html

Class 1 基本觀念

C語言的重要性很多東西都是由C 語言去衍生出來的,如:UNIX、Linux、BSD、MacOS、Windows、Android、C++、JAVA、數據機、嵌入式系統、以伺服器去做的網頁服務的企業等.....一堆東西都和C語言有關

所以不管你上課有沒有在聽,由上面一大堆有的沒的就可以看出C語言對世界的影響力

C語言的執行方式•逐行執行•由上到下,由左到右•(javastrip與node.js是每行同時執行的)

二進位

•在電腦硬體(正邏輯)上電壓一定數字以上稱作1,電壓一定數字以下稱作0

•二進位:101111 ->1*25+0*24+1*23+1*22+1*21+1*20

•基於節省空間我們以000000開始計算,後影響到電腦的程式

主要的觀念

#include<stdio.h> //指引用函式庫,這裡是引用stdio.h的函式庫

#define MY_NUMBER = 10 // 定義某一單詞,使那單詞變成指定值

int main(void){ }//在此層的大括弧底下的就是主要在進行的程式

變數、常數、記憶體

•當我們執行「int a=10;」時,以最簡單的方式來說

•我們命名的變數a是一個盒子•而我們的int 就是在宣告盒子的類型與大小•地板是記憶體的位置,會因為盒子的類型與 大小而會用到不同大小的地板空間•常數10是我們丟下去的東西•而當我們下次要用10時就去找a的盒子

a

10

程式與變數

int a=10; //將10這個數字放入a的變數中

a++;//先呼叫a,再加1,同a=a+1;、a+=1;

++a;//先加1,再呼叫a,同a=1+a;

字元與字串

•這是字元:"a"、"b"、"c"

•這是字串:"It is a"、"because ..."、"cat"

0與空值•這是0:"0"

•這是空值:""

Class 2 資料型態

資料型態

•請參閱此網站(交大):http://yes.nctu.edu.tw/vc/ref/basicdatatype.htm

I/O函式•請參閱此網站(金門大學的陳鍾誠教授寫的<C 語言的格式化符號>): http://ccckmit.wikidot.com/cp:format

指定資料型態為唯讀

•const int a=10;

•當該程式碼執行時,a會被設定為10,且之後無法變動

陣列(一)

•定義 陣列名[宣告數字] = 寫入陣列0的資料,寫入陣列1的資料.......

•int a[5]= {10,20,30,40,50}; 宣告陣列與其存放的數值

•注意!從0開始編號,5只是宣告的數量

• 給你們看一個有趣的東西: https://www.wetalk.tw/thread-35858-1-1.html

0 1 2 3 4

10 20 30 40 50

陣列(二)

int a[5]= {10,20,30,40,50}; 宣告陣列與其存放的數值

在這樣設定後,若我們去呼叫陣列:呼叫a[0]回傳10呼叫a[1]回傳20呼叫a[2]回傳30呼叫a[3]回傳40呼叫a[4]回傳50

陣列(三)

•當然我們能在程式中,對單一陣列內部的值去修改,如:a[3]=75

0 1 2 3 4

10 20 30 75 50

二維陣列

•一維陣列: int a[5];

•二維陣列:int a[2][5]

0 1 2 3 4

0 0 1 2 3

1 0 1 2 3

4

4

類似子集合的概念

Class 3 邏輯運算子

數學運算子

•+ 加 EX:c=a+b•- 減 EX:c=a-b•* 乘 EX:c=a*b•/ 除 EX:c=a/b•% 取餘數 EX:c=a%b

關係運算子

a>b //a大於ba<b //a小於ba>=b //a大於等於ba<=b //a小於等於ba==b //a等於ba !=b //a不等於b

邏輯運算子A B A&&B A||B !A

0 0 0 0 1

0 1 0 1 1

1 0 0 1 0

1 1 1 1 0

若讀者看不懂,請將0視為假,並將1視為真並參閱以前讀過的數學課本聯集與交集部份

位元運算子

•當我們輸入數字125與65時,會轉為二進制1111101與1000001

•&:執行AND的1位元運算(凡0即0)•|:執行OR的1位元運算(凡1即1)•^:執行XOR的1位元運算(基數1為1,偶數1為0)•~:執行NOT的1位元運算(單一二進位變數1/0互換)•>>:右移位元 (說明太麻煩,省略)•<<:左移位元 (說明太麻煩,省略)

11111011000001 (XOR----------------0111100

1位元運算

if ...else...#include<stdio.h>int main(void){

int a =1;if(a==0){

printf(" a is 0");}else{

printf("a is not 0");}return 0;

}/* 用if括弧內的邏輯運算子去判斷是否要執行底下大括弧的程式,若不是,將會去執行else{}內的程式 */

Output: a is not 0

Class 4 敘述式

所謂的if判斷if(當A事件成立){

A事件成立時會執行的程式}else if(當A事件不成立,B事件卻成立時){

B事件成立時會執行的程式}else{

當A/B事件都沒成立的情況下,會執行的程式}

if#include<stdio.h>int main(void){

int a=0;if(a==0){

printf(" a is 0");}return 0;

}

/* 用if括弧內的邏輯運算子去判斷是否要執行底下大括弧的程式 */

Output: a is 0

if ...else if...else#include<stdio.h>int main(void){

int a =1;if(a==0){

printf(" a is 0");}else if(a==1){

printf("a is 1");}else{

printf("What?");}return 0;

}/* 用if括弧內的邏輯運算子去判斷是否要執行底下大括弧的程式,若不是會再進行else if的判斷,若都是,將會去執行else if(){}內的程式 ,在以上都不是的情況下,將會去執行else{}內的程式 */

Output: a is 1

switch ...case#include<stdio.h>int main(void){

int a;scanf("%d",&a);switch(a){

case 1 :printf("HI");

}return 0;

}/*當我們輸入1,程式會回應"HI",但若輸入齊通東西都會被跳過*/

Input: 1Output: HI

所謂的for迴圈for(定義數字 ;邏輯判斷 ;執行完內部程式在進行的指令){

邏輯判斷後,會執行的內部程式}

for迴圈(一)#include<stdio.h>int main(void){

for(int a=0;a<10;a++){printf("%d ",a);

}return 0;

}

/* 此時會輸出0 1 2 3 4 5 6 7 8 9。因為當a=10時,就不符合a<10的條件,會跳出迴圈*/

Output:0 1 2 3 4 5 6 7 8 9

for迴圈(二)#include<stdio.h>int main(void){

for(int a=0;a<10;a++){brack;printf("%d ",a);

}return 0;

}

/* 此時不會輸出。因為當一執行迴圈時就會碰到brack,brack是指強制跳出迴圈的意思 */

Output:

for迴圈(三)#include<stdio.h>int main(void){

int a=0;for(;a<10;a++){

printf("%d ",a);}return 0;

}

/* 這樣的寫法也是可行的,雖然我們沒宣告回圈內變數,但我們以事先定義了a,所以是可容許的。此時會輸出0 1 2 3 4 5 6 7 8 9。因為當a=10時,就不符合a<10的條件,會跳出迴圈 */

Output:0 1 2 3 4 5 6 7 8 9

for迴圈(四)#include<stdio.h>int main(void){

int a =0;for(;;){

printf("%d ",a);}return 0;

}

/* for(;;)是指無限迴圈,可能會造成系統當機,除非你會跳出迴圈,請盡可能不要這麽寫 */

Output:0 0 0 0 0 0 0 0 0 0 0....(無限執行)

while...#include<stdio.h>int main(void){

int a=0;while(a==0){

printf(" a is 0");a=a+1;

}return 0;

}

/* 類似for迴圈的執行模式,運用while括弧內的邏輯運算子去判斷是否要執行底下大括弧的程式,當a變成1時,便會跳出while的迴圈 */

Output: a is 0

do...while#include<stdio.h>int main(void){

int a=0;do{

printf("What is a?");a=a+1;

}while(a==0);return 0;

}

/* 先做do括弧底下的動作,在判斷是否符合While的條件 */

Output: What is a?

goto•程式語言底層控制部份函式的原始結構

•但在我們寫程式時,用goto會破壞程式的結構

•在此不多談

exit()•強制跳出程式

•exit(1);指強制跳出程式並向系統回傳1

Class 5 指標

何謂指標?

•當我們執行「int a=10 *b;」時,以最簡單的方式來說

•int *b就是宣告b是一個指標•我們可以將b指標指到a所在的記憶體位置上•也就是讓b直接去獨a的資料

a

10 b

宣告指標

•int *b; //宣告指標(指向變數用)•int **b; //宣告雙重指標(指向指標用)

雙重指標

•將一指標指到另一個指標上•而另一個指標對應於記憶體

•在這情況 c 、b 、a 皆指向於 同一個記憶體上,所以呼叫每 一個的變數都是會回傳10

a

10 b

c

指派指標

•int a=10,*b,**c;•b =&a; //將變數b指向a的記憶體位置•c =&b;//將變數c指向b的記憶體位置(因為b以指定到a的記憶體位置上,所以c也會得到a的記憶體位址)

Class 6 基本Intput/Output 控制

printf•送出文字到螢幕上

•"\n"是換行

•"\\"是顯示\號

•用法:printf("%d",a); //指輸出int的常數,並將使用代數a放入該常數

scanf•掃描鍵盤的輸入直到按下enter送出

•用法:scanf("%d",&a);將輸入int範圍內的常數,並放於a所在的記憶體上

scanf_s•這是微軟為了不讓原本的scanf的漏洞出現,而發展的專有函數

•但這也是掃描鍵盤的輸入直到按下enter送出的功能

•用法:scanf_s("%d",&a);將輸入int範圍內的常數,並放於a所在的記憶體位置上

•而在Linux上使用gcc去編譯,scanf_s會在編一時跳出錯誤

Class 7 副程式

何謂區域變數與全域變數

•區域變數:只有一個區域範圍內能使用的變數

•全域變數:執行完宣告後,每個地方都能使用

區域變數與全域變數(一)

#include<stdio.h>int m=0;//此處宣佈的變數為全域變數(不是很好的寫法,改用#define定義會比較好)int main(void){

int a=10; //a為區域變數,只能在main(void)內去呼叫使用for(int b=0;b<10;b++){//int b為區域變數只能在for迴圈內呼叫使用

int c; //c為區域變數只能在for迴圈內呼叫使用printf("%d ",a);

}return 0;

}

區域變數與全域變數(二)

#include<stdio.h>int m=0;int main(void){

int a=10; for(int b=0;b<10;b++){

int c; printf("%d ",a);

}return 0;

}/*全域變數m 可有效使用的範圍(這樣設定全域變數不是一個好寫法)*/

區域變數與全域變數(三)

#include<stdio.h>int m=0;int main(void){

int a=10; for(int b=0;b<10;b++){

int c; printf("%d ",a);

}return 0;

}/*區域變數a 可有效使用的範圍*/

區域變數與全域變數(四)

#include<stdio.h>int m=0;int main(void){

int a=10; for(int b=0;b<10;b++){

int c; printf("%d ",a);

}return 0;

}/*區域變數 b 可有效使用的範圍*/

區域變數與全域變數(五)

#include<stdio.h>int m=0;int main(void){

int a=10; for(int b=0;b<10;b++){

int c; printf("%d ",a);

}return 0;

}/*區域變數 c 可有效使用的範圍*/

將區域變數變成全域變數(一)

#include<stdio.h>int m=0;int main(void){

int a=10; for(int b=0;b<10;b++){

a = b; }printf("%d ",a);return 0;

}/* 將區域變數b的內容,寫入到全域變數a上 ,這時就會輸出9*/

Output: 9

將區域變數變成全域變數(二)

•被教授糾正,為了讓程式能夠結構化,所以不要寫這種全域變數(被稱做是很爛的寫法),還是乖乖的用return回傳吧......!

副程式(一)

•指在主程式底下另外執行的一組程式

•我們可以用副程式去寫一些運算,只要用主程式去呼叫副程式,就能夠直接進行運算

•假如我們要做一個150筆不同資料的複雜運算時,與其去寫150遍佔行數,不如用副程式去執行150次

副程式(二)#include<stdio.h>int f1(int s); //向系統宣告main外有f1這個副程式int main(void){

int a=10 ,b=0; b=f1(a); //執行f1的副程式,並回傳100寫入到bprintf("a*a is %d ",b);return 0;

}int f1(int s){ //將a=10定義成s=10

s=s*s; //平方計算10*10=100return s; // 將100回傳

}

Output: a*a is 100

遞迴函數

•在副程式中,變數會去呼叫自己,如:帕斯卡三角形就可以透過遞迴函數,呼叫自己在進行運算

Class 8 結構

何謂結構?struct 結構名稱 {

定義資料型態 變數 ;定義資料型態 變數 ;定義資料型態 變數 ;

}宣告賦予結構的物件名稱(可多數);

賦予結構的物件名稱.結構名稱內的變數名;

結構

#include<stdio.h>int main(void){

struct my_test {int a[10] ;char b[8] ;int c ;int d ;

}M,N ;M.c = 10;N.c = 20;

結構(續)printf("M.c is: %d \n",M.c);printf("N.c is: %d \n",N.c);return 0;

}

/*因為我們是將相同的結構賦予給M和N所以M.c和N.c是不一樣的東西*/

Output: M.c is: 10 N.c is: 20

結構(續)

•M.c與N.c的關係

將struct的內容放入M 將struct的內容放入N

宣告my_test的內容結構(上述的a,b,c,d型態與命名)

提取M裡面的c並在N裡面的C輸入20

提取N裡面的c並在N裡面的C輸入20

至於列舉,在此不談

這樣也講完這學期十分之七的程式基礎了

最後再看當初的一個程式:

#include<stdio.h>#include<stdlib.h>int main(void){

int a;printf("Input You Age:");scanf("%d",&a);printf("\n You Age Is :%d",a);return 0;

}

現在的你能看懂了嗎?

這次就先說到這裡

因為到這邊應該就行了

感謝大家的觀看

到此結束

感謝大家的閱讀!說起來這次趕工趕的有點累