###### tags: `Coding`
# C / C++ 學習筆記(partII)
## 編寫規則(指標之前)
---
### 陣列
#### 定義
* 一群具有相同資料型態元素集合而成
* 於記憶體中會使用一段"連續"的記憶體空間存放 => 同等於array
* 陣列的變數定義:
* "元素" 資料型態 陣列名稱[元素個數]; 範例: int arr[3];
* 陣列index從0開始
* 陣列元素的存取:
* 陣列變數名稱[索引編號]
#### 陣列初始化寫法
```
int counter[6] = {0,0,0,0,0,0}; //初始化六個位置皆為零
int counter2[6] = {0};//初始化時,最少需要指定一個值,沒有被指定到的位置會被自動指定為0(此case 指定到第一個位置)
int counter3[] ={0,0,0,0,0,0} //直接以初始化的值設定矩陣大小
```
#### 輸入0~99數(數個),並以10為單位,繪製bar chart
```
#include <stdio.h>
#include <math.h>
void bar(int []);
int main(){
char check;
int array_size,input_number;
printf("This programe is used for value 0~99\nplease input your size of input array:");
scanf("%d",&array_size);
int count[10] ={0};
for (int i=0;i<array_size;i++){
printf("your %d number:",i+1);
scanf("%d",&input_number);
count[input_number/10] += 1;
input_number = 0;
}
bar(count);
return 0;
}
void bar(int count[]){
for(int i=0;i<10;i++){
int CountDown = 0;
if (i ==0){
printf("The amount of 00~09:");
}
else{
printf("The amount of %d~%d:",i*10,(i+1)*10-1);
}
while (CountDown <count[i]){
printf("*");
CountDown++;
}
printf("\n");
}
}
```
#### 陣列排序 => 演算法
**註解:此章節先跳過,等之後實作演算法時補充**
#### 陣列的記憶體配置
 通常在宣告定義變數時(例如int)時,會藉由宣告的變數型態安排相對應的記憶體空間給該變數,當在宣告陣列時,會依照指定的元素型態,分派相對應大小的記憶體空間。
#### 陣列中的陣列(矩陣)
```
int v[x][y] =>可看成v[0]存有[y]個element...
```
 在陣列中的陣列(矩陣)當中,記憶體的儲存方式為藉由row儲存,也就是若今天有矩陣為:<br>
> v[2][3]<br>
則其在記憶體內的儲存方式為如下:<br>
```
v[0][0] -> v[0][1] -> v[0][2] -> v[1][0] -> v[1][1] -> v[1][2]
```
#### 以二維陣列實踐隨機九宮格
```
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main(){
srand(time(NULL));
int matrix[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
int count,random,row,col,t;
for (int i=0;i<3;i++){
for (int j=0;j<3;j++){
random = rand() % 9;
row = random / 3;
col = random % 3;
t = matrix[i][j];
matrix[i][j] = matrix[row][col];
matrix[row][col] = t;
}
}
for (int i=0;i<3;i++){
for (int j=0;j<3;j++){
printf("%d ",matrix[i][j]);
}
printf("\n");
}
return 0;
}
```
#### 陣列的複製
* 陣列無法直接用另一個陣列初始化 ex:<br> v[10]=n (x)
##### 使用迴圈複製陣列
```
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
srand(time(0));
int i,n[10],v[10];
for (i = 0;i < 10;i++){
n[i] = rand() % 100;
}
for (i = 0;i < 10; i++){
v[i] = n[i];
}
for (i = 0;i < 10;i++){
printf("n[%d]:%d\n",i,n[i]);
print("v[%d]:%d\n",i,v[i]);
}
return 0;
}
```
#### 函式間傳遞陣列
* 在陣列傳遞間,傳送到函式的陣列並不會為複製者,而是使用原本陣列(若是整數或是浮點數則是複製一份於新的函式使用。)
* 函式的回傳值不可為陣列,取代而知當陣列作為引數傳入,即可直接改變陣列(需要注意此點)。
* C語言導入指標運算來做陣列的處理。
* 作為引入時,可以不給予大小(在作為傳入的時候是否有寫陣列大小無差異)。
##### 原理
 於函式間傳遞陣列時,複製的不是陣列的全部資訊,而是複製陣列的起始位置,也就是傳入起始的(address),之後藉由搭配每個資料型態的大小,去計算每個元素的起始位置,得到該位置的元素,因此於宣告時,不管函式宣告的陣列大小為何都不影響,因為單純傳入的是起始位置,其餘都是藉由計算得到,同時於函數中更改陣列,會更改到原陣列原因也是於此,改變這個動作是直接操作address,而非複製值。
