Bài giảng Ngôn ngữ lập trình C và C++ - Bài 5: Bảng và Con trỏ - Đỗ Đăng Khoa

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ộ

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

pdf 63 trang yennguyen 6440
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng 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 hãy click vào nút Download ở trên

Tóm tắt nội dung tài liệu: Bài giảng Ngôn ngữ lập trình C và C++ - Bài 5: Bảng và Con trỏ - Đỗ Đăng Khoa

Bài giảng 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
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 hoàn toà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 toà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 toà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 toá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 toà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 toá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 toá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 toá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 toá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 toán con trỏ
Phép toá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 toá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 toá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 toàn bộ mảng a
5/3/2015
39
Phép toán con trỏ
Phép toá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 toán con trỏ
Hiệu của 2 con trỏ
Phép toá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 toá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 toá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 toà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 toá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 toà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 toá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 ngoà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 ngoài này sẽ bị thay đổi
ngoà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ừ khoá 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(); 
} 

File đính kèm:

  • pdfbai_giang_ngon_ngu_lap_trinh_c_va_c_bai_5_bang_va_con_tro_do.pdf