Tài liệu Ngôn ngữ lập trình C và C++ - Bài 5: Bảng và con trỏ - Đỗ Đăng Khoa: 5/3/2015
1
TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
Ngôn ngữ lập trình C và C++
Bài 5: Bảng và Con trỏ
TS. Đỗ Đăng Khoa
Bộ môn Cơ học Ứng dụng
Viện Cơ khí
5/3/2015
2
Khái niệm về Bảng (Mảng)
Khi cần lưu trữ một dãy n phần tử dữ liệu chúng ta cần khai
báo n biến tương ứng với n tên gọi khác nhau -> khó khăn để có
thể nhớ và quản lý hết được tất cả các biến
Một bảng trong C/C++ là một tập hợp các phần tử dữ liệu liên
quan có cùng kiểu và được truy cập bởi một tên chung
Tất cả các phần tử của bảng chiếm một tập hợp các vị trí bộ
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
nhớ liền kề nhau, và sử dụng chỉ số để xác định từng phần tử
Chỉ số bảng bắt đầu từ 0
Mảng có thể có nhiều chiều
Ví dụ: danh sách sinh viên, danh sách điểm số của sinh viên,etc
2
5/3/2015
3
Mảng một chiều
// mảng số nguyên một chiều có 10 phần tử
int A[10];
A[3]=1;
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
-- -- 1--A -- -- ---- -- --
4 5 630 2 8 971
A[4] A[5] A[6]A[3]A[0] A[2] A[8] A[9]A[7]A[1]
5/3/2015
4...
63 trang |
Chia sẻ: putihuynh11 | Lượt xem: 534 | Lượt tải: 0
Bạn đang xem trước 20 trang mẫu tài liệu Ngôn ngữ lập trình C và C++ - Bài 5: Bảng và con trỏ - Đỗ Đăng Khoa, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
5/3/2015
1
TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
Ngơn ngữ lập trình C và C++
Bài 5: Bảng và Con trỏ
TS. Đỗ Đăng Khoa
Bộ mơn Cơ học Ứng dụng
Viện Cơ khí
5/3/2015
2
Khái niệm về Bảng (Mảng)
Khi cần lưu trữ một dãy n phần tử dữ liệu chúng ta cần khai
báo n biến tương ứng với n tên gọi khác nhau -> khĩ khăn để cĩ
thể nhớ và quản lý hết được tất cả các biến
Một bảng trong C/C++ là một tập hợp các phần tử dữ liệu liên
quan cĩ cùng kiểu và được truy cập bởi một tên chung
Tất cả các phần tử của bảng chiếm một tập hợp các vị trí bộ
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
nhớ liền kề nhau, và sử dụng chỉ số để xác định từng phần tử
Chỉ số bảng bắt đầu từ 0
Mảng cĩ thể cĩ nhiều chiều
Ví dụ: danh sách sinh viên, danh sách điểm số của sinh viên,etc
2
5/3/2015
3
Mảng một chiều
// mảng số nguyên một chiều cĩ 10 phần tử
int A[10];
A[3]=1;
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
-- -- 1--A -- -- ---- -- --
4 5 630 2 8 971
A[4] A[5] A[6]A[3]A[0] A[2] A[8] A[9]A[7]A[1]
5/3/2015
4
Khai báo bảng (mảng) một chiều
Các đặc tính riêng của bảng (mảng) cần được định nghĩa.
Kiểu dữ liệu của các phần tử
Tên mảng: đại diện cho vị trí phần tử đầu tiên
Kích thước mảng : một hằng số
Khai báo mảng giống như cách khai báo biến. Chỉ khác là
tên mảng được theo sau bởi một hoặc nhiều biểu thức đặt
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
trong cặp dấu ngoặc vuơng [], để xác định kích thước của
mảng.
int x[20]; // x cĩ thể chứa 20 số nguyên
float price[10]; // price cĩ thể chứa 10 số thực
char letter[70]; // letter cĩ thể chứa tối đa 69 kí tự
do cần cần cĩ kí tự kết thúc khơng (\0) cuối xâu
5/3/2015
5
Các qui tắc về bảng (mảng)
Các phần tử của mảng cĩ cùng kiểu dữ liệu
Mỗi phần tử của mảng cĩ thể được sử dụng như một biến
riêng lẻ
Kiểu dữ liệu của mảng cĩ thể là int, char, float hoặc double
Mảng được “đối xử” khơng giống hồn tồn với biến
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
Hai mảng cĩ cùng kiểu và cùng kích thước cũng khơng
được xem là tương đương nhau
Khơng thể gán trực tiếp một mảng cho một mảng khác.
Khơng thể gán trị cho tồn bộ mảng, mà phải gán trị cho
từng phần tử của mảng
5/3/2015
6
Khởi tạo bảng (mảng) một chiều
Mỗi phần tử của một mảng cần được khởi tạo riêng rẽ.
Kiểu_dữ_liệu tên_bảng[kích_thước_mảng] = {ds_các_phần_tử_bảng};
Kiểu_dữ_liệu tên_bảng[] = {ds_các_phần_tử_bảng};
Ví dụ:
int id[7] = {1, 2, 3, 4, 5, 6, 7};
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
float x[] = {5.6, 5.7, 5.8, 5.9, 6.1};
char vowel[6] = {'a', 'e', 'i', 'o', 'u', '\0'};
char vowel[6] = "aeiou"; // Kí tự NULL được trình biên dịch
tự động thêm vào, do đĩ vẫn phải dự trữ thêm một chỗ cho
NULL.
Các phần tử của mảng cĩ thể được gán giá trị bằng cách sử
dụng vịng lặp for
5/3/2015
7
Sử dụng bảng (mảng) một chiều
Để chỉ thành phần thứ i (hay chỉ số i) của một mảng ta viết
tên mảng kèm theo chỉ số trong cặp ngoặc vuơng [].
int id[7] = {1, 2, 3, 4, 5, 6, 7};
cout<<id[0];// id[0]=1
Tuy mỗi mảng biểu diễn một đối tượng nhưng khơng thể áp
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
dụng các thao tác lên tồn bộ mảng mà phải thực hiện thao tác
thơng qua từng thành phần của mảng
Ví dụ: chúng ta khơng thể nhập dữ liệu cho mảng a[10] bằng câu
lệnh:
cin >> a ; // sai
mà phải nhập cho từng phần tử từ a[0] đến a[9] của a
5/3/2015
8
Ví dụ về mảng một chiều
Tìm số bé nhất của một dãy số. In ra số này và vị trí của nĩ
trong dãy
void main(){
float a[100], min;// a chứa tối đa 100 số
int i,n,k;
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
cout > n;
for (i=0; i>
a[i]; }
min = a[0]; k = 0;
for (i=1; i<n; i++) if (a[i] < min ) { min = a[i]; k = i; }
cout << “So be nhat la" << min << “tai vi tri" << k;
}
5/3/2015
9
Xâu/mảng ký tự
Một xâu kí tự là một dãy bất kỳ các kí tự (kể cả dấu cách) do
vậy nĩ cĩ thể được lưu bằng mảng kí tự
Cần thiết phải cĩ kí tự kết thúc xâu ‘\0’
char [độ dài] ; // khơng khởi tạo
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
char [độ dài] = xâu kí tự ; // cĩ khởi tạo
char [] = xâu kí tự ; // cĩ khởi tạo
Ví dụ:
char hoten[26] ; // xâu họ tên chứa tối đa 25 kí tự
char monhoc[31] = "NNLT C++" ;
char thang[] = "Muoi hai" ; // độ dài mảng = 9
5/3/2015
10
Sử dụng Xâu/mảng ký tự
Xâu kí tự cĩ những đặc trưng như mảng, tuy nhiên chúng
cũng cĩ những điểm khác biệt
Truy cập một kí tự trong xâu: cú pháp giống như mảng. Ví dụ:
char s[50] = "I\'m a student" ;/* chú ý kí tự '
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
phải được viết là \‘ */
cout << s[0] ;// in kí tự đầu tiên, tức kí tự
'I'
s[1] = 'a' ; // đặt lại kí tự thứ 2 là 'a'
5/3/2015
11
Sử dụng Xâu/mảng ký tự
Khơng được thực hiện các phép tốn trực tiếp trên xâu như:
char s[20] = "Hello", t[20] ; /* khai báo hai
xâu s và t */
t = "Hello" ;// sai, chỉ gán được khi khai báo
t = s ; // sai, khơng gán được tồn bộ mảng
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
if (s < t) // sai, khơng so sánh được hai mảng
Các hàm thư viện xử lý xâu ký tự được khai báo trong file
nguyên mẫu
5/3/2015
12
Các hàm xử lý xâu ký tự (chuỗi)
Các hàm xử lý chuỗi được tìm thấy trong thư viện chuẩn
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
5/3/2015
13
Ví dụ về các hàm xử lý xâu ký tự (chuỗi)
Hàm strcpy(s1,s2): Hàm sao chép xâu s2 vào s1
char s[10], t[10] ;
t = "Face" ; // khơng được dùng
s = t ; // khơng được dùng
strcpy(t, "Face") ; // được, gán "Face" cho t
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
strcpy(s, t) ; // được, sao chép t sang s
cout << s << " to " << t ;//in ra: Face to Face
5/3/2015
14
Ví dụ về các hàm xử lý xâu ký tự (chuỗi)
Hàm strncpy (s1,s2,n): Hàm sao chép n ký tự xâu s2 vào s1
char s[10], t[10] = "Steven";
strncpy(s, t, 5) ;// copy 5 kí tự "Steve" vào s
s[5] = '\0' ; // đặt dấu kết thúc xâu
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
// in câu: Steve is young brother of Steven
cout << s << " is young brother of " << t ;
strncpy(s1+5, s2+3, 2): Câu lệnh này cĩ nghĩa: lấy 2 kí tự thứ
3 và thứ 4 của xâu s2 đặt vào 2 ơ thứ 5 và thứ 6 của xâu s1 (chỉ
số bắt đầu từ 0)
5/3/2015
15
Ví dụ về các hàm xử lý xâu ký tự (chuỗi)
Hàm strcat (s1,s2): Hàm nối xâu s2 vào s1
char a[100] = “Ban", b[4] = “toi";
strcat(a, “ va ”);
strcat(a, b);
cout << a // Ban và toi
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
char s[100] , t[100] = "Steve" ;
strncpy(s, t, 3); s[3] = '\0'; // s =
"Ste"
strcat(s, "p"); // s = "Step"
cout << t << " goes "<< s << " by " <<s /* Steve
goes Step by Step*/
5/3/2015
16
Ví dụ về các hàm xử lý xâu ký tự (chuỗi)
Hàm strncat (s1,s2,n): Hàm nối n ký tự xâu s2 vào s1
char s[20] = “Nha " ;
char t[] = “anh chi"
strncat(s, t, 3) ; // s = “Nha anh"
hoặc:
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
strncat(s, t+4, 3) ; // s = "Nha chi"
5/3/2015
17
Ví dụ về các hàm xử lý xâu ký tự (chuỗi)
Hàm strcmp(s1,s2): Hàm so sánh 2 xâu s1 và s2
if (strcmp(s1,s2))
cout << "s1 khác s2";
else cout << "s1 bằng s2" ;
Hàm strncmp(s1,s2,n) : giống hàm strcmp(s1, s2) nhưng chỉ
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
so sánh tối đa n kí tự đầu tiên của hai xâu
char s[] = “Ha Noi" , t[] = “Ha noi" ;
cout << strcmp(s,t) ; /* -32 (vì 'N' = 78, 'n' =
110) */
cout << strncmp(s, t, 3) ; /* 0 (vì 3 kí tự đầu của
s và t là như nhau) */
5/3/2015
18
Ví dụ về các hàm xử lý xâu ký tự (chuỗi)
Hàm strcmpi(s1, s2): Như strcmp(s1, s2) nhưng khơng phân
biệt chữ hoa, thường
char s[] = “Ha Noi" , t[] = “ha noi" ;
cout << strcmpi(s, t) ; // 0 (vì s = t)
Hàm strupr(s): Hàm đổi xâu s thành in hoa, và cũng trả lại xâu
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
in hoa đĩ
char s[10] = "Ha noi" ;
cout << strupr(s) ; // HA NOI
cout << s ; // HA NOI (s cũng thành in hoa)
Hàm strlwr(s): Hàm đổi xâu s thành in thuờng, kết quả trả lại
là xâu s
5/3/2015
19
Truyền mảng một chiều cho hàm
Một hàm cĩ thể nhận địa chỉ của một bảng một chiều theo ba
cách
Một con trỏ
int myfunction(float *x)
Một bảng cĩ kích thước
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
char yourfunction(float x[5])
Một bảng khơng cĩ kích thước
void yourfunction(float x[ ])
Hai cách sau thường ít được sử dụng
5/3/2015
20
Ví dụ: lấy giá trị trung bình của mảng
#include
using namespace std;
double getAverage(int arr[], int size);
int main ()
{
int balance[5] = {1000, 2, 3, 17, 50};
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
double avg;
avg = getAverage( balance, 5 ) ;
cout << “Gia tri trung binh: " << avg << endl;
return 0;
}
5/3/2015
21
Ví dụ: lấy giá trị trung bình của mảng
double getAverage(int arr[], int size)
{
int i, sum = 0;
double avg;
for (i = 0; i < size; ++i)
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
{
sum += arr[i];
}
avg = double(sum) / size;
return avg;
}
5/3/2015
22
Mảng hai chiều
// Mảng số nguyên hai chiều – 3 hàng và 10 cột
int A[3][10];
A[1][2] = 1;
4 5 630 2 8 971
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
-- -- ----
A
-- -- ---- -- --
-- 1 ---- -- -- ---- -- --
-- -- ---- -- -- ---- -- --0
2
1
5/3/2015
23
Mảng hai chiều
Mảng hai chiều cĩ thể xem như là một mảng (m phần
tử) với mỗi phần tử là mảng một chiều (n phần tử)
Một mảng hai chiều trơng giống như một ma trận gồm
các hàng và các cột
Trong bộ nhớ tất cả các phần tử của mảng được sắp
liên tiếp theo từng hàng của mảng
Khai báo mảng hai chiều:
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
[m][n] ;
5/3/2015
24
Khởi tạo bảng (mảng) hai chiều
Mỗi phần tử của một mảng cần được khởi tạo riêng rẽ.
Kiểu_dữ_liệu tên_bảng[hàng][cột] = {ds_các_phần_tử_bảng};
Kiểu_dữ_liệu tên_bảng[][cột] = {ds_các_phần_tử_bảng};
Ví dụ:
int x[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
int x[][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
Kết quả của phép gán trong cả hai trường hợp:
x[0][0]=1 x[0][1]=2 x[0][2]=3 x[0][3]=4
x[1][0]=5 x[1][1]=6 x[1][2]=7 x[1][3]=8
x[2][0]=9 x[2][1]=10 x[2][2]=11 x[2][3]=12
5/3/2015
25
Khởi tạo bảng (mảng) hai chiều
Khởi tạo bảng xâu ký tự
char name[4][10] = {"Sally", "Joyce", "Lisa",
"Alice"};
Kết quả:
name[0] = "Sally" name[1] = "Joyce"
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
name[2] = "Lisa" name[3] = "Alice"
5/3/2015
26
Sử dụng bảng (mảng) hai chiều
Để chỉ thành phần hàng i (hay chỉ số i) và cột j của một mảng
ta viết tên mảng kèm theo các chỉ số trong 2 cặp ngoặc vuơng
[].
int id[2][2] = {1, 2, 3, 4};
cout<<id[0][0];// id[0][0]=1
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
id[1][1]=5;
cout<<id[1][1];// id[1][1]=5;
5/3/2015
27
Truyền mảng hai chiều cho hàm
Một hàm cĩ thể nhận địa chỉ của một bảng 2 chiều theo ba
cách
Một bảng con trỏ
int myfunction(float **x)
Một bảng cĩ kích thước
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
char yourfunction(float x[4][5])
Một bảng khơng cĩ kích thước
void yourfunction(float x[ ][5])
5/3/2015
28
Ví dụ: truyền mảng 2 chiều cho hàm
Truyền mảng kích thước cố định
int array[10][10];
//ðịnh nghĩa hàm nhận mảng 2 chiều cố định
void passFunc(int a[10][10])
{ // thao tác với a[i][j] }
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
passFunc(array);
Hoặc
void passFunc(int a[][10])
{//thao tác với a[i][j] }
passFunc(array);
5/3/2015
29
Ví dụ: truyền mảng 2 chiều cho hàm
Hoặc
void passFunc(int (&a)[10][10])
{ // thao tác với a[i][j] }
passFunc(array);
Hoặc
void passFunc(int (*a)[10][10])
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
{ // thao tác với (*a)[i][j] }
passFunc(&array);
Hoặc
void passFunc(int (*a)[10]) // con trỏ tới mảng
{ // thao tác với a[i][j] }
passFunc(array);
5/3/2015
30
Ví dụ: truyền mảng 2 chiều cho hàm
Sai nếu định nghĩa
void passFunc(int **a) //con trỏ tới con trỏ
{ // }
Truyền mảng kích thước thay đổi
int **array;
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
array = new int *[10];
for(int i = 0; i <10; i++)
array[i] = new int[10];
void passFunc(int **a)
{ // ... }
passFunc(array);
5/3/2015
31
Khái niệm về Con trỏ
Con trỏ là một biến, nĩ chứa địa chỉ ơ nhớ của một biến khác
Nếu p là con trỏ chứa địa chỉ của biến c ta gọi p trỏ tới c và c
được trỏ bởi p
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
Con trỏ cung cấp phương thức truy xuất gián tiếp đến giá trị
của một phần tử dữ liệu
Các con trỏ cĩ thể trỏ đến các biến cĩ kiểu dữ liệu cơ bản như
int, char, double, hay dữ liệu tập hợp như mảng hoặc cấu trúc.
Phép tốn lấy địa chỉ của đối tượng: &.
31
5/3/2015
32
Ứng dụng của con trỏ
Để trả về nhiều hơn một giá trị từ một hàm
Để truyền mảng và chuỗi từ một hàm đến
một hàm khác thuận tiện hơn
Để làm việc với các phần tử của mảng thay
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
vì truy xuất trực tiếp vào các phần tử này
Để cấp phát bộ nhớ và truy xuất bộ nhớ (Cấp
phát bộ nhớ trực tiếp)
5/3/2015
33
Khai báo con trỏ
Khai báo con trỏ: chỉ ra một kiểu cơ sở và
một tên biến được đặt trước bởi dấu *
Cú pháp khai báo tổng quát:
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
Ví dụ:
;
int *p;
5/3/2015
34
Các tốn tử con trỏ * và &
Để con trỏ p trỏ đến biến x ta phải dùng phép gán p = địa chỉ
của x.
Nếu x khơng phải là mảng ta viết: p = &x.
Nếu x là mảng ta viết: p = x hoặc p = &x[0].
Khơng gán p cho một hằng địa chỉ cụ thể. Ví dụ viết p = 200
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
là sai.
Phép tốn * cho phép lấy nội dung nơi p trỏ đến, ví dụ để
gán nội dung nơi p trỏ đến cho biến f ta viết f = *p.
& và * là 2 phép tốn ngược nhau. Cụ thể nếu p = &x thì x =
*p. Từ đĩ nếu p trỏ đến x thì bất kỳ nơi nào xuất hiện x đều cĩ
thể thay được bởi *p và ngược lại.
5/3/2015
35
Ví dụ về con trỏ
int i, j ; // khai báo 2 biến nguyên i, j
int *p, *q ; // khai báo 2 con trỏ nguyên p, q
p = &i; // cho p trỏ tới i
q = &j; // cho q trỏ tới j
cout << &i ; // hỏi địa chỉ biến i
cout << q ; // hỏi địa chỉ biến j (thơng qua q)
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
i = 2; // gán i bằng 2
*q = 5; // gán j bằng 5 (thơng qua q)
i++ ; cout << i ;// tăng i và hỏi i, i = 3
5/3/2015
36
Ví dụ về con trỏ
// tăng j (thơng qua q) và hỏi j, j = 6
(*q)++ ;
cout << j;
(*p) = (*q) * 2 + 1;// gán lại i (thơng qua p)
cout << i ; // 13
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
5/3/2015
37
Phép tốn con trỏ
Phép tốn gán
Gán con trỏ với địa chỉ một biến: p = &i ;
Gán con trỏ với con trỏ khác: p = q ;
Phép tốn tăng giảm địa chỉ
p ± n: con trỏ trỏ đến thành phần thứ n sau (trước) p
Một đơn vị tăng giảm của con trỏ bằng kích thước
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
của biến được trỏ
Ví dụ giả sử p là con trỏ nguyên (2 byte) đang trỏ đến
địa chỉ 200 thì p+1 là con trỏ trỏ đến địa chỉ 202, p - 3
chứa địa chỉ 194
5/3/2015
38
Ví dụ về phép tốn +/- con trỏ
int a[100] = { 1, 2, 3, 4, 5, 6, 7 };
int *p, *q;
// cho p trỏ đến mảng a, *p = a[0] = 1
p = a; cout << *p ;
// *p = a[5] = 6
p += 5; cout << *p
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
// q = a[1] = 2 ;
q = p - 4 ; cout << *q
for (int i=0; i<100; i++)
cout << *(p+i) ;// in tồn bộ mảng a
5/3/2015
39
Phép tốn con trỏ
Phép tốn tự tăng giảm
p++, p--, ++p, --p: tương tự p+1 và p-1, chú ý đến tăng
(giảm) trước, sau
int a[2] = {3, 7}, *p = a;
// tăng (sau) giá trị nơi p trỏ ≡ tăng a[0] thành 4
(*p)++ ;
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
// tăng (trước) giá trị nơi p trỏ ≡ tăng a[0] thành 4
++(*p) ;
// lấy giá trị nơi p trỏ (3) và tăng trỏ p (tăng sau),
// p -> a[1]
*(p++) ;
// tăng trỏ p (tăng trước), p -> a[1] và lấy giá trị
//nơi p trỏ (7)
*(++p) ;
5/3/2015
40
Phép tốn con trỏ
Hiệu của 2 con trỏ
Phép tốn này chỉ thực hiện được khi p và q là 2 con
trỏ cùng trỏ đến các phần tử của một dãy dữ liệu nào
đĩ trong bộ nhớ
Hiệu p - q là số thành phần giữa p và q
Giả sử p và q là 2 con trỏ nguyên, p cĩ địa chỉ 200 và
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
q cĩ địa chỉ 208. Khi đĩ p - q = -4
Phép tốn so sánh
So sánh giữa địa chỉ của hai nơi được trỏ bởi các con
trỏ này
Chỉ áp dụng cho hai con trỏ trỏ đến phần tử của cùng
một mảng dữ liệu nào đĩ
5/3/2015
41
Ví dụ về phép tốn so sánh con trỏ
float a[100], *p, *q ;
// p trỏ đến mảng (tức p trỏ đến a[0])
p = a ;
// q trỏ đến phần tử thứ 3 (a[3]) của mảng
q = &a[3] ;
cout << (p < q) ; // 1
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
cout << (p + 3 == q) ; // 1
cout q - 1) ; // 0
cout = q - 2) ; // 0
// in tồn bộ mảng a
for (p=a ; p < a+100; p++)
cout << *p ;
5/3/2015
42
Cấp phát động với con trỏ
Cấp phát tĩnh: cấp sẵn trước khi chạy chương trình và
khơng thể thay đổi tăng, giảm kích thước hoặc vị trí trong
suốt quá trình chạy chương trình
Cấp phát động: kích thước cụ thể (mảng) sẽ được cấp
phát trong quá trình chạy chương trình
Khi khơng dùng nữa ta cĩ thể thu hồi (cịn gọi là giải
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
phĩng) số ơ nhớ này để chương trình sử dụng vào việc
khác
Việc cấp phát và thu hồi này được thực hiện thơng qua
các tốn tử new, delete và con trỏ p
5/3/2015
43
Cấp phát động với con trỏ
Cú pháp của câu lệnh new.
p = new ; // cấp phát 1 phần tử
p = new [n] ; // cấp phát n phần tử
Nếu khơng cĩ vùng nhớ với số lượng như vậy thì việc cấp
phát là thất bại và p = NULL
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
Ví dụ:
int *p ;
// cấp phát vùng nhớ chứa được 1 số nguyên
p = new int ;
// cấp phát vùng nhớ chứa được 100 số nguyên
p = new int[100] ;
5/3/2015
44
Ví dụ cấp phát động với con trỏ
double *p ;
int n ;
cout << “Kich thuoc mang can cap phat: ";
cin >> n;
p = new double[n];
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
if (p == NULL)
{
cout << “Khong du bo nho”;
exit(0) ;
}
5/3/2015
45
Giải phĩng bộ nhớ động
Để giải phĩng bộ nhớ đã cấp phát cho một biến (khi khơng
cần sử dụng nữa) ta sử dụng câu lệnh delete.
delete p ; // p là con trỏ được sử dụng trong new
Giải phĩng tồn bộ mảng được cấp pháp thơng qua con trỏ
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
p ta dùng câu lệnh:
delete[] p ; // p là con trỏ trỏ đến mảng
5/3/2015
46
Con trỏ và mảng 1 chiều
Con trỏ trỏ đến mảng cũng tương tự trỏ đến các biến khác
Con trỏ p trỏ đến mảng a thì p+i là địa chỉ thành phần thứ i
của mảng a và do đĩ: *(p+i) = a[i] = *(a+i).
Khi viết *(p++) thì lại khác với *(a++), cụ thể viết p++ là hợp
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
lệ cịn a++ là khơng được phép (p thực sự là một biến, nĩ cĩ
thể thay đổi được giá trị cịn a là một hằng)
5/3/2015
47
Con trỏ và xâu ký tự
Con trỏ kí tự cĩ thể xem như một biến xâu kí tự
Khác với mảng kí tự, ta được phép sử dụng phép gán cho 2
xâu dưới dạng con trỏ
Ví dụ:
char *s, *t = "Tin hoc" ;
s = t; // thay cho hàm strcpy(s, t) ;
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
Thực chất phép gán trên chỉ là gán 2 con trỏ với nhau
5/3/2015
48
Ví dụ về con trỏ và xâu ký tự
char *s = new char[30], *t ;
strcpy(s, "Hello") ;
// trong trường hợp này khơng cần cấp phát bộ
//nhớ cho t vì t và s cùng sử dụng chung vùng nhớ
t = s ;
nhưng:
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
char *s = new char[30], *t ;
strcpy(s, "Hello") ;
// trong trường hợp này phải cấp bộ nhớ cho t vì
// cĩ chỗ để strcpy sao chép sang nội dung của s
t = new char[30];
strcpy(t, s) ;
5/3/2015
49
Con trỏ và mảng hai chiều
float a[2][3], *p;
a khơng được xem là mảng 1 chiều với 6 phần tử mà được
quan niệm như mảng một chiều gồm 2 phần tử, mỗi phần tử là 1
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
bộ 3 số thực
Địa chỉ của mảng a chính là địa chỉ của phần tử đầu tiên a[0][0],
và a+1 khơng phải là địa chỉ của phần tử tiếp theo a[0][1] mà là địa
chỉ của phần tử a[1][0]
Phép gán p = a là dễ gây nhầm lẫn vì p là con trỏ float cịn a là
địa chỉ mảng (1 chiều)
5/3/2015
50
Con trỏ và mảng hai chiều
Cách sai:
p = a ; // sai vì khác kiểu
Các cách đúng:
// ép kiểu của a về con trỏ float (cũng là kiểu
//của p)
p = (float*)a;
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
p = a[0]; // gán với địa chỉ của mảng a[0]
// gán với địa chỉ số thực đầu tiên trong a
p = &a[0][0];
5/3/2015
51
Con trỏ và mảng hai chiều
Sau khi gán a cho p (p là con trỏ thực), việc tăng giảm p chính là
dịch chuyển con trỏ trên từng phần tử (thực) của a.
p trỏ tới a[0][0]
p+1 trỏ tới a[0][1]
p+2 trỏ tới a[0][2]
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
p+3 trỏ tới a[1][0]
p+4 trỏ tới a[1][1]
p+5 trỏ tới a[1][2]
5/3/2015
52
Mảng con trỏ
Nhiều con trỏ cùng kiểu cũng được tổ chức thành mảng
Mỗi phần tử của mảng con trỏ là một con trỏ trỏ đến một mảng
nào đĩ
Cách khai báo:
*tên_mảng_con_trỏ[size];
Ví dụ:
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
int *a[10];
khai báo một mảng a chứa 10 con trỏ. Mỗi con trỏ a[i] chứa địa chỉ
của một mảng nguyên nào đĩ (kích thước các mảng này cĩ thể
khác nhau)
5/3/2015
53
Con trỏ và Hàm
Giá trị trả lại của hàm là một mảng:
Khơng cĩ cách nào để giá trị trả lại của một hàm là mảng
Hàm trả lại một con trỏ trỏ đến dãy dữ liệu kết quả là tương
đương với việc trả lại mảng
Mảng kết quả cĩ thể được trả lại vào trong tham đối của hàm
Ví dụ:
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
int* tragiatri1()
{
// tạo mảng kết quả với 3 giá trị 1, 2, 3
int kq[3] = { 1, 2, 3 };
return kq ;
}
5/3/2015
54
Con trỏ và Hàm
int* tragiatri2()
{
// cấp phát 3 ơ nhớ nguyên
int *kq = new int[3];
*kq = 1;
*(kq+1)=2;
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
*(kq+2)=3 ;
return kq ;
}
5/3/2015
55
Con trỏ và Hàm
main()
{
int *a, i;
a = tragiatri1();
for (i=0; i<3; i++)
cout *(a+i);// khơng phải là 1, 2, 3
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
a = tragiatri2();
for (i=0; i<3; i++)
cout *(a+i);// 1, 2, 3
If(a!=NULL)
delete []a;
}
5/3/2015
56
Con trỏ và Hàm
Việc sử dụng hàm trả lại con trỏ là phải hết sức cẩn thận. Muốn
trả lại con trỏ cho hàm thì con trỏ này phải trỏ đến dãy dữ liệu
nào sao cho nĩ khơng mất đi sau khi hàm kết thúc như hàm
tragiatri1()
Nếu muốn trả lại giá trị con trỏ thì vùng dữ liệu mà nĩ trỏ đến
phải được cấp phát một cách tường minh (bằng tốn tử new),
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
chứ khơng để chương trình tự động cấp phát và tự động thu hồi
Đối và giá trị trả lại là xâu kí tự
Đối của các hàm xâu kí tự cĩ thể khai báo dưới 2 dạng: mảng
kí tự hoặc con trỏ kí tự
Giá trị trả lại luơn luơn là con trỏ kí tự
5/3/2015
57
Con trỏ và Hàm
Đối là hằng con trỏ
Khi các biến ngồi khơng cĩ nhu cầu thay đổi nhưng đối tương
ứng với nĩ vẫn phải khai báo dưới dạng con trỏ
Cĩ khả năng do nhầm lẫn, các biến ngồi này sẽ bị thay đổi
ngồi ý muốn
Đối cần được khai báo như là một hằng con trỏ bằng cách
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
thêm trước khai báo kiểu của chúng từ khố const
5/3/2015
58
Con trỏ Hàm
Một hàm (tập hợp các lệnh) cũng cĩ tên gọi , cĩ địa chỉ lưu trong
bộ nhớ và cĩ thể truy nhập đến hàm thơng qua tên gọi hoặc địa
chỉ của nĩ
Để truy nhập (gọi hàm) thơng qua địa chỉ chúng ta phải khai báo
một con trỏ chứa địa chỉ này và sau đĩ gọi hàm bằng cách gọi tên
con trỏ
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
Khai báo
(*tên biến hàm)(d/s tham đối);
(*tên biến hàm)(d/s tham đối) = ;
Phân biệt giữa 2 khai báo: float (*f)(int) là khai báo con trỏ hàm
cĩ tên là f và float* f(int) là khai báo hàm f với giá trị trả lại là một
con trỏ float
5/3/2015
59
Con trỏ Hàm
/* khai báo con trỏ hàm cĩ tên là f trỏ đến hàm
cĩ một tham đối kiểu int và cho giá trị kiểu
float*/
float (*f)(int);
// con trỏ trỏ đến hàm với cặp đối (float, int).
void (*f)(float, int);
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
5/3/2015
60
Khởi tạo con trỏ Hàm
Cú pháp của khởi tạo cũng như phép gán là như sau:
biến con trỏ hàm = tên hàm;
Con trỏ hàm f và tên hàm được trỏ phải giống nhau về kiểu trả
lại và danh sách đối
Ví dụ:
// khai báo hàm luỹ thừa
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
float luythua(float, int);
// khai báo con trỏ f tương thích với hàm luythua
float (*f)(float, int);
f = luythua; // cho f trỏ đến hàm luỹ thừa
5/3/2015
61
Sử dụng con trỏ hàm
Để sử dụng con trỏ hàm ta phải gán nĩ với tên hàm cụ thể
Bất kỳ nơi nào được phép xuất hiện tên hàm thì ta đều cĩ thể
thay nĩ bằng tên con trỏ
float bphuong(float x) {
return x*x;
}
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
void main()
{
float (*f)(float);
f = bphuong;
cout << « Binh phuong cua 3.5 la" << f(3.5) ;
}
5/3/2015
62
Mảng con trỏ hàm
Các con trỏ hàm giống nhau cĩ thể được gộp lại vào trong một
mảng
Thêm [n] vào sau tên mảng với n là số lượng tối đa các con trỏ
Ví dụ:
void cong(int a, int b){
cout << a << " + " << b << " = " << a+b ; }
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
void tru(int a, int b) {
cout << a << " - " << b << " = " << a-b ; }
void nhan(int a, int b){
cout << a << " x " << b << " = " << a*b ; }
void chia(int a, int b){
cout << a << ": " << b << " = " << a/b ; }
5/3/2015
63
Mảng con trỏ hàm
main()
{
clrscr();
// khai báo, khởi tạo 4 con trỏ
void (*f[4])(int, int) = {cong, tru, nhan, chia};
int m, n;
TRƯỜNG ĐẠI HỌC
BÁCH KHOA HÀ NỘI
cout "Nhập m, n " ; cin >> m >> n ;
for (int i=0; i<4; i++)
f[i](m,n);
getch();
}
Các file đính kèm theo tài liệu này:
- tai_lieu_lap_trinh_c_2693_1988931.pdf