#### sizeof
* 資料型別占用記憶體的大小為「實作定義」,隨編譯器與設定上不同而有所差異。
* 使用sizeof運算子可以求出某個值或型別所占用的記憶體大小空間(於printf內使用%zu呈現結果)
##### 使用sizeof求陣列長度
**註解:此用法只能在沒有經過函式傳遞的情況下,在計算矩陣的位元數時,可能不如想像**
```
int main(){
int v[3] = {1,2,3};
printf("Size of int: %zu\n",sizeof(int));
printf("Size of v[0]: %zu\n",sizeof(v[0]));
printf("Size of v: %zu\n",sizeof(v));
printf("Length of v: %zu\n",sizeof(v)/sizeof(v[0])); // 用總共大小除以單個,可以得知陣列長度
return 0;
}
```
#### 存取陣列元素的原理
 每次執行時,陣列的開始記憶體位置會不同,但單次執行到刪去陣列前,陣列的起始記憶體位置會相同,且由於陣列的連續性,可以用開始位置搭配sizeof()計算每個陣列元素的記憶體位置。
```
v[i]的記憶體起始位置(address) = 第一個元素的起始位置(address of first element)+i * sizeof(元素型態)
```
---
### 全域變數
#### 特性
1. 在沒有給定初始值的時候,初始值為0。
2. 流程:初始化全域變數 -> 呼叫main() (在呼叫main前,變會初始化全域變數)
#### 缺點
 全域變數的使用會使得函數間的關係不明確(因為沒有特定給哪個變數使用)。
#### 與靜態區域變數差異
* 靜態的區域變數只會有一份,且只會初始化一次,寫法如下:
```
#include <stdio.h>
int count(void){
static int k = 0; //使用static指定變數為靜態變數
k++;
return k;
}
int main(){
for (int i = 1; i <= 5; i++){
printf(" %d\n",count());
}
return 0;
}
```
#### 靜態全域變數
```
static unsigned int x = 1; //此為靜態全域變數,只有在此檔案內才可使用
int main(){
return 0;
}
```
-----
### 整數溢位
#### limits.h
```
#include <stdio.h>
#include <limits.h>
int main(){
printf("%d\n", INT_MAX);
printf("%d\n", INT_MIN);
return 0;
}
```
**使用INT_MAX & INT_MIN 查看範圍內的最大與最小值(for 整數)**
**Unsigned 型態在溢位後會算輸入值對(UINT_MAX +1 )取餘數**
---
### 偽亂數生成
 使用線性同餘法(Linear congruential generator)
