How to program C〈指標〉

基本介紹

指標宣告

1
2
3
4
5
6
// countPtr 型態為 int*
// 表示指向 int 的指標
int *countPtr;

// 以下宣告只有 a 是指標 b 不是
int *a,b;

指標表示如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
count addr = 0060FF08
countPtr addr = 00000000
*/
int count = 7;
int *countPtr = NULL;

// countPtr addr = 0060FF08
countPtr = &count;

/*
值 0060FF08 7
名稱 countPtr count
位址 0060FF0C 0060FF08
*/

宣告指標時最好就要給初始值或設為 NULL

NULL 定義在 stdio.h 中

指標運算子

& 為其中一種運算子,稱為位址運算子

目的就是將後面的變數位址取出,注意 & 只能用在變數

不能用在常數、運算式或宣告為 register 的變數

  • 稱為取值運算子或為間接運算子

他會取出指標所指變數位址的值,也就是對指標求值

  • 和 & 是互補的,也就是 *&valPtr = &$valPtr

Call Func by Ref

有兩種方法傳參數給 func,傳值和傳址

傳值的好處是可以減少一些負擔

如果你要傳一個物件很大,而使用傳值時

會複製這一個物件而造成空間負擔

const 在 pointer 上使用

const 告訴編譯器,某變數的值不能修改

在函式參數使用 const 有 6 種情況

傳值 2 種、傳址 4 種

不同種類是依照你要給函式多大權力來決定

另外傳值只能改變主函式的一個值(利用 reutrn)

要改兩個值就只能用傳址

傳址給函式有 4 種方法

1
2
3
4
1.指向 "非常數 data" 的 "非常數 pointer"(什麼都能做)
2.指向 "常數 data" 的 "非常數 pointer"(保護資料)
3.指向 "非常數 data" 的 "常數 pointer"(陣列)
4.指向 "常數 data" 的 "常數 pointer"(什麼都不能做)

4 種代表不同權限,最高的是第 1 種

以下為傳入一字串,將小寫改大寫的函式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void covToUp(char *strPtr)
{
while( *strPtr != '\0' )
{
if( (int)(*strPtr)>= 97 && (int)(*strPtr)<= 122)
{
*strPtr = (int)(*strPtr)-32;
}
/* 也可以用內建函式轉
if( islower(*strPtr) )
{
*strPtr = toupper(*strPtr);
}
*/
strPtr++;
}
}

int main()
{
/*
a = 97 z=122
A = 65
a -> A = -32
*/
char str[] = "characters and $32";
printf("1. str = %s\n",str);
covToUp(str);
printf("2. str = %s\n",str);
return 0;
}

第 2 種為指向 “常數 data” 的 “非常數 pointer”

也就是說,pointer 可以指向其他變數,但變數本身的值不能改

此指標宣告為 const char *ptr 要由右往左念!

ptr 是一個指標(*),他指向的變數是常數字元(const char)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void printChar(const char *strPtr)
{
while( *strPtr != '\0' )
{
printf("char = %c\n",*strPtr);
/* 加入以下會編譯錯誤
*strPtr = toupper(*strPtr);
*/
strPtr++;
}
/* 就算在這裡將傳入的指標設為 NULL
回到 main str 的位址還是不變
代表 strPtr 只是一個副本而已
*/
strPtr = NULL;
}


int main()
{
char str[] = "characters and $32";
printf("1.str addr = %p\n",&str);
printChar(str);
printf("2.str addr = %p\n",&str);
return 0;
}

結構傳給 func 時都是傳值,但這樣很浪費空間

所以你可以改用此種方法傳給 func

再來第 3 種方法指向”非常數 data” 的 “常數 pointer”

此種 pointer 永遠指向同一個位址

就像陣列變數一樣,陣列變數永遠指向陣列第一個位址

但是我們又可以藉由此指標更改裡面儲存的值

宣告此種指標為 char *const ptr,一樣由右往左念

ptr 是一個常數指標(*const),他指向字元類型

最後一種存取全縣最低,宣告為const char *const ptr

氣泡排序

氣泡排序可以由小排到大或由大排到小,邏輯上是一樣的

以小到大來說,每一回合就是兩兩比較

每一回合都會把最大的搬到最後面,總共會進行 N-1 回合

舉例來說,有個陣列是 6,3,9,2 (共 4 個數字)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
第 1 回合
(6) 3 9 2 -> 3,6,9,2
3 (6) 9 2 -> 3,6,9,2
3 6 (9) 2 -> 3,6,2,9
第 1 回合結束 9 最大放在最後面

第 2 回合
(3) 6 2 -> 3,6,2
3 (6) 2 -> 3,2,6
第 2 回合結束 6 最大放在最後面

第 3 回合
(3) 2 -> 2,3
第 3 回合結束 3 最大放在最後面


最後為 2,3,6,9

程式碼如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void bubbleSort(int aInt[])
{
int aLen = sizeof(aInt);
int i = 0;
int j = 0;
for(i = 0 ; i<aLen-1 ;i++)
{
for(j = 0 ; j<aLen-1 ;j++)
{
if( aInt[j] > aInt[j+1] )
{
int temp = aInt[j];
aInt[j] = aInt[j+1];
aInt[j+1] = temp;
}
}
}
}

函式指標

和陣列一樣,函式名稱也就代表函式的起始位址

以下程式示範將函示指標傳入某個函式參數中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void pArray(int aInt[],int aLen)
{
int i = 0;
for(i = 0 ; i<aLen ;i++)
{
printf("aInt[%d] = %d\n",i,aInt[i]);
}
}


int asc (int, int);
int desc (int, int);
void bubbleSort (int [], int , int (*)(int,int));

int asc(int a, int b)
{
return b<a;
}

int desc(int a, int b)
{
return b>a;
}

void bubbleSort(int aInt[], int aLen, int (*comp)(int,int))
{
int i = 0;
int j = 0;
for(i = 0 ; i<aLen-1 ;i++)
{
for(j = 0 ; j<aLen-1 ;j++)
{
//if( aInt[j] > aInt[j+1] )
if( (*comp)(aInt[j],aInt[j+1]) )
{
int temp = aInt[j];
aInt[j] = aInt[j+1];
aInt[j+1] = temp;
}
}
}
}

int main()
{
int val[] = {6,21,3,9,2,10};
int aLen = sizeof(val)/sizeof(val[0]);
bubbleSort(val, aLen, desc);
return 0;
}