# 程式設計 [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,大部分是讀取記憶體位置 |
## 運算符號
* 計算順序

* 等號 = 是指定 (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中,陣列的值就代表是陣列的位址
```
**多維陣列**

* 陣列初始化
```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:*取值,&取址,不會就畫圖

* 指標+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,其他可以推論就好

* 一般使用時會以單引號包住字元,電腦就會自動轉換
```c=
char c ;
c = ’m ’;
printf ( " % c " , c );
```
* 字元的大小只有一個位元組
* 確認字元的片語

* 字元轉換

## 字串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`