* X~n+1~ = (X~n~xa+c) % m
* 初始值X~0~則稱為亂數種
* 可以利用unsigned int 溢位時會求餘數的概念編寫
```
#include <stdio.h>
int main(){
unsigned int next = 1;
for (int i =1;i<=5;i++){
next = next * 1103515245+12345; //此行為求餘數
int rand = (unsigned int)(next/65536) % 32768; //取兩次餘數(此方式只會取到高位,以使得隨機性更高)
printf("%d\n",rand);
}
return 0;
}
```
---
### 字串簡介
#### 字串是字元的序列
* 字元型別(char)可儲存「一個」字元,而通常需要處理的文字是「一串」字元。
* 字串沒有特別的資料型別,(char [])。
* 透過<string.h>,提供字元陣列的函式實現對字串的操作。
* char str[] = {'H','e','l','l','o'};
* 字串是以'\0'表示結尾的字元陣列
* char str[] = {'H','e','l','l','o','\0'}
* 在讀入字串前就要先決定好存放字串的緩衝大小(字元大小-1因為有\0)
### const修飾字
在const賦值之後,除了初始化的階段之外,其值無法再被賦予
* 於C語言內可看成唯讀(read-only)的屬性
---
## 編寫規則(指標)
### 於無指標的情況下
* 在無指標的情況下,使用外部函式傳入變數時如下:<br>
```
void addone(int n){
n = n+1;
}
int main(){
int a = 3;
addone(a);//計算方式為複製a的值進去addone,而非將a傳入,因此不會改變a的值
print("%d",a);//此時a會印出3
}
```
* 無法直接複製陣列(同理無法複製字串)
* 無法動態變更陣列長度
### 指標基本簡介
* 指標為一種**資料型別**用以儲存**記憶體位置** !!很重要是資料型別
* 可解決問題:
* 呼叫函式時,直接更改引數值
* 複製陣列
* 複製字串
* 動態更改陣列長度
* 變數宣告語法:
`資料型別 *變數名稱; //表示變數內儲存的是資料型別的「記憶體位置」`
* 範例:
```
int count = 9;
int *countAddr = &count;
```
* 以上述範例而言,可整理成下表:
| 表示式 | 資料型別 | 值 |
| ----- | ------ | -- |
|count|int|9|
|&count|`int*`|數字9之儲存位置(為int)|
|countAddr|`int*`|數字9之儲存位置(為int)|
* 注意,雖然指標變數儲存的是整數,但是當中儲存的資料型別與int不同,而指標變數間不能自由轉型(無隱性轉型),如下:
```
int count = 9;
int a ;
int *b;
// type 1 compare
a = count //(o) int == int
b = count // (x) (int* ) != int
// type 2 compare
a = &count//(x) int != (int*)
b = &count //(o) (int*) == (int*)
```
### 指標的間接運算
#### 取址運算子 (`&`)
可使用取址運算子取得變數開頭的記憶體位置。
```
int count = 9;
int *countAddr = &count;
```
#### 間接運算子(`*`)
可使用間接運算子,取得開頭位於指定地址的變數
```
int count = 9;
int *countAddr = &count;
result = *countAddr;
```
| 表示式 | 資料型別 | 值 |
| ----- | ------ | -- |
|count|int|9|
|&count|`int*`|數字9之儲存位置(為int)|
|countAddr|`int*`|數字9之儲存位置(為int)|
|`*countAddr`|int|9|
|result|int|9|
#### 指標運算(基礎)
```
int count = 9;
int *countAddr = &count;
*countAddr = 10; => 等同於將count改為10 => 使用指標直接改變該address的位置
```
#### 指標與函式呼叫
函式呼叫時,複製記憶體位置(同樣是複製,但是由值改為記憶體位置)
```
#include <stdio.h>
void pass_pointer(int *n){//這邊的星號為告知資料型態
*n += 1; //這邊的星號為間接運算子
}
int main(){
int a = 3;
pass_pointer(&a);//函式所需要的變數為指標變數,因此傳入address
printf("%d",a);
return 0;
}
```
 以上述的方式編寫函式時,由於傳入的值為指標變數(複製address),因此在函式內做變動時更改的是address內的值,由此可以直接變更到a,而不同於之前的方式,變更到的是複製的值。
