**程式設計Ⅰ【筆記Ⅲ】**
===
* [name=Ivy Lin]
* [筆記1](https://hackmd.io/NPFilj4fSNu020tfh5s3TQ)
* [筆記2](https://hackmd.io/50cD9oibROGhAn2loo5v6g)
## 第十三週<2023.5.9>
### 指標
1. **不同的指標type,用++符號的差別**
* 令指標變數時,會依照不同型別來區分一個資料
* 因此,每個指標type加上的值都不一樣
(char:1|int:4|float:4|double:8|long long:8)
2. **等價指標寫法**
* 取值: a[i] <=> * (a+i)
* 取址: &a[i] <=> (a+i)
3. **定義新type:**
```clike=
//typedef 可以定義新的型別名稱以利辨識
typedef (型別) (名字) ;
```
4. **指標的門牌號碼要先定義好才可以定義它的值**
5. **二維陣列的指標**
* a[0][0]的地址和a[0]地址相同
* 對大指標+1 =>位移整個格數
* 對小指標+1 =>位移一格
Ex. a[5][5]
(1) a+1 = a[1][0]
(2) a[0]+1 = a[0][1]
* 二微陣列指標變數宣告
```clike=
//定義一指標,所指的整數陣列包含n個整數元素
//若定義int z[4][2],則指標變數要訂int (*ptr)[2]
//對陣列指標變數做+1,會跳到下一個位元組(意即:z[0]->z[1])
int (*指標名稱)[陣列數量n]
//定義一個陣列、每個元素均為指向整數的指標
int *指標名稱[陣列數量n]
```
## 第十四週<2023.5.16>
### 指標
1. **指標變數宣告**
```clike=
int *pt; //儲存int型別地址的指標
int **p2; //儲存int*型別地址的指標
int (*pa)[3]; //二維陣列一次跳過三格int的指標
int a[2][3], b[3][2];
pt=&a[0][0]; //<=> pt=&a; <=> pt=a;
pt=a[0]; // <=>t=&a[0];
pa=a; // 單位為三的int指標(陣列)
pa=b; // 會出現錯誤(單位不同)
p2=&pt; // 指向int型別指標的指標www
*p2=b[0]; // *p2=>位址 可以指向b[0](指標位置)
p2=b; // p2(指向指標的指標) != b(指標) 錯誤
```
2. **傳遞二維陣列到函數**
```clike=
int function(int (*arr)[COLS]);
// int function(int [][COLS]);
// int function(int arr[][COLS]);
```
3. **應用陣列來排序**
```clike=
//pseudo code => Insertion Sort
/*又稱插牌法,將元素一個一個比較後插入陣列中*/
void insesertion(int *arr,int size){
for(int i=1;i<size;i++){
int v=arr[i],j=i-1;
while(j>=0 && arr[j]>v){
arr[j+1]=arr[j];
j--;
}
arr[j+1]=v;
}
}
```
4. **指標v.s遞迴v.s陣列**
=>利用指標做陣列的遞迴
```clike=
#include <stdio.h>
void permutation(char * a, int n);
void swap( char *, char *);
int main(void)
{
char str[] = "cats";
permutation(str, 4);
return 0;
}
void permutation(char *a, int n)
{
int i;
if (n == 1) {
printf("%s\n", a);
}
else {
for (i = 0; i < n; i++) {
swap(&a[i], &a[n-1]); // 交換元素
permutation(a, n-1);
swap(&a[i], &a[n-1]); //遞迴回到原狀態
}
}
}
void swap(char *x, char *y)
{
char tmp;
tmp = *x;
*x = *y;
*y = tmp;
}
```
5. 字串處理<string.h>
* strcmp
* 比較字串長度是否相等
```clike=
int strcmp ( const char * str1, const char * str2 );
```
>str1 : the first string to be compared
>str2 : the second string to be compared
> < 0 : str1 is less than str2 lexicographically
> 0 : str2 is less than str1
> == 0 : str1 is equal to str2
* strcpy
* 複製字串from到字串to(會覆蓋)
```clike=
char * strcpy ( char *restrict to, const char *restrict from );
```
* strncpy
* 從字串from複製n個元素到自串to(會覆蓋)
```clike=
char * strncpy ( char *restrict to, const char *restrict from, size_t count );
```
* strcat
* 將字串from接到字串to後面
```clike=
char *strcat( char *restrict to, const char *restrict from );
```
* strncat
* 從字串from複製n個元素接到自串to後面
```clike=
char *strncat( char *restrict to, const char *restrict from, size_t count );
```
* memset
* 設定開頭,可以將count個字元填上想要的ch
```clike=
void *memset( void *dest, int ch, size_t count );
```
* memcpy
* 複製count個字元從from複製到to
```clike=
void* memcpy( void *to, const void *from, size_t count );
```
6. 實作strcpy strcat
```clike=
#include <stdio.h>
#include <string.h>
void Strncpy(char *to, char *from, int n) {
while (*from != '\0' && n>0) {
*to = *from;
to++;
from++;
n--;
}
}
void Strncat(char *to, char *from, int n) {
while(*to != '\0') to++;
Strncpy(to, from, n);
}
int main(void)
{
char str1[] = "abc";
char str2[10] = {0};
Strncat(str2, str1, 2);
Strncat(str2, str1, 2);
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
}
```
7. 反轉字串
```clike=
#include <stdio.h>
#include <string.h>
void reverse(char *start, char *end) {
char tmp;
while (start < end) {
tmp = *start;
*start = *end;
*end = tmp;
start++;
end--;
}
}
int main(void)
{
char str[256];
int i;
char *start;
fgets(str, 255, stdin);
// this is a book \0
str[strlen(str)-1] = ' ';
start = str;
for (i=0; i<strlen(str); ++i) {
if (str[i] == ' ') {
reverse(start, str + i - 1);
start = str + i + 1;
}
}
reverse(str, str+strlen(str)-2);
str[strlen(str)-1] = '\0';
printf("%s\n", str);
}
```
## 第十五週<2023.5.23>
### 指標
#### Qsort
```clike=
void qsort( void *ptr, size_t count, size_t size, int (*comp)(const void *, const void *) )
//Qsort在main函數中的寫法
qsort((int *)input, n, sizeof(input[0]), compare);
qsort(input, n, sizeof(input[0]), compare);
```
* ptr
>指向要排序的陣列的起始位置的指標
>也可以直接寫陣列名字!
* count
>在陣列中有幾個元素要排序
* size
>在陣列中每個元素的大小,以 byte 為單位
>sizeof是可以求大小的函數,記得要是"元素"大小
* comp
>比較
>要注意的一點是:compare需要自己寫!
```clike=
//簡單int比較
qsort(input, n, sizeof(int), compare);
int compare(const void *a, const void *b)
{
//下面這句就是說將a改成(int *的指標)然後在對a取值
int x = *(int *)a;
int y = *(int *)b;
if (x < y)
return -1;
else if (x > y)
return 1;
else
return 0;
}
//比較複雜的char比較
//x代表一筆資料有幾個元素EX.char input[5][4];=>x=4
qsort(input, n, x*sizeof(char), compare);
int compare(const void *a,const void *b){
char c=*(char *)a;
char d=*(char *)b;
//strcmp就是比較字串大小
return strcmp(c,d);
}
```
#### 一些指標的tips
```clike=
//陣列指標=>型別 名稱 [size][size]
//int陣列
int input[10][2];
int (*ptr)[2]; //指位元2的指標變數
//char陣列
char arr[10][5];
char (*pt)[5];
//函數指標=>記得函數的位址
int f(int *,int);
int (*p)(int *,int);
p=f;
```
### 實作一個可以存函數的指標
```clike=
# include <stdio.h>
# include <stdlib.h>
int sum(int a[], int n)
{
int i, ans = 0;
for (i=0; i<n; i++) {
ans += a[i];
}
return ans;
}
int sum_squared(int a[], int n)
{
int i, ans = 0;
for (i=0; i<n; i++) {
ans += a[i]*a[i];
}
return ans;
}
int middle(int a[], int n)
{
return a[n/2];
}
int run ( int (*fp) (int *, int ) , int * a, int n)
{
return fp(a, n);
}
int main(void)
{
int a[] = {1, 2, 3, 4};
printf("%d\n", run(sum, a, 4));
printf("%d\n", run(sum_squared, a, 4));
printf("%d\n", run(middle, a, 4));
return 0;
}
```
### 取得記憶體的方法
```clike=
void* malloc( size_t size );
void free( void* ptr );
```
>size
要分配的記憶體大小,以 byte 為單位
>回傳值
如果成功的話,回傳的指標是指向分配的的記憶體的開頭位址
如果失敗的話,回傳一個空指標
>ptr
要釋放的記憶體的指標,必須是當初分配某塊空間時拿到的那個位址;如果ptr為NULL,則此函式不會做任何事情
```clike=
//令一個指標變數來存放malloc回傳的指標(記得轉型別)
//動態一維
int *ptr;
ptr=(int *)malloc(arr_size*(sizeof(int)));
//ptr有可能會是NULL
free(ptr);
//動態二維(第一種方式)
//直接數全部的陣列
int row,column;
double *pt;
pt=(double *)malloc((sizeof(double)*row*column));
//使用上就只能自己來換算index了!
//比如說pt[i*column+j];
for (int i=0; i<rows; ++i) {
for (int j=0; j<columns; ++j) {
pt[i*columns+j]=某個你想放的資料;
}
}
free(pt);
//動態二維(第二種方式)
//記住每個row的開頭地址
int row,col;
char p = (int**) malloc(row*sizeof(int*));
//使用=>利用創建的二維空間記住原本陣列的開頭地址
char array = (int*) malloc(row*col*sizeof(int));
for (i=0; i<row; ++i) {
p[i] = &array[i*col];
}
//然後再一一填入後面的資料
for (i=0; i<row; ++i) {
for (j=0; j<col; ++j) {
p[i][j] = 某個你想放的資料;
}
}
free(p);
```
### 字串與指標
* 在傳遞字串時,會用指向字串的第一個字元的指標作為該字串的代表
1. string literal 的型別是 char[]
2. 如果是用指標去指向某個 string literal,那麼其指向的內容是不可寫的
```clike=
const char* str = "string"; // 如果寫出 str[0]='t',在編譯時就會出錯
char str[] = "string"; // str[0]='S' 是可以的
```
>const char* str = "string";
>char str[] = "string";
>兩種是不同的!
### 更改指標值的函數
* 為了更改指標(本身有一層* 了!),函數會傳入指標的地址(&指標名),為了存取這個變數,在宣告函數時需要多一層*(也就是宣告成"型別**指標名)
>因為在收地址時,會使用到取址運算子(&),為了消除它的作用,需要額外加上星號來抵銷,剩下就是指標原本宣告的型別
```clike=
//實作
void malloc_float2( float* * q , size_t sz) // q = &ptr
{
*q = (float *) malloc(sz*sizeof(float)); // *q => *&ptr => ptr
}
int main(void)
{
float * ptr = NULL;
int i;
int n = 100;
malloc_float2(&ptr, n);
}
```
## 結構structure
Structures 是可以由使用者自己定義,由其他型別 (甚至是 structure) 的變數所組成的一個資料型別
這樣的方式在使用上較方便,也會讓程式更簡潔易懂
```clike=
//定義x,y是平面上的座標
struct point{
int x;
int y;
};
//使用要加上 struct point
//不想加struct的寫法=>
typedef struct {
int x;
int y;
} point;
//pt會包含x和y兩個 members,要更改 members 的內容,使用 . 來存取
//如果是指標變數,則可以透過->來存取
point a={5,10};
a.x=20;//將a點的x座標改成20
point* b=&a; //point變成新的型別,所以可以令一個point*的指標
(*b).x=20; //*b==a
b->x=20; //指標要直接改數值就用->
```
* 小撇步:宣告函數變數是指標時,傳入的值一定是地址(也救世會需要取址運算子&)