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

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...

pdf63 trang | Chia sẻ: putihuynh11 | Lượt xem: 511 | Lượt tải: 0download
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:

  • pdftai_lieu_lap_trinh_c_2693_1988931.pdf
Tài liệu liên quan