##### 簡易兩變數排序(由定義兩function,使用pointer)
```
#include <stdio.h>
void swap(int *a,int *b), sort(int *a,int *b);
int main(){
int a,b;
printf("please input your first and second number:");
scanf(" %d%d",&a,&b);
sort(&a,&b);
printf("your smaller number is %d,the bigger number is %d",a,b);
return 0;
}
void sort(int *a,int*b){
if (*a > *b){
swap(a,b);//注意這邊的傳入值,不能只傳入&a,&b,因為這兩個值存的是記憶體位置,因此需要先用間接運算子,touch到原本的值再傳送該值的記憶體(swap(&*a,&*b)),or 直接傳入a,b(因為已經是記憶體位置)
};
}
void swap(int *a,int *b){
int tmp = *a;
*a = *b;
*b = tmp;
}
```
#### 何時該傳數值本身或是數值位置
* 基本原則:
* 可以傳值就傳值 => 複製值比較安全,確保函式間乾淨,且用起來較方便
* 例外規則:
* 作為引數的變數在呼叫函式時會變動
* 無法直接複製值(陣列、字串)
* 複製成本過高(較複雜的結構)
#### 指標對整數的加減運算
```
int v[5];
// 可透過將陣列元素的位置加減一個整數,來求得其他元素的位置
&v[0] + 1 == &v[1];//從v[0]往前移動一個陣列元素後的位置(為C語言定義)
&v[1] + 1 == &v[2];//從v[1]往前移動一個陣列元素後的位置
&v[2] - 1 == &v[1];//從v[2]回推移一個陣列元素後的位置
&v[0] + &v[1] // 編譯失敗(x)
&v[2] - &v[1] == 1 // 從v[2] 位置到 v[1]位置距離1個元素
&v[3] - &v[0] == 3 // 從v[3] 位置到 v[0]位置距離1個元素
```
#### 陣列與指標間的關係
!!非常特別
```
int v[5]; //宣告一個有五個元素的陣列
int *n; //宣告定義一個int指標
n = &v[0];
// !!!陣列型別可以隱性轉型成該陣列第一個元素的記憶體位置(通常陣列無法直接複製)
n = v // n = &v[0]
```
透過指標運算存取陣列元素
```
int v[5];
int *n = v;
n == &v[0]; *n == v[0]; // *n = 0等價於v[0] = 0
n+1 == &v[1]; *(n+1) == v[1]; // *(n+1) == 0 等價於 v[1] = 0
n+2 == &v[2]; *(n+2) == v[2]; // *(n+2) == 0 等價於 V[2] = 0
```
#### 循序存取陣列元素
```
#include <stdio.h>
int main(){
int v[5] = {1,2,3,4,5};
int *n = v;
int i;
for (i = 0;i<5;i++) {
printf("%d\n",*(n+i));
}
return 0;
}
```
```
#include <stdio.h>
int main(){
int v[5] = {1,2,3,4,5};
int *n;
for (n = v;n != &v[5],n++){ //在此有一個很特別的點,雖然v[5]不存在,但是當在C語言之中寫存在陣列的下一個不存在元素時,其地址會被寫於該陣列尾端+1(下一個陣列開始)
printf("%d\n",*n);
}
return 0;
}
```
#### 指標與下標運算子(`[]`)
 在C語言之中,下標運算子具有下列特性:
```
v[0] = 0; // a[b] => *(a+b),v[0] => *(v+0),0[v] => *(0+v)
```
由上述規律可得下列:
```
int v[5];
int *n = v; //這邊會記錄v[0]的記憶體位置
n[0] == *n; //n[0] => *(n+0) = > *(&v[0]+0)
n[1] == *(n+1); //n[1] => *(n+1) = > *(&v[1]+0)
```
#### 使用指標在函式中傳遞陣列
```
int maxv(int[]);
int main(){
int a[3] = {3,9,7};
printf("Max: %d\n",maxv(&a[0]));
return 0;
}
int maxv(int *v){
int max = *(v+0),i;
for (i = 1;i<3;i++){
if (*(v+i) > max){
max = *(v+i);
}
}
return max;
}
```
#### 統整:指標與陣列關係
* 指標儲存某陣列元素位置時的特殊性
* 透過加減整數,可以算出陣列其他元素的記憶體位置
* 加N等於向後移動N個元素後的記憶體位置
* 減N等於向前(往回)移動N個元素後的記憶體位置
* `a[b]` 運算等同於`*(a+b)`,反之亦同
* 在陣列中從a開始往後移動b所在的陣列元素
* 當指標儲存某陣列的第一個元素記憶體後,使用起來與陣列幾乎相同
* 陣列可以隱性轉型成該陣列第一個元素的記憶體位置
#### 指標與遞增遞減運算子
```
int main(){
int v[5];
int i;
int *p ;
for ( p = v; p != &v[5];i++){
*p = 0;
}
return 0;
}
```
or 寫成:
```
int main(){
int v[5];
int *p = v;
while (p!= V+5){
*p++=0; //*(p++) = 0 => ++放於後,會獲得++前的值
}
return 0;
}
```
#### 指標與字串
* 字串以雙引號包裝時,可直接隱性轉成指標(不須先轉乘陣列)
* 複合字面常數 => 藉由強制轉型將變數轉型 printf((char[]){'t','e','s','t','\0'});
#### 字面常數特殊性
```
char strA[] = "test"; // 會開設記憶體空間(五個)儲存現有字串
char *strB = "test"; // 字串字面常數可隱性轉型成字元指標,於此種宣告方式,strB會儲存記憶體位置,而字串會被儲存於唯讀記憶體空間
strA[0] = 'T' // (o)
strB[0] = 'T' // (x) 未定義行為,因為儲存於唯讀空間
strA = "Test";//(x) 編譯失敗
strB = "Test"; // (o)
```
#### 字串字面常數與const char*
於使用字面常數儲存資料時,為了安全,會使用const字面常數儲存,在printf內傳遞資料時,傳遞的也是const資料,避免呼叫printf時產生未定義行為(害怕誤改內容),通常能放雙引號(字串字面)位置時,大多都會使用const char。
#### 指標與const
Type`*`可以轉成const Type`*`
```
char strA[] = "test";
char *strB = "test";
const char *strC = "test";
strA[0] = 'T'; //(o)
strB[0] = 'T'; //未定義行為
strC[0] = 'T'; //(x)編譯失敗
strA = strB; //(x) (char []) = (char * )
strA = strC; //(x) (char []) = (const char *)
strB = strA; //(o) (char *) = (char [])
strB = strC; //(x) (char * ) = (const char * )
strC = strA; //(o) (const char *) = (char [])
strC = strB; //(o) (const char * ) = (char *)
```
#### 陣列的指標
```
int (*q)[3] = &v; //q儲存具有三個整數的陣列v的位置
//存取
(*q)[2] =5
```
陣列傳遞差別
```
void print_int(int *n){
int i;
for (i=0; i<3; i++){
printf(" %d",n[i]);
}
printf("\n");
}
void print_addres(int (*q)[3]){
int i;
for (i=0;i<3,i++){
printf("%d",(*q)[i]);
}
printf("\n");
}
int main(){
int v[3] = {1,2,3};
print_int(v); //此方式傳遞的是v陣列開頭第一個元素的位置
print_address(&v); // 此方式傳遞的是V陣列的位置
return 0;
}
```
使用sizeof的情況下,如果傳入的是陣列的指標,則可以得到陣列大小:<br>
```
void print(int (*p)[3]{ //(但此情況傳入的陣列大小其實是固定的,不能傳入變動值)
int i;
for (i = 0;i<sizeof(*q)/sizeof((*q)[0]); i++){
printf("%d",(*q)[i])
}
printf("\n");
}
```
#### 指標間轉型的限制
 絕大部分的情況下,指向不同型別的指標間是不能隱性轉型的。
