Bài tập trong các bài học Ví dụ char *pchar; short *pshort; long *plong; pchar ++; pshort ++; plong ++; Giả sử các địa chỉ ban đầu tương ứng của 3 con trỏ là 100, 200 và 300, kết quả ta có các giá trị 101, 202 và 304 tương ứng Nếu viết tiếp plong += 5; => plong = 324 pchar -=10; => pchar = 91 pshort +=5; => pshort = 212 2 char *a; Ví dụ short *b; long *c; Các con trỏ a, b, c lần lượt trỏ tới ô nhớ 1000, 2000 và 3000. Cộng các con trỏ với một số nguyên: a = a + 1;//con trỏ a dời đi 1 byte b = b + 1;//con trỏ b dời đi 2 byte c = c + 1; //con trỏ c dời đi 4 byte Chú ý ++ và -- có độ ưu tiên cao hơn * nên *p++ tương đương với *(p++) tức là tăng địa chỉ mà nó trỏ tới chứ không phải tăng giá trị mà nó chứa. *p++ = *q++ sẽ tương đương với *p = *q; p=p+1; q=q+1; 4 Ví dụ: #include <iostream.h> void main () { int a = 20, b = 15, *pa, *pb, temp; pa = &a; // con trỏ pa chứa địa chỉ của a pb = &b; // con trỏ pb chứa địa chỉ của b temp = *pa; *pa = *pb; *pb = temp; // kết quả xuất ra cout << "a = " << a << endl; màn hình cout << “b = ” << b; a = 15 } b = 20 Con trỏ và mảng • Truy cập các phần tử mảng bằng con trỏ Kiểu mảng Kiểu con trỏ &<Tên mảng>[0] <Tên con trỏ > &<Tên mảng> [<Vị <Tên con trỏ> + <Vị trí>] trí> <Tên mảng>[<Vị *(< Tên con trỏ > + trí>] <Vị trí>) 6 char ch[10], *p; Ví dụ p = ch; • p được gán địa chỉ của phần tử đầu tiên của mảng ch. p = ch; • Để tham chiếu phần tử thứ 3 trong mảng ch, ta dùng một trong 2 cách sau: • ch[2] • *(p+2) Ví dụ #include <iostream.h> void main () { int numbers[5], * p; p = numbers; *p = 10; p++; *p = 20; p = &numbers[2]; *p = 30; p = numbers + 3; *p = 40; p = numbers; *(p+4) = 50; for (int n=0; n<5; n++) cout << numbers[n] << ", "; } Ví dụ int Numb 10 20 30 40 50 Numbers[5]; int *p; ers p p= Numbers; p p 20 p p *p = 10; p++; *p = p = 20; *p = 30; &numbers[2]; p = numbers +*p = 40; 3; p = numbers; *(p+4) = 50; Con trỏ và xâu • Ta có char tinhthanh[30] =“Da Lat”; • Tương đương : char *tinhthanh; tinhthanh=“Da lat”; Hoặc: char *tinhthanh =“Da lat”; • Ngoài ra các thao tác trên xâu cũng tương tự như trên mảng *(tinhthanh+3) = “l” • Chú ý : với xâu thường thì không thể gán trực tiếp như dòng thứ 3 10 Mảng các con trỏ • Một ưu điểm khác của mảng trỏ là ta có thể hoán chuyển các đối tượng (mảng con, cấu trúc.) được trỏ bởi con trỏ này bằng cách hoán đổi các con trỏ • Ưu điểm tiếp theo là việc truyền tham số trong hàm • Ví dụ: Vào danh sách lớp theo họ và tên, sau đó sắp xếp để in ra theo thứ tự ABC.
11 Mảng các con trỏ 12 Con trỏ trỏ tới con trỏ Ví dụ: in ra một ma trận vuông và cộng mỗi phần tử của ma trận với 10 13 Bài tập Viết chương trình tính tổng các phần tử mảng sử dụng con trỏ int main() { int numArray[6]; int i, sum = 0; int *ptr = numArray; cout << "Nhap 6 phan tu: " << endl; for (i = 0; i < 6; i++) cin >> *(ptr+i); ptr = numArray; for (i = 0; i < 6; i++) { sum = sum + *ptr; ptr++; } cout << "Tong cac phan tu cua mang la: " << sum << endl; return(0); } Bài tập Xây dựng một hàm tính độ dài của chuỗi sử dụng con trỏ. Bài tập Xây dựng một hàm tính độ dài của chuỗi sử dụng con trỏ. int string_ln(char*p) /* p=&str[0] */ { int count = 0; while (*p != '\0') { count++; p++; } return count; } Bài tập Viết chương trình nhập vào n số nguyên, thực hiện (sử dụng con trỏ): -Tính giá trị trung bình, giá trị min max của các phần tử trong mảng -Sắp xếp các phần tử trong mảng theo thứ tự tăng dần Ví dụ 19 Bộ nhớ động cho mảng 2 chiều • Cách 1: Biểu diễn mảng 2 chiều thành mảng 1 chiều • Gọi X là mảng hai chiều có kích thước m dòng và n cột. A là mảng một chiều tương ứng, khi đó X[i][j] = A[i*n+j] 20 Bộ nhớ động cho mảng 2 chiều • Cách 2: Dùng con trỏ của con trỏ • Ví dụ: Với mảng số nguyên 2 chiều có kích thước là R * C ta khai báo như sau: int **mt; mt = new int *[R]; int *temp = new int[R*C]; for (i=0; i< R; ++i) { mt[i] = temp; temp += C; } • Để giải phóng: delete [] mt[0]; delete [] mt; 21 Bộ nhớ động cho mảng 2 chiều • Ví dụ khác để cấp phát động cho mảng hai chiều chứa các số thực float 22 Bài tập Viết chương trình cộng hai ma trận với dữ liệu mỗi ma trận được cấp phát bộ nhớ động theo hai cách: 1) Sử dụng con trỏ 2) Sử dụng con trỏ trỏ đến con trỏ Cộng hai ma trận với mỗi ma trận được cấp phát động #include <iostream.h> //Cấ p phát vùng nhớ cho ma trận #include <conio.h> A int main() if(!AllocMatrix(&A,M,N)) { { int M,N; cout << "Khong con du bo int *A = NULL, nho! " *B = NULL, << endl; *C = NULL; return 1; cout << "Nhap so dong cua } ma tran:"; cin>>M; //Cấ p phát vùng nhớ cho ma trận B cout << "Nhap so cot cua ma tran:"; cin>>N; if(!AllocMatrix(&B,M,N)) { cout << "Khong con du bo nho! " << endl; FreeMatrix(A); return 1; //Cấ p phát vùng nhớ cout<<"Ma tran thu 1"<<endl; //cho ma trận C DisplayMatrix(A,M,N); if (! cout<<"Ma tran thu AllocMatrix(&C,M,N)) 2"<<endl; { DisplayMatrix(B,M,N); cout << "Khong con AddMatrix(A,B,C,M,N); du cout <<"Tong hai ma tran " bo nho!" <<endl; <<endl; DisplayMatrix(C,M,N); //Giải phóng vùng //Giải phóng vùng nhớ A nhớ A FreeMatrix(A); FreeMatrix(A); //Giải phóng vùng nhớ B //Giải phóng vùng FreeMatrix(B); nhớ B //Giải phóng vùng nhớ C FreeMatrix(B); FreeMatrix(C); return 1; return 0; } } cout << "Nhap ma tran thu 1"<< endl; InputMatrix(A,M,N,'A'); cout << "Nhap ma tran thu 2"<<endl; InputMatrix(B,M,N,'B'); clrscr(); //Cộng hai ma trận void AddMatrix(int *A,int *B,int*C,int M,int N) { for(int I=0;I<M*N;++I) C[I] = A[I] + B[I]; } //Cấ p phát vùng nhớ cho ma trận int AllocMatrix(int **A,int M,int N) { *A = new int [M*N]; if (*A == NULL) return 0; return 1; } //Giải phóng vùng nhớ void FreeMatrix(int *A) { if (A!=NULL) delete [] A; } //Nhập các giá trị của ma trận void InputMatrix(int *A,int M,int N,char Symbol) { for(int I=0;I<M;++I) for(int J=0;J<N;++J) { cout<<Symbol<<"["<<I<<"]["<<J<<"]="; cin>>A[I*N+J]; } } //Hiển thị ma trận void DisplayMatrix(int *A,int M,int N) { for(int I=0;I<M;++I) { for(int J=0;J<N;++J) { //canh le phai voi chieu dai 7 ky tu out.width(7); cout<<A[I*N+J]; } cout<<endl; } } Mở rộng: các vấn đề với cấp phát bộ nhớ động • Memory Leaks: Rò rỉ bộ nhớ xảy ra khi bộ nhớ được phân bổ không bao giờ được sử dụng lại nhưng không được giải phóng.
Mở rộng: các vấn đề với cấp phát bộ nhớ động • Double Free: giải phóng một khối bộ nhớ hai lần • Dangling Pointers: nếu một con trỏ vẫn tham chiếu bộ nhớ gốc sau khi nó được giải phóng, nó được gọi là con trỏ lơ lửng. Con trỏ không trỏ đến một đối tượng hợp lệ. Mở rộng: Con trỏ đến hằng (Pointers to a constant) • Một con trỏ có thể được xác định để trỏ đến một hằng số. Điều này có nghĩa là con trỏ không thể được sử dụng để sửa đổi giá trị mà nó đang tham chiếu.
• int num = 5; • const int limit = 500; • int *pi; // Pointer to an integer • const int *pci; // Pointer to a constant integer • pi = # • pci = &limit; Mở rộng: Con trỏ hằng (Constant pointers) • Con trỏ là hằng không thể thay đổi, giá trị mà nó trỏ đến có thể thay đổi • int num; • int *const cpi = # • Các lệnh sau là hợp lệ • *cpi = limit; • *cpi = 25; Truyền tham chiếu • Hàm nhận tham số là con trỏ void Swap(int *X, int *Y) { int Temp = *X; *X = *Y; *Y = Temp; } • Để hoán đổi giá trị hai biến A và B Swap(&A, &B); 32 Truyền tham chiếu • Hàm nhận tham số là tham chiếu void Swap(int &X, int &Y){ int Temp = X; X = Y; Y = Temp; } • Để hoán đổi giá trị hai biến A và B Swap(A, B); 33 Truyền tham chiếu #include <iostream.h> Khi một hàm trả int X = 4; về một tham int & MyFunc(){ return X; chiếu, chúng ta } có thể gọi hàm int main(){ ở phía bên trái Cout << "X=“ << X << endl; Cout << "X=“ << MyFunc() << endl; của một phép MyFunc() = 20; // ~X=20 gán. Cout << "X=“ << X << endl; return 0; } 34 Đa năng hoá toán tử • Định nghĩa lại chức năng của các toán tử đã có sẵn Thể hiện các phép toán một cách tự nhiên hơn • Ví dụ: thực hiện các phép cộng, trừ số phức Trong C: Cần phải xây dựng các hàm AddSP(), TruSP() Không thể hiện được phép cộng và trừ cho các biểu thức như: a=b+c-d+e+f-h-k 35 #include <stdio.h> struct SP { double real; double img; }; SP SetSP(double real, double img); SP AddSP(SP C1,SP C2); SP SubSP(SP C1,SP C2); void DisplaySP(SP C); int main(void) { SP C1,C2,C3,C4; C1 = SetSP(1.0); cout << "\nSo phuc thu nhat:"; DisplaySP(C1); cout << "\nSo phuc thu hai:"; DisplaySP(C2); C3 = AddSP(C1,C2); C4 = SubSP(C1,C2); cout << "\nTong hai so phuc nay:"; DisplaySP(C3); cout << "\nHieu hai so phuc nay:"; DisplaySP(C4); return 0; } 36 SP SetSP(double real,double img) { SP tmp; tmp.real = real; tmp.img = img; return tmp; } SP AddSP(SP C1,SP C2) { SP tmp; tmp.img; return tmp; } SP SubSP(SP C1,SP C2) { SP tmp; tmp.img; return tmp; } void DisplaySP(SP C) { cout << C.img; } C++ • C++ cho phép chúng ta có thể định nghĩa lại chức năng của các toán tử đã có sẵn một cách tiện lợi và tự nhiên. Điều này gọi là đa năng hóa toán tử. • Một hàm định nghĩa một toán tử có cú pháp sau: data_type operator operator_symbol ( parameters ) { ……………………………… } Trong đó: • data_type: Kiểu trả về.
• operator_symbol: Ký hiệu của toán tử. • parameters: Các tham số (nếu có).h> typedef struct SP { double real; double img;} SP; SP SetSP(double real, double img); void DisplaySP(SP C); SP operator + (SP C1, SP C2); SP operator - (SP C1, SP C2); int main() { SP C1,C2,C3,C4; C1 = SetSP(1.