# 程式設計 [C] 練習ideone網址 https://ideone.com/myrecent ## 變數屬性 1. 類別 (int, float, str...) 2. 名字 (不能是數字開頭) 3. 值 (宣告變數記得要給初始值,否則變數有可能會是任意值) 4. 記憶體位址 () * 一個位元組是8個位元,char是1個位元組,int是4個位元組,float是4個位元組,double是8個位元組,pointer是8個位元組 ## printf / scanf * printf / scanf 屬於 <stdio.h> standard I/O library 程式庫的函數,要記得先include才能使用 * 我們把 scanf 以十進位的形式讀到的 value 要 assign 給變數 i 的時候,就必須知道變數 i 的地址 * 計算機使用固定數目的位元組代表一個整數, 所以 int 所能存的整數大小是有一定範圍的。 這個範圍與一個 int 在記憶體中佔幾個位元組有關,可以使用 sizeof 察知變數所占的位元組,如果計算的結果超過一個變數所能記錄的範圍就是==溢位(overflow)==,溢位會使計算的結果不正確 ```c= #include<stdio.h> int main(){ int i = 0; scanf("%d", &i) printf("%d",i); printf("%d", sizeof(i)) return 0; } ``` * % 代表 printf 要印的資料, * d 代表 decimal(以十進位的形式)印出, * 參數 i 代表要印出存在變數 i 的值, * &i 代表變數 i 的地址 ## 換行符號 | 動作 | 符號 | 備註 | | ---- | ---- | --- | | 換行 | \n | newline | | 以16進位讀取 | %p |pointer,大部分是讀取記憶體位置 | ## 運算符號 * 計算順序 ![](https://i.imgur.com/fGaR8kz.png =400x) * 等號 = 是指定 (assignment) 的動作,等號右邊的事會先做 ```c= int i = 0; ``` 等號右邊的 value 0 會指定給等號左邊的變數 i,當成變數 i 的新值,變數 i 原本的值就會消失 ```c= int i = j = 0; ``` j 會先等於 0,i 才會等於 j * 非! 邏輯的 not i 意思是 i 為 真 時答案為 偽。 i 為 偽 時答 案為 真。 這在 C 中 寫成 !i。 ## 判斷式 condition if condition 為 true,則A,否則B ```c= (condition) ? A : B ``` * switch ```c= switch(flag){ case 1: statement1; break; case 2: statement2; break; default: default_statement; } ``` ## 迴圈 loop * for loop ```c= for(initialization;loop_condition;adjustment) ``` * do while 一定至少會做一次 ```c= do{ statement; } while(condition) ``` ## 陣列 array * Bubble Sort 泡沫排序法 ```c= int m , n [100]; int i , j , temp ; scanf ("%d " , & m ); for(i=0;i<m;i++){ scanf("%d", &(n[i])); } for(i=m-2; i>=0; i--){ for(j=0; j<=i;j++){ if (n[j] > n[j+1]){ temp = n[j]; n[j] = n[j+1]; n[j+1] = temp; } } } ``` * 找出質數 prime number ```c= int composite[101]; int i, n; int j = 2; int count = 0; int column; scanf("%d", &n); scanf("%d", &count); for(i=2;i<=n;i++){ composite[i] = 0; } while(j*j<=n){ while(composite[j] == 1): j++; for(i = 2*j; i<=n; i+=j){ composite[i] = 1; j++; } for ( i = 2; i <= n ; i ++) if ( composite [ i ] == 0) { if ( count % column == ( column - 1)) printf (" %3 d \ n ", i ); else printf (" %3 d ", i ); count ++; } ``` * Array記憶體位址 **單維陣列** ```c= int a[10]; printf("%d",sizeof(a[0])); #整數一次占4個位元組 printf("%d", sizeof(a)); #10個整數占40個位元組 for(int i=0; i<10;i++){ printf("%p", &(a[i])); #各整數的記憶體位址,相連間隔4個位元組 } printf("%p", &a); #會印出第一個整數的記憶體位址來代表陣列的位址 printf("%p",a); #在C中,陣列的值就代表是陣列的位址 ``` **多維陣列** ![](https://i.imgur.com/GL6BMbZ.png =500x) * 陣列初始化 ```c= #單維 int a[5]; int a[5]={1,2,3,4,5}; #陣列初始值 int a[]={1,2,3,4,5}; #初始可以不要給大小,陣列會自己判斷 int a[1000] = {0}; #假如初始值的數量不夠,陣列會自己補0 #多維 (幾個維度幾個括號) int array [2][3] = {{1 , 2 , 3} , {4 , 5 , 6}}; #第一個記憶體大小位置可以不寫,但是不能全部都不寫,會影響到記憶體的位址 int array [][3] = {{1 , 2 , 3} , {4 , 5 , 6}}; int array [3][3] = {{1 , 2} , {4}}; int array [3][3] = {{1 , 2 , 0} ,{4 , 0 , 0} ,{0 , 0 , 0}}; #不夠會自動補0 ``` :bulb:特別注意,若宣告陣列時設置的大小為變數,此時陣列無法設置初始化數值 **如果可以的話盡量避免使用變數宣告陣列大小,很容易有問題** ```c= int A[m]={0}; #錯誤 ``` * 控制列印格式 ```c= int count = 0; int column = 一列想要印幾個數字; if(count % column == ( column - 1)) printf ( " %3 d \ n " , i ); else printf ( " %3 d " , i ); count ++; ``` %3d 代表一次會印3個空格,可以控制列印的格式(對齊) ## 浮點數 float * float 32位元組的浮點數 * double (倍準浮點數) 是64位元組的浮點數,會比較精準 ```c= float f; double df; scanf("%f", &f); scanf("%lf", &df); printf("%f", f); printf("%f", df); ``` * double 在 scanf 要寫成 %lf (long float) ,一般的 float 只要寫成 %f 就可以了 * printf 會通通把浮點數升到 double ,所以只要都打 %f 就可以了 ### 浮點數計算 當不同類別的資料要運算的時候,就必須把低級的升到高級的才能運算 * int < float < double * 算式中出現不同類別,潛在的類別轉換 ```c= int i; int j ; double d ; scanf ( " % d " , & i ); scanf ( " % d " , & j ); scanf ( " % lf " , & d ); printf ( " % d \ n " , i / j ); #整數相除 printf ( " % f \ n " , i / d ); #整數會升級成浮點數再相除 ``` * 手動類別轉換 如果對變數做類別轉換,只是在算式中將變數的值轉換為 該類別,該變數的實際類別並不會改變 ```c= int count = 100; int sum = 250; average = sum / count ; #2,沒有進行類別轉換 average = ( double ) sum / count ; #2.5,先進行類別轉換才計算 average = ( double ) ( sum / count ); #2,先計算完才進行轉換 ``` ## 函式 function * 主函式 ```c= int main(void){ return 0; } ``` * 系統定義函式 ex. printf, scanf ... 等等,只要有include標頭檔就能用 **標頭檔查詢網址** https://www.cplusplus.com/ ```c= # include < stdio .h > /* for printf scanf */ # include < stdlib .h > /* for abs */ standard library # include < math .h > /* for sin */ ``` * 檢查回傳值 ex. scanf要怎麼知道還有沒有input,用end of file ```c= while (scanf("%d",&i) != EOF) data process ``` ex. scanf回傳值會是讀到的變數個數 ```c= n = scanf ( " % d /% d /% d " , &i , &j , & k ); //如果只有一個就會是1 ``` * 使用者定義函式 ```c= void foo(int array[], int n){ #這兩個是形式參數 for(int i=0;i<n;i++) printf("%d", array[i]); //return; void可放可不放 } int main{ int a[10]={0}; foo(a,10); #這兩個是實際參數 //這裡a會是array起始的地址 } ``` ## 指標 pointer * 指標變數的值代表另一個變數的記憶體位址 * 在宣告變數時在變數名稱前加星號 * 就代表這是一個指標變數 * 指標變數也有資料類別,例如指向整數的指標,則我們就將這個變數的值當作一個整數的記憶體位址 ex. iptr是一個指向int的指標變數 ```c= int * iptr ; printf("%d", sizeof(iptr)); #8 ``` * 指標的大小都是八個位元組 * 當一個指標變數前面加上星號時,就代表從這個記憶體位址取值 (dereference) ex. 當*iptr出現在左右兩邊時代表的意思不同 ```c= i = * iptr ; #取iptr指向的地址取值assign給變數i * iptr = i ; #把i的值存到iptr指向的位址 #一樣的意思 *( iptr + i ) iptr [ i ] ``` :bulb:*取值,&取址,不會就畫圖 ![](https://i.imgur.com/jHB5OUb.png) * 指標+1 **(可以想像成成指向下一個元素)** ```c= iptr ++; ``` 這裡的加 1 是指加一個元素的大小,所以如果是整數,指標變數就是加 sizeof(int) = 4;如果是倍準浮點數,指標變數就是加sizeof(double) = 8。 * 指標在陣列的應用 ```c= int a [5]; int i; int * ptr ; for (i = 0; i < 5; i ++) scanf ("%d " , &( a[i ])); #同時初始i和ptr,用逗號分開 for (i = 0, ptr = a; i < 5; i ++ , ptr ++) { printf (" %p\n " , ptr ); * ptr += 3; } ``` * 指標相減可以得到相對位置,指標可以代替陣列,陣列也可以代替指標 * 指標主要用於動態資料結構(ex. linked-list)和字串,其餘盡量避免使用 ## 字元 char * 電腦裡的字元是用ASCII表來轉換的 ```c= char c ; int i , j ; printf ( " 0123456789 abcdef \ n " ); for ( i = 2; i <= 7; i ++) { for ( j = 0; j <= 15; j ++) { c = i * 16 + j ; printf ( " % c " , c ); } printf ( " \ n " ); } ``` * 特別記65是A,97是a,其他可以推論就好 ![](https://i.imgur.com/smZQ5XH.png) * 一般使用時會以單引號包住字元,電腦就會自動轉換 ```c= char c ; c = ’m ’; printf ( " % c " , c ); ``` * 字元的大小只有一個位元組 * 確認字元的片語 ![](https://i.imgur.com/bvJ9ea6.png =400x) * 字元轉換 ![](https://i.imgur.com/RPXQRlP.png =330x) ## 字串string **標頭檔<string.h>** * 字串要用一個陣列的方法來宣告 ```c= char s [80]; char s [80]= { ’m ’ , ’a ’ , ’i ’ , ’n ’ , ’( ’ , ’) ’ , ’\ n ’ , ’{ ’ , ’\ n ’ , ’} ’ , ’\ n ’} ; int i ; for ( i = 0; i < 11; i ++) printf ( " % c " , s [ i ]); ``` * **一次印出所有字串string** 約定好只要看到 **'\0'** 的字元,就會自動停止併入string裡,因此想要一次印出所有字串,只需要在最後一個字元後面加入 **'\0'** ```c= char s [80] = { ’m ’ , ’a ’ , ’i ’ , ’n ’ , ’( ’ , ’) ’ , ’\ n ’ , ’{ ’ , ’\ n ’ , ’} ’ , ’\ n ’ , ’ \0 ’} ; int i ; printf ( " % s " , s ); ``` <br> * 字元常數(單引號) ’m’ / 字串常數(雙引號) " main ()\ n {\ n }\ n " <br> * 空字串 陣列會自動補0,0用字元來表示就是 **'\0'**,所以至少會有一個位元組的大小要用來放 **'\0'** ```c= char empty_string [] = " " ; ``` <br> * 字串的輸入 string本身就已經是一個陣列,變數string代表這個陣列的位址,所以scanf不需要再特別 &string ```c= char string [10]; while ( scanf ( " % s " , string ) != EOF ) { printf ( " % s \ n " , string ); ``` <br> * 字元指標類別的字串也可以有初始值 編譯器會在唯讀中放一個字元陣列,使陣列初始化但是只能讀不能寫 如果是宣告陣列初始化就沒關係 ```c= char *string = "programming" ; ``` * 特殊片語 ### **1. strlen** ```c= int strlen ( char * string ); ex. char *string = "programming" printf("%d", strlen(string)); ``` ### **2. strcpy** ```c= char *strcpy(char *destination, char *source); #把source的資料覆蓋掉destination #陣列原本就是地址,不用再特地加指標上去 //strcopy的時候會順便把字串末端的 \0 也一起copy char source[100]; char destination[100]; scanf("%s\n", source); scanf("%s\n", destination); strcpy(source, destination); ``` ### **3. strcat** ```c= char *strcat(char *destination, char *source); #把source的字接在destination後面 //strcat會copy source直到最後 \0 ,然後從destination \0 的地方開始接 char source[100]; char destination[100]; scanf("%s\n", source); scanf("%s\n", destination); strcat(source, destination); ``` ### **4.strchr** string中搜尋一個字元 c,如果找到字元 c,則回傳第一個字元 c 的index strrchr 由字串的後面往前面找字元 c ```c= char * strchr ( char * string , int c ); char * strrchr ( char * string , int c ); ex. char str[] = "This is a sample string"; char * pch; printf ("Looking for the 's' character in \"%s\"...\n",str); pch=strchr(str,'s'); while (pch!=NULL) { printf ("found at %d\n",pch-str+1); pch=strchr(pch+1,'s'); } ex. char pathname [40]; char file [40][40]; int file_count = 0; char * start = pathname ; char * slash ; int i ; scanf ( " % s " , pathname ); if (* start == ’/ ’) start ++; while ( start != NULL ) { slash = strchr ( start , ’/ ’ ); #如果找不到代表沒有/了,直接把剩下的字都抓進來就好了 if ( slash == NULL ) { strcpy ( file [ file_count ] , start ); file_count ++; start = NULL ; } else { strncpy ( file [ file_count ] , start , slash - start ); file [ file_count ][ slash - start ] = ’ \0 ’; file_count ++; start = slash + 1; } } ``` ### **5. strstr** string 1 中尋找 string 2,若有回傳index,否則回傳NULL ```c= char * strstr ( char * string1 , char * string2 ); ex. char * ptr ; char zodiac [12][40]; int i ; for ( i = 0; i < 12; i ++) scanf ( " % s " , zodiac [ i ]); for ( i = 0; i < 12; i ++) { ptr = strstr ( zodiac [ i ] , " er " ); if ( ptr == NULL ) printf ( " No er in % s \ n " , zodiac [ i ]); else printf ( " er at %d - th in % s \ n " ,ptr - zodiac [ i ] , zodiac [ i ]); } ``` ### **6. strcmp** ```c= int strcmp(char *string1, char *string 2); #比大小,若1較小回傳負數,若1較大回傳正數,若兩字串相等則回傳0,從頭開始以ASCII碼的大小來比 ``` * 如果有很多字串陣列要複製的時候可能會花費比較多的時間,我們可以改以作指標陣列的方式來進行,省去strcpy的時間 ```c= char zodiac[12][40]; char *zptr[12]; int i, j; char *temp; for(i=0; i<12; i++){ scanf("%s", zodiac[i]); zptr[i] =zodiac[i]; } for(i =10; i<=0; i--){ for(j = 0; j<=i; j++){ if(strcmp(zptr[j], strcmp(zptr[j+1]) > 0){ temp = zptr[j]; zptr[j] = zptr[j+1]; zptr[j+1] = temp; } } } for(i = 0; i<12; i++){ printf("%s\n", zptr[i]); } ``` <br> * 如果source的資料太長,destination的空間不夠接的話,會造成**緩衝區覆蓋(buffer overrun)**,字串後面的其他重要資料就會被覆蓋 解決方法:引進兩個新的函式 strncpy、strncat ```c= //int i 可以幫忙控制最多要複製幾個字元,但是字尾要自己補 \0 char *strncpy(char *destination, char *source, int i); char *strncat(char *destination, char *source, int i); int strncmp(char *string1, char *string 2, int i); ex. char destination[16]; char source[80]; strncpy(destination, source, 15) #只有16格,最多只能複製15個字元,第16個要放 \0 destination[15] = '\0'; ``` * string to token token指的是一段字元,是第一個字串中被第二個字串參數 delimeters 中的任何字元所隔開的部分,delimeters可以是多個 ```c= char * strtok ( char * string , char * delimeters ); start = strtok ( string , delimeters ); #先呼叫一次,回傳一個值給指標變數start # 進入一個 while 迴圈檢查 start 是否為 NULL。如果start 不為 NULL,start 就會指到原字串 string 中的一個字串 while ( start != NULL ) { strcpy ( file [ file_count ] , start ); file_count ++; # 唯一的差別是這邊要寫一個NULL,意思就是說你第一次進去的時候,是從字串的頭,以後你每一次給一個NULL,它就知道說,哦我是延續了上一次的那個string to token start = strtok ( NULL , delimeters ); } ``` <br> ###### tags: `C`