##### 合法的隱性轉型
1. 同類型別內的互轉
1. float = double
2. int = char
2. 整數與浮點數間互轉
1. int = double (無條件捨去)
2. double = int
3. 陣列可以隱性轉型成指向第一個元素的指標
1. int * = int [3]
4. Type * 可以隱性轉型成 const Type *
1. const int * = int *
5. void * 與其他型別的指標"可能"可以互轉
##### 指標與整數間的轉型
**電腦如何儲存指標?**
* 指標是一個儲存記憶體位置的資料型別
* 電腦中記憶體位置想像成一段連續空間,以位元組為單位,替每個位元組給定特定編號或表示法(每個電腦的表示法並沒有規定要相同(C語言的case下))
 void 可視為一個泛用型的記憶體空間,因此可自由轉型,在指標與整數型別中,無法確定記憶體位置所需的位元數,因此可能會發生整數的資料型別位元組不夠的情況。而是否可直接將整數轉型為指標,則需要依照強制轉型的實作定義,未必可成功。<br>
##### 指標間的強制轉型
**記憶體對齊**
 在記憶體的對齊上為實作定義,可能為對齊到偶數、或是以固定公差為對齊基準,可有效增加記憶體的讀取效率。以此類推,若是將char對齊為1、int對齊為4,則char的記憶體位置未必能儲存int,反之int的記憶體位置必定能儲存char,因此在轉型時,需要注意到對齊的大小,才能確保技藝體能互轉。
