Transcript Int main()
第八章
指標(Pointer)
1
大綱
•
•
•
•
•
•
8.1 位址運算子&
8.2 指標之宣告
8.3 指標之使用
8.4 記憶體配置
8.5 指標與陣列
8.6 指標與字串
•
•
•
•
•
•
2
8.7 指標與函數
8.8 指標與結構
8.9 指標中之指標
8.10 指標指向函數
8.11 指標陣列
8.12 指標常犯之錯誤
指標與變數
int x=5;
int * ptrX = &x;
cout << *ptrX << endl;
名稱
“x”
“ptrX”
位址
0x03
0x11
值
5
0x03
3
Pointer Indirection (Pointers to Pointers)
What is the result?
58 58 58
8.1 位址運算子 &
• 一般變數皆為靜態的資料,若欲取得該變數之位址,只能
使用位址運算子(Address Operator) 「&」 (Ampersand)
• int a=10;
• cout << &a; //輸出位址 00A5
• cout << a; //輸出內容 10
5
例題: 列印一般變數之位址。
#include <iostream>
//cout
using namespace std;
int main( ){
short a=10, b; //短整數佔2bytes
int c=23, d;
//整數佔4 bytes
float p=1.1, q; //浮點數佔4bytes
cout << “&a=” << &a << endl;
cout << “&b=” << &b << endl;
cout << ”&c=” << &c << endl;
cout << “&d=” << &d << endl;
cout << ”&p=” << &p <<endl;
cout << “&q=” << &q << endl;
return 0;
}
位址會因電腦不同而異
6
8.2 指標之宣告
• 指標是一種間接指向,在宣告上與一般變數不同。
• 資料型態 *變數名稱; //星號在變數之左上方
如 int *a, *b, *c;
• 資料型態* 變數名稱; //星號在資料型態之右上
如 int* a, * b, * c;
• 宣告 int *ptr 後變數ptr稱為指標變數,它是儲存『位址的值』,
*ptr為內容,其意義如下:
• *ptr: 表ptr位址內所指示之內容
• ptr: 表儲存位址
7
8.3 指標之使用
• 指標變數在使用時,不可指向不存在(void)之位置,否則會造成
不可預料的結果,使用指標應遵循下述兩種狀況:
• 1. 指向已存在之位址。
• 2. 要求分配記憶體。
• 讓指標指向已存在位址就是將一般變數之位址設定給指標變
數,讓一般變數與指標共用同一塊記憶體,只要一般變數內容改
變,指標變數之內容也會改變,反之亦然。
• int a=10,*p;
a
10
*ptr
• p=&a;
8
8-3-1指標與位址運算子
• *與&兩者皆為指標之參考符號
• 兩者之關係如:
• int *ptr, a=10; //宣告ptr為指標變數
• ptr = &a; //ptr存變數a的位址
• 將a之位址設定給ptr,接著兩變數皆指向同一塊記憶體,變數利
用「&」取得位址 。
a
10
*ptr
9
8-3-2 指標型態之一致性
• 指標型態之一致性: 指向已存在之指標變數應與其資料型態
一致,因不同型態之指標是無法轉換的。
• 列印數值(內容):
1. 一般變數 cout << a;
2. 指標變數 cout << *ptr;
• 列印位址:
1. 位址運算子 cout << &a;
2. 指標變數 cout << ptr;
10
8-3-3 指標之設定
• 同樣指標型態可以互相設定,亦即可使許多指標指向同一位
址,如下例,p1與p2同樣指向x,即三者指向同一地點。
*p1
x
10
*p2
11
例題: 指標間之設定。
#include <iostream>
//cout
using namespace std;
int main( ){
int x=10;
int *p1, *p2;
p1 = &x;
p2 = p1;
//指標間之設定
cout << "p2=" << p2 << endl;
cout << “*p2=”<<*p2<<endl
cout <<”*p1=”<<*p1<<endl;
cout <<”x=”<<x<<endl
return 0;
}
執行結果:
p2=0064FE00
*p2=10
*p1=10
x=10
12
8-3-4 指標位址之運算
•
•
•
•
•
指標位址之遞增或遞減,如宣告*ptr;後之ptr++, ++ptr
或ptr--, --ptr等係針對所存在之位址作該資料型態大
小作加減,而非加一或減一之運算。
如:int *ptr, a[3]={10,20,30};
ptr = &a;
若&a之位址為「0064FE00」,則ptr之內容亦為
「0064FE00」。
ptr++; // ptr之內容就變為「0064FE04」
13
資料型態遞增減之增減量
•
單位為bytes
宣告
運算
增減量
char *ch;
ch++;
ch--;
1
int *ptr;
ptr++;
ptr--;
4
float *f;
f++;
f--;
4
double *d;
d++;
d--;
8
14
例題: 利用指標作運算。
#include <iostream>
//cout
using namespace std;
int main( ){
int *ptr, a=10;
ptr = &a;
cout << ptr << ","<< &a << endl;
cout << *ptr << a << endl;
(*ptr)++;
cout << "a="<< a << endl;
return 0;
}
15
例題:字串利用指標作運算。
#include <iostream>
//cout
using namespace std;
int main( ){
char *ch, s[6]="abcde"; //字串
ch = s;
cout << ch << ","<< ++ch << endl;
cout << s << ","<< s+1<< ","<< s[4] <<endl;
return 0;
}
16
例題:整數陣列利用指標作運算。
#include <iostream>
//cout
using namespace std;
int main( ){
int s[5]={10,20,30,40,50};
int *ptr = s;
for (int i=0; i<5; i++)
cout << "s["<<i<<"]="<< &s[i] <<endl;
cout << "*ptr內容="<< *ptr << endl;
cout << "ptr位址="<< ptr << endl;
ptr = ptr + 1;
cout << "ptr+1內容="<< *ptr << endl;
cout << "ptr+1位址="<< ptr << endl;
return 0;
}
17
8.4 記憶體配置
•
•
•
對於陣列大小設定需給定一常數值,且大小設定後更改困
難, 用完後記憶體無法歸還。
下列方式是錯誤的:
cin >> size;
int a[size];
指標要求分配記憶體時,指令敘述為new, 當記憶體使用
完要歸還或釋放時指令敘述為delete, 兩者必須相互的
出現。
18
8.4.1 new與delete之使用
•
•
•
分配或釋放記憶體之敘述格式:
指標變數 = new 資料型態[大小](初值); //配置
delete [ ] 指標變數; //釋放
•
1.
2.
3.
配置記憶體
配置單一記憶體
配置單一記憶體並給予初值
配置多個記憶體:
以指標當陣列使用,有固定大小及彈性大小。
19
8.4.2 配置單一記憶體
• 指標變數 = new 資料型態;
• delete 指標型態;
– 如:
– double *f;
– f = new double; //配置一個浮點數空間
– delete f; //釋放一個空間,在delete 後不必加入[ ]
20
例題: 配置記憶體空間,輸入資料及顯示指標變數位址
#include <iostream>
//cout
using namespace std;
int main( ){
int *a, *b, c;
a= new int; b= new int; //分配空間
cout << "a之位址="<< a << endl;
cout << "b之位址="<< b << endl;
cout << "c之位址="<< &c << endl;
cout << "輸入a, b="; cin >> *a >> *b;
c = *a + *b;
cout <<"c=" << c << endl;
delete a; //交回分配之記憶體
delete b;
return 0;
}
a之位址=0x00672d54
b之位址=0x00672d64
c之位址=0x0064fe00
輸入a,b=10 20
c=30
21
8.4.3 配置單一記憶體並給初值
• 指標型態 = new 資料型態(初值);
• delete 指標型態;
– 如:
int *a;
a = new int(10);//則*a之值為10,而非設定10個int空間
或
int *a=new int(10); //宣告,配置與初值合併
22
例題: 指標初值之設定(來自計算值)
#include <iostream>
//cout
using namespace std;
const double PI=3.14159;
const double radius = 10.0;
int main( ){
float *area=new float(PI*radius*radius);
cout << "Area="<<*area<<endl;
cout << area << endl;
delete area;
cout << *area << endl;
cout << area << endl;
return 0;
}
23
8.4.4 配置多個記憶體
• 指標變數 = new 資料型態[大小];
• delete [ ] 指標變數;
– 如:
– int *a;
– a=new int[5]; //配置5 * 4=20 Bytes給a,固定大小。
– delete [ ] a;
//釋放a之空間
24
例題:以指標輸入10個浮點數資料,求平均。(固定大小)
#include <iostream>
//cout
using namespace std;
int main( ){
float *f, sum=0, ave;
f = new float[10];
cout << "Enter 10 data:\n";
for (int i=0;i<10;i++){
cin >> *(f+i);
sum += *(f+i);
}
ave=sum/10.0;
cout << “10個數平均=" << ave << endl;
delete[ ] f;
return 0;
}
25
例題: 指標資料之排序,輸入三指標變數資料後由小到大輸出。
#include <iostream>
//cout
using namespace std;
void swap(int *a ,int *b);
int main( ){
int *a,*b,*c;
a = new int; //配置記憶體
b = new int;
c = new int;
cout << "輸入a,b,c=";
cin >> *a >> *b >> *c;
swap(a,b); //交換
swap(b,c);
swap(a,b);
cout << "排序後:"<<*a<<" "<<*b
<<" "<<*c<<endl;
delete c; //釋放記憶體
delete b;
delete a;
return 0;
}
void swap(int *a, int *b){
if (*a>*b) {
int t = *a;
*a = *b;
*b = t; } }
26
例題: 以指標來設定彈性陣列。
#include <iostream>
//cout
using namespace std;
int main( ){
int *a, size, i;
cout <<"輸入陣列大小:"; cin >> size;
a = new int[size]; //分配記憶體
for (i=0; i<size; i++){
cout <<i<<“:”<< (a+i) <<“="; //輸出位址
cin >> *(a+i); }
cout <<”輸出資料:”;
for (i=0; i<size; i++)
cout << *(a++) <<" ";
delete[ ] a; //釋放記憶體
return 0; }
27
8.5 指標與陣列
• 指標與陣列兩者具有相當的密切關係,如:
– int str[30], *ptr;
– ptr = str; 或 ptr = &str[0];
• 表ptr已被設定到str陣列第一個元素,因此要存取第五個元素
可以如下寫法:
– str[4];
– *(str+4);
– *(ptr+4);
28
陣列存取資料之方法
• 設宣告 int a[5], *ptr;
–以標準陣列方式存取, 如a[n]。
–以陣列名稱加上索引值來存取,如*(a+n)。
–以指標變數加上索引值來存取,如*(ptr+n),
需事先執行ptr=a或ptr=&a[0]。
• 以指標來存取陣列之元素比以索引來存取陣列之元素
之速度較快。
29
8.5.1 陣列名稱之指標用法。
#include <iostream>
//cout
using namespace std;
int main( ){
int *ptr, a[5]={5,10,15,20,25}, i;
cout <<” 標準存取: ”;
for (i=0; i<5; i++)
cout << a[i] << “ “;
cout << endl;
cout <<” 陣列名稱存取: ”;
for (i=0; i<5; i++)
cout << *(a+i) << “ “; cout << endl;
cout <<” 指標存取: ” ;
ptr = a;
for (i=0; i<5; i++)
cout << *(ptr+i) << “ “; cout << endl;
return 0;
}
30
指標與陣列
指標變數
陣列名稱
位址
內容
存取方式
ptr+0
a+0
0066FF00
5
a[0], *(a+0), *(ptr+0)
ptr+1
a+1
0066FF04
10
a[1], *(a+1), *(ptr+1)
ptr+2
a+2
0066FF08
15
a[2], *(a+2), *(ptr+2)
ptr+3
a+3
0066FF0C
20
a[3], *(a+3), *(ptr+3)
ptr+4
a+4
0066FFF0
25
a[4], *(a+4), *(ptr+4)
ptr
0066FF00
31
8.5.2 以指標變數取代陣列
#include <iostream>
//cout
using namespace std;
const N = 5;
int main( ){
int a[N], *ptr,i;
ptr = a;
//或ptr=&a[0]
cout << "輸入”<<N<<”筆資料:";
for (i=0; i<N; i++)
cin >> *(ptr+i); //以指標輸入
cout << "以陣列名稱輸出:";
for (i=0; i<N; i++)
cout << *(a+i) << " ";cout << "\n";
return 0;
}
32
8.5.3 陣列各元素位址之取得
#include <iostream>
//cout
using namespace std;
const N = 5;
int main( ){
int a[N], i, *ptr;
cout << "以陣列元素:\n";
for (i=0; i<N; i++)
cout <<"a["<<i<<"]="<< &a[i] <<endl;
cout << "以指標:\n";
ptr = a; //或 ptr = &a[0];
for (i=0; i<N; i++)
cout <<“ptr+”<<i<<“=”<< ptr+i <<endl;
cout << "以陣列名稱:\n";
for (i=0; i<N; i++)
cout <<"a+"<<i<<"="<< (a+i) <<endl;
return 0; }
33
8.5.4 指標變數與二維陣列
#include <iostream>
//cout
using namespace std;
const int N=4,M=3;
int main( ){
int a[N][M], i, j, *ptr;
ptr = &a[0][0];
for ( i=0; i<N*M; i++)
cin >> *(ptr+i);
cout <<” 陣列資料如下:\n”;
for (i=0; i<N; i++){
for ( j=0; j<M; j++){
cout.width(3);
cout << *(*(a+i)+j) << " "; }
cout << "\n"; }
return 0;
}
34
8.6 指標與字串
在C++內字串本身就是指標,因字串是屬於字元陣列, 如:
#include <iostream>
//cout
using namespace std;
int main( ){
char *ptr;
char s[ ]="This is a string.";
cout << "s= " << s << endl;
ptr =s;
cout <<”輸入字串:” ;
cin >> ptr;
cout << "輸入後: s=" << s << endl;
return 0;
}
35
s=This is a string,
輸入字串:Clinton
輸入後:s=Clinton
例題: 將指標內字串搬移到另一字串內及ptr++之使用。
#include <iostream>
//cout
using namespace std;
int main( ){
char *ptr="This is pointer string.";
char s[25]; int i;
cout << "ptr= " << ptr << endl;
for (i=0; i<strlen(ptr)+1; i++)
s[i]=*(ptr+i);
cout << "搬移後 : s=" << s << endl;
ptr++; //ptr=ptr+1;指向第二位置
cout << “ptr++=”<<ptr<<endl;
ptr=ptr+4;//跳過4個位置到第6個位置
cout << “ptr+4=”<<ptr<<endl;
return 0;
}
ptr=This is pointer string.
搬移後:s=This is pointer string.
ptr++=his is pointer string.
ptr+4=is pointer string.
36
1
2
3
4
T
h
i
s
ptr+0
ptr+1
ptr+2
ptr+3
9
10
11
p
o
i
ptr+8
ptr+9
5
6
7
i
s
ptr+4
ptr+5
ptr+6
ptr+7
12
13
14
15
…
n
t
e
r
…
ptr+10 ptr+11 ptr+12 ptr+13 ptr+14
37
8
…
8.7 指標與函數
• 函數之傳回值若以函數名稱傳回只有一個值, 這是以
return傳回數值。
• 函數除了可傳回值外, 還可以利用參數傳回參考位址與指
標位址, 解除只能傳回一個之限制。
• 若要傳回一個以上之資料就需利用參數之傳遞以傳回多個
值, 使用方式有傳址(參考)呼叫(Call by Reference)及指
標呼叫(Call by Pointer), 而函數名稱除了傳數值外亦可以
傳址呼叫及指標呼叫傳回。
38
函數與指標之可能的組合
宣告
呼叫
函數定義
說明
int a;
sum(&a,...);
void sum(int *a,...)
指標參數傳回
int a;
sum(a,...);
void sum(int &a,...)
參考參數傳回
int *a;
sum(a,...);
void sum(int *a,...)
指標參數傳回
int *a;
a=sum(…);
int *sum(…)
函數名傳回
int *a, *b;
a=sum(b,...);
int *sum(int *b,...)
函數名,指標參數
傳回
39
8.7.1 一般變數傳給指標
• 一般變數傳遞參數給函數, 若需再傳回, 在函數之參數需宣告為指標變
數, 而呼叫者需以位址運算子「&」告訴函數
– 資料型態 函數名稱(資料型態*, …); //函數原型
– void call_fun( int*, int*);
– 呼叫函數(&一般變數, …); //呼叫函數
– 如: { int a, b;
– call_fun(&a, &b); …}
– 資料型態 函數名稱(資料型態 *變數, …) //函數定義本體
– void call_fun( int *p, int *q) {….}
40
例題: 寫一函數傳回兩輸入資料給主程式後,在主程式內求和
後輸出。
#include <iostream>
//cout
using namespace std;
void get_data(int*, int*);
int main( ){
int a,b,sum;
get_data(&a,&b);
sum = a + b;
cout << "sum=" << sum << endl;
return 0; }
void get_data(int *a, int *b) {
cout << "輸入a,b=";
cin >> *a >> *b; }
輸入a, b=10 20
sum=30
41
共用同一塊記憶體
主程式
函數get_data
內容
位址
位址
a
10
0066FF00
→
0066FF00
a
b
20
0066FF04
→
0066FF04
b
42
例題: 寫一函數將兩數互換後在主程式輸出(指標接收)。
#include <iostream>
//cout
using namespace std;
void swap_data(int*, int*);
int main( ){
int a=10, b=15;
cout << "交換前:" << a <<" "<< b << endl;
swap_data(&a, &b);
cout << "交換後:" << a <<" "<< b << endl;
return 0; }
void swap_data( int *a, int *b ){
int t;
t = *a;
*a = *b;
*b = t;
}
43
交換前: 10 15
交換後: 15 10
8.7.2 傳址方式傳參數
#include <iostream>
//cout
using namespace std;
void swap_data(int*, int*);
int main( ){
int a=10, b=15;
cout << "交換前:" << a <<" "<< b << endl;
swap_data(a, b);
cout << "交換後:" << a <<" "<< b << endl;
return 0; }
void swap_data( int &a, int &b ){
int t;
t = a;
a = b;
b = t;
}
44
8.7.3 指標傳給指標
#include <iostream>
//cout
using namespace std;
void add_one(int*);
int main( ){
int a=10, *b;
b = &a;
add_one(b);
cout << "*b=" << *b << endl;
cout << " a=" << a << endl;
return 0;
}
void add_one(int *a){
(*a)++;
}
45
8.7.4 函數名稱以指標傳回
#include <iostream>
//cout
using namespace std;
int *get_sum(int,int);
int main( ){
int a=10, b=20, *p;
p = get_sum(a,b);
cout << "sum=" << *p;
delete p;
return 0;
}
int *get_sum(int a, int b) {
int *p=new int (a+b);
return p;
}
46
8.7.5 傳回一個以上資料
#include <iostream>
//cout
using namespace std;
int *get_sum(int*,int);
int main( ){
int *a,*b,*p;
a = new int(10);
b = new int(20);
p = get_sum(a,b);
cout <<"*a="<<*a<<endl
<<"*b="<<*b<<endl
<<"*p="<<*p<<endl;
delete p; delete b; delete a;
return 0;
}
int *get_sum(int *a,int *b){
int *p=new int(*a+*b);
(*a)++;
--(*b);
return p; }
47
8.9 指標中之指標(雙指標)
• 設定一指標指向另一指標, 第二指標指向內容值之用法, 此
種情形稱為多重間接指向(multiple indirection)或指
標中之指標(pointers to pointers)。在此種情況下,
• 第一指標儲存第二指標之位址, 而第二指標系指向儲存數
值之位址。
• 雙重指標可用來設定二維陣列, 先要求列(Row)之指標, 再
以列之指標來設定行(Column)之指標, 使列為第一指標,
行為第二指標, 形成陣列中陣列, 其存取方式可用一般之陣
列存取方法。
48
單一指標
雙指標
指標
變數
位址
內容
指標1
指標2
變數
位址
位址
內容
49
例題: 指標中之指標。
#include <iostream>
//cout
using namespace std;
int *get_sum(int,int);
int main( ){
int x, *p, **m;
x = 100;
p = &x; //p指向x
m = &p; //m指向p
cout << "**m="<<**m<<endl;
cout << "*p=" << *p << endl;
cout << "x=" << x << endl;
return 0;
}
50
8.10 指標指向函數
• 指標也可指向已經存在之函數, 雖然函數不是變數但它仍
然在記憶體內有一實體位址, 有實體位址就可將指標指向
該位址, 就稱為函數指標(function pointer), 函數之位址是
函數被呼叫時之進入點, 一旦函數被指標指到相當於可以
呼叫該函數; 函數指標也可當引數(Argument)或參數
(Parameter)傳給其他函數。
51
例題: 指標指向pow函數 。
#include <iostream>
//cout
#include <math.h>
//pow(x,y)
using namespace std;
int main( ){
double x, y;
double (*p)(double, double);
p = pow;
//求x之y次方
cout << "x, y=";
cin >> x >> y;
cout << x <<"之" << y <<"次方="<< (*p)(x,y);
return 0;
}
52
8.11 指標陣列
• 許多相同資料型態之指標變數集合成一陣列, 以索引或註
標來存取各指標, 此種方式稱為指標陣列(Array of
pointer)。
• int *p[5]; //類似int *p0, *p1, *p2, *p3, *p4;
– 要將int x=10; 設定給第三個元素為:
– p[2]=&x;
– 取得其內容為:
– *p[2];
53
例題: 以指標指向5個變數之位址後將資料輸出。
#include <iostream>
//cout
#include <math.h>
//pow(x,y)
using namespace std;
int main( ){
int *p[5], a=15, b=10, c=20, d=30, e=40;
p[0] = &a;
p[1] = &b;
p[2] = &c;
p[3] = &d;
p[4] = &e;
for (int i=0; i<5; i++)
cout << *p[ i ]<<" "<<p[ i ]<<endl;
return 0;
}
54
例題: 以指標指向5個變數之位址後將資料輸出。
位址
0x0064fe00
0x0064fdfc
0x0064fdf8
0x0064fdf4
0x0064fdf0
內容
15
10
20
30
40
變數
a
b
c
d
e
0x0064fddc
0x0064fde0
0x0064fde4
0x0064fde8
0x0064fdec
:
0x0064fe00
0x0064fdfc
0x0064fdf8
0x0064fdf4
0x0064fdf0
p[0]
p[1]
p[2]
p[3]
p[4]
55
8.12 指標常犯之錯誤
• 寫程式最容易犯的錯誤莫過於指標, 它給予你很大的空間
去使用, 也給你造成很大的困擾, 尤其當指標指定到一錯誤
之位址, 往往很不容易偵錯(debug) 。
• 一個錯誤之指標很不容易去發現, 主要原因並不是指標本
身之問題, 而是每次程式在運作時使用錯誤的指標, 讀取或
寫進入未明確設定之記憶體, 在讀取時最糟糕之狀況是讀
取出一堆沒用或不對的資料, 此種情形只有程式真正執行
時才會發生, 會誤導偵錯之方向, 那是程式師之夢饜。
56
第八章指標 習題
1. 設計一程式,使用指標做兩個整數相加與相減。
2. 使用指標完成兩個3×4矩陣A、B相加後放入C。
3. 撰寫一個程式,利用傳址方式,求兩整數的最大公因數(gcd)及最小公
倍數(lcm)。
4. 撰寫一個程式,定義調查字串長度的函式 int length(char *str)。然後
輸入一字串,查出字串的長度。
5. 撰寫一個程式,可以接受帶有5個元素的陣列,定義傳回最大值的函式
int max(int x[ ])。利用max( ),從鍵盤輸入學生的數目與成績,然後
輸出最高分。
6. 設計一程式,使用指標做一維陣列的排序。例如給一數列{92,81,70,
69,19,55,44,32,58,25} 使用指標將此數列
(a)由大排到小,(b)由小排到大。
57
指 標-指標與二維陣列
•
•
指標與二維陣列
二維陣列在人們眼中通常用於處理二維的行列表格,但電腦內部的記憶體,是
以一維陣列的方式排列,所以雖然是二維的陣列,但在電腦的記憶體中已轉為
一維的方式儲存。
• 以指標來處理二維陣列,第一步驟設定第一元素的位址給指標變數:
int *ptr;
*(*(a+m)+n); /* 取出陣列a裡,第m+1列,第n+1
int a[4][3];
行的值
ptr=&a[0][0];
第二步驟以ptr來表示二維陣列的值,必須先將二維排列轉為一為陣列的直線排列:
58
矩陣產生的問題
Memory leak問題:在使用指標建立一維動態陣列時有提到,如果配置了記憶體空間沒有釋
放完全,將造成程式所吃的記憶體會愈來愈多,最後將因資源潰乏導致程式無法順利結束,
這種問題便稱為 memory leak。一般而言,memory leak是因為記憶體空間沒有進行釋放或釋
放不完全引起的,而且除錯階段也不好抓,更是一般設計者的困擾。
記憶體碎片化(memory fragment)問題:上例的方式,當我們的 ROW 及 COL 過大時,將會
使得記憶體位址不連續,這個問題會使得到時存取陣列時,速度將變慢。
59
#include <iostream>
using namespace std;
int ROW = 2;
int COL = 4;
int main(int argc, char**argv)
{
int i=0, j=0;
int **ptr2 = NULL;
// 生成一維指標陣列
ptr2 = new int*[ROW];
// 每個指標陣列再生成整數陣列
for(i=0; i<ROW; i++) ptr2[i] = new int[COL];
// write
int cnt = 0;
for(i=0; i<ROW; i++) {
for(j=0; j<COL; j++) ptr2[i][j] = cnt++;
}
// read value and address
for(i=0; i<ROW; i++) {
for(j=0; j<COL; j++) {
cout << "ptr2[" << i << "][" << j<< "]=" << ptr2[i][j];
cout << "(" << hex << &ptr2[i][j] << ")" << endl;
}
}
// 釋放指標陣列
for(i=0; i<ROW; i++) delete ptr2[i];
// 釋放指標
system(“pause”);
return 0;
60
}
執行結果
ptr2[0][0]=0(384430)
ptr2[0][1]=1(384434)
ptr2[0][2]=2(384438)
ptr2[0][3]=3(38443C)
ptr2[1][0]=4(384470)
ptr2[1][1]=5(384474)
ptr2[1][2]=6(384478)
ptr2[1][3]=7(38447C)
標準樣板函式庫(STL)
STL(Standard Template Library)
提供多種類別樣板以供使用
由以下三大部分所組成
Iterator(抽象指標)
Container(容器):
vector, list, stack, queue, map,…
Algorithm(演算法):
find, sort, count,…
61
• 抽象指標(Iterator)
•
Iterator是一種可以讓使用者逐一存取container中元
素的工具
•
所有STL的container C都有(甚至自己寫的class)
•
使用者均可利用C::iteratorit;的方式來宣告iterator,
以取得容器內元素的位址
•
容器均有提供begin( ),end( )成員函數,讓使用者取
得容器的第一個元素的位址與最後一個元素後一個的
位址
62
抽象指標(Iterator)
範例:
#include<iostream>
#include<vector>
using namespace std;
Int main() {
Int a[7] = { 8, 1, 3, 2, 5, 1, 4};
Vector <int> v(a,a+7);
vector<int>::iterator it ;
for ( it = v.begin( ); it != v.end( ) ; it++)
cout<< *it <<" ";
cout<< endl;
return 0;}
63
容器(Containers)
Vector
動態陣列,此陣列可任意增長其大小,並提供隨機存取
List
雙向linked list,支援循序雙向存取(無法隨機存取)
Map
支援查表功能之資料結構,資料均以key/value方式存入
其他… (stack, queue, set…)
64
向量vector
vector其特性如同陣列一樣,
但又比內建的陣列多了許多好
用的功能
vector可以動態成長,可以將
一個vector指定給另一個
vector
vector知道自己內含元素的個
數
常用函式
[n]
取得第索引值n之資料
size
計算長度
front
取得開頭元素
back 取得結尾元素
pop_back
移除結尾資料
push_back
新增資料於結尾
begin 取得開頭元素之抽象指標
end
取得結尾元素之抽象指標
insert
erase
插入資料
刪除資料
65
vector::vector
範例:
#include <iostream>
#include <vector>
using namespace std;
int main () {
int i;
int myints[] = {16,2,77,29};
vector<int> v1 (myints, myints+ 4);
vector<int> v2;
v2 = v1;
for (i=0; i< v2.size(); i++)
cout<< " " << v2[i];
cout<< endl;
system(“pause”);
return 0;
}
66
vector::pop_back, push_back
範例:
#include <iostream>
#include <vector>
using namespace std;
int main ( )
{
vector<int> v;
int sum=0;
v.push_back(100);
v.push_back(200);
v.push_back(300);
while (! v.empty())
{
sum+=v.back();
v.pop_back();
}
cout<< "sum = " << sum << endl;
system("PAUSE");
return EXIT_SUCCESS;
67
}
vector::begin, end
#include <iostream>
#include <vector>
using namespace std;
int main ( )
{
vector<int> v;
vector<int>::iterator it;
for (int i=1; i<=5; i++)
v.push_back(i);
for ( it=v.begin() ; it < v.end(); it++ )
cout<< " " << *it;cout<< endl;
system(“pause”);
return 0;
}
68
vector::insert
// inserting into a vector
#include <iostream>
#include <vector>
using namespace std;
int main ( )
{
int i;vector<int> v;
int a[ ] = { 10,20,30 };
v.insert(v.begin(), a, a+3); //插入一段資料於開頭
v.insert(v.begin()+1,15); //插入一筆資料於第二個位子
v.insert(v.end(),100);//插入一筆資料於最後
for (i=0; i<v.size(); i++)
cout<< " " << v[i];
cout<< endl;
System(“pause”);
return 0;}
69
vector::erase
#include <iostream>
#include <vector>using namespace std;
int main ( )
{
int i;
vector<int> v;
for (i=1; i<=10; i++)
v.push_back(i);
v.erase(v.begin()+5); //指定一筆資料刪除
v.erase(v.begin(),v.begin()+3); //刪除一段資料
for (i=0; i<v.size(); i++)
cout<< " " << v[i];
cout<< endl;
system(“pause”);
return 0;
}
70
vector是否能達成動態建立二維陣列的需求
#include <cstdlib>
#include <iostream>
#include <vector>
using namespace std;
void printTwoDimDynamicArray(vector<vector <int> > ivec)
{
for(int y = 0;y!= ivec.size(); ++y)
{for(int x=0;x!= ivec[0].size();++x) {
cout<<ivec[y][x]<<" ";}
cout<<endl;}
}
int main(int argc, char *argv[])
{
const int sizex=3;
const int sizey=2;
vector<vector<int> > ivec(sizey,vector<int>(sizex));
for(int y=0; y!=sizey; ++y)
{
for(int x=0;x!=sizex;++x)
{ivec[y][x]=y+x;}
}
}
printTwoDimDynamicArray(ivec);
system("PAUSE");
return EXIT_SUCCESS;
71
#include <cstdlib>
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char *argv[])
{
//2維的array[n][n]
int n;
int **array_2D = new int *[n];
for(int i=0;i<n;i++){
array_2D[i]=new int[n];
}
vector<int> row;
row.assign(n,0);//配置一個row的大小
vector< vector<int> > vect_2D;
vect_2D.assign(n,row);//配置2維
system("PAUSE");
return EXIT_SUCCESS;
}
72