##### 改變陣列大小
* 不可行 -> 無法以現有的記憶體空間做擴充(因為陣列需要連續擺放,擴充的記憶體位置可能已經有儲存資料)
* 可行 -> 創造新陣列 ->使用指標實現
##### 動態記憶體(包含在<stdlib.h>內)
* 使用malloc函式動態配置記憶體
* void* malloc(size_t size);
* size為非負整數型態(size_t),表示要配置的記憶體空間大小(可由sizeof算出)
* 回傳值void* 表示可隱性轉型成其他資料型態的指標
* 範例: int* larger = malloc(sizeof(int) * (length+1));//創建一個大小為(length+1)個int大小的空間,larger指向的malloc空間不會被自動釋放
* 使用free函式釋放動態配置的記憶體
* void free(void* ptr);
* 使用realloc函式複製記憶體內容
* void* realloc(void* ptr,size_t size);
* ptr 是要重新配置的記憶體空間開頭位置
* size是重新配置後的記憶體空間大小
* 回傳值為重新配置後的記憶體空間開頭位置
* 在空間足夠的情況下,有可能使用擴充的方式執行,可能會較有效率。
##### 在函式中傳遞二維陣列
 編寫邏輯:<br>使用p儲存A陣列的內部陣列開頭位置,並將此值傳入至新的函式中,以此傳入內部陣列的開頭位置,而後面呼叫回全時,則會先進入p,也就是A陣列的相對應元素開頭位置,以此讀取內部值。
```
#include <stdio.h>
#include <stdlib.h>
int print_two_dimensional(int **p,int height,int wide){
for (int i = 0;i<height;i++){
for (int j = 0; j<wide;j++){
printf("%d",p[i][j]);
}
printf("\n");
}
};
int main(){
int A[2][3] = {{1,2,3},{4,5,6}};
int *p[2] = {A[0],A[1]};
//printf("%d",*p[0]);
print_two_dimensional(p,2,3);
return 0;
}
```
###### 儲存多個字串
1. 使用二維陣列儲存多個字串
```
char strA[3][4] = {"How","are","you"}; // 三個字卻使用四格空間,是因為字串結尾需要'\0'作為結束點。
```
2. 只用指標陣列儲存多個字串
```
const char *strB[3] = {"How","are","you"};//strB會儲存的是後面三個字串的開頭位置,為不可變動
```
兩者差異:<br>
```
strA[2][0] = 'Y'; // (o)
strB[2][0] = 'Y'; // (x) ->因為資料型態為const,為不可變動型態
strB[2] = 'What'; // (o) ->因為是更改整個陣列
strA[2] = 'What'; //(x) - >陣列不可直接被複製
```
3. 藉由儲存開始位置
```
char *strC[3] = {strA[0],strA[1],strA[2]};
strC[2][0] = 'Y'; // (o)相等於修改strA
char strD[5] = "What" // (o) ->讓字串由const char轉為char
strB[0] = strC;//(o)
strB[0][0] = 'w'; //(o)
```
##### 輸入多個字串
**ver 1 ->在輸入大於等於三個字串時會出現亂碼(還沒找到原因)**
```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
讓使用者輸入多字串,印出使用者輸入的字串,當使用者輸入END表示結束。
*/
int main(){
int size = 0; //紀錄共多少字元
char *save = NULL; //設定save用來儲存多個字串 (後續會用realloc使其變浮動)
char input[50]; //設定input字串大小
char **str = NULL; //雙重指標,用來儲存每個字串開頭的位置(後續會用realloc使其變浮動)
int len = 0; //紀錄總共儲存多少字串
while (1){
printf("please input your word:");
scanf("%s",input);
if (strcmp(input,"end") == 0) break;
int size_loop = size;
size += strlen(input)+1;
save = realloc(save,sizeof(char)*(size));
str = realloc(str,sizeof(char)*(len +1));
str[len] = &save[size_loop];
strcpy(str[len],input);
len++;
}
printf("-------\n");
int i;
for (i =0;i<len;i++){
printf("%s ",str[i]);
}
printf("\n(%d,%d)\n",len,size);
return 0;
}
```
**ver 2 可正常運作**
```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
讓使用者輸入多字串,印出使用者輸入的字串,當使用者輸入END表示結束。
*/
int main(){
int size = 0;
char *save = NULL;
char input[50];
char **str = NULL;
int len = 0;
while (1){
printf("please input your word:");
scanf("%s",input);
if (strcmp(input,"end") == 0) break;
size += strlen(input) + 1;
str = realloc(str,sizeof(char)*(len +1));
str[len] = calloc(strlen(input) + 1, sizeof(char));
strcpy(str[len],input);
len++;
}
printf("-------\n");
int i;
for (i =0;i<len;i++){
printf("%s ",str[i]);
}
printf("\n(%d,%d)\n",len,size);
return 0;
}
```
----
<a href ="https://hackmd.io/61b3sW1zR2q-rJboeJr0OA?both">C/C++ 學習筆記(PartI)</a>