Bài giảng Kỹ thuật lập trình - Chương 3: Vài kiến thức nâng cao về C, C++ - Vũ Đức Vượng
•Khái niệm : Giá trị các biến được lưu trữ trong bộ nhớ MT, có thể truy cập tới các giá trị đó qua tên biến, đồng thời cũng có thể qua địa chỉ của chúng trong bộ nhớ.
•Con trỏ thực chất là 1 biến mà nội dung của nó là địa chỉ của 1 đối tượng khác ( Biến, hàm, nhưng không phải 1 hằng số).
•Có nhiều kiểu biến với các kích thước khác nhau, nên có nhiều kiểu con trỏ. Con trỏ int để trỏ tới biến hay hàm kiểu int.
•Việc sử dụng con trỏ cho phép ta truy nhập tới 1 đối tượng gián tiếp qua địa chỉ của nó.
•Trong C, con trỏ là một công cụ rất mạnh, linh hoạt
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Kỹ thuật lập trình - Chương 3: Vài kiến thức nâng cao về C, C++ - Vũ Đức Vượng", để tải tài liệu gốc về máy hãy click vào nút Download ở trên.
File đính kèm:
- bai_giang_ky_thuat_lap_trinh_chuong_3_vai_kien_thuc_nang_cao.ppt
Nội dung text: Bài giảng Kỹ thuật lập trình - Chương 3: Vài kiến thức nâng cao về C, C++ - Vũ Đức Vượng
- 3.1 Mảng • Là một dãy hữu hạn các phần tử liên tiếp có cùng kiểu và tên • Có thể là 1 hay nhiều chiều, C không giới hạn số chiều của mảng • Khai báo theo syntax sau : DataType ArrayName [size]; Or DataType ArrayName [Size1][Size2] [Sizen];
- 3.2 Con trỏ • Khái niệm : Giá trị các biến được lưu trữ trong bộ nhớ MT, có thể truy cập tới các giá trị đó qua tên biến, đồng thời cũng có thể qua địa chỉ của chúng trong bộ nhớ. • Con trỏ thực chất là 1 biến mà nội dung của nó là địa chỉ của 1 đối tượng khác ( Biến, hàm, nhưng không phải 1 hằng số). • Có nhiều kiểu biến với các kích thước khác nhau, nên có nhiều kiểu con trỏ. Con trỏ int để trỏ tới biến hay hàm kiểu int. • Việc sử dụng con trỏ cho phép ta truy nhập tới 1 đối tượng gián tiếp qua địa chỉ của nó. • Trong C, con trỏ là một công cụ rất mạnh, linh hoạt
- Ví dụ : int i,j, *p; 100 i i= 5; p= & i; 102 j j= *p; *p= j+2; 104 p 100 5 i 100 5 i Gán i=5 gán p = & i 102 j 102 j 104 p 104 100 p 100 5 i 100 7 i gán J = *p *p = j+2 102 5 j 102 5 j 104 100 p 104 100 p
- Các phép toán trên con trỏ • Một biến trỏ có thể cộng hoặc trừ với 1 số nguyên n để cho kết quả là 1 con trỏ cùng kiểu, là địa chỉ mới trỏ tới 1 đối tượng khác nằm cách đối tượng đang bị trỏ n phần tử • Phép trừ giữa 2 con trỏ cho ta khoảng cách ( số phần tử ) giữa 2 con trỏ • Không có phép cộng, nhân, chia 2 con trỏ • Có thể dùng các phép gán, so sánh các con trỏ, nhưng cần chú ý đến sự tương thích về kiểu. Ví dụ : char *pchar; short *pshort; long *plong; sau khi xác lập địa chỉ cho các con trỏ, nếu : pchar ++; pshort ++; plong ++; và các địa chỉ ban đầu tương ứng của 3 con trỏ là 100, 200 và 300, thì kết quả ta có các giá trị tương ứng là : 101, 202 và 304 tương ứng
- Con trỏ void* • Là con trỏ không định kiểu (void *).Nó có thể trỏ tới bất kì một loại biến nào. Thực chất một con trỏ void chỉ chứa một địa chỉ bộ nhớ mà không biết rằng tại địa chỉ đó có đối tượng kiểu dữ liệu gì. => không thể truy cập nội dung của một đối tượng thông qua con trỏ void. Để truy cập được đối tượng thì trước hết phải ép kiểu con trỏ void về con trỏ có định kiểu của kiểu đối tượng float x; int y; void *p; // khai báo con trỏ void p = &x; // p chứa địa chỉ số thực x *p = 2.5; // báo lỗi vì p là con trỏ void /* cần phải ép kiểu con trỏ void trước khi truy cập đối tượng qua con trỏ */ *((float*)p) = 2.5; // x = 2.5 p = &y; // p chứa địa chỉ số nguyên y *((int*)p) = 2; // y = 2
- Con trỏ 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
- • 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 chuyển các con trỏ • Ưu điểm tiếp theo là việc truyền tham số trong hàm • Ví dụ : Vào ds lớp theo họ và tên, sau đó sắp xếp để in ra theo thứ tự ABC. #include #include #define MAXHS 50 #define MAXLEN 30
- Con trỏ trỏ tới con trỏ • Bản thân con trỏ cũng là 1 biến, vì vậy nó cũng có địa chỉ và có thể dùng 1 con trỏ khác để trỏ tới địa chỉ đó. • ; • Ví dụ : int x = 12; int *ptr = &x; int ptr_to_ptr = &ptr; • Có thể mô tả 1 mảng 2 chiều qua con trỏ của con trỏ theo công thức : ArrName[i][k] = *(*(ArrName+i)+k) Với ArrName+i là địa chỉ của phần tử thứ i của mảng *(ArrName+i) cho nội dung ftử trên *(ArrName+i)+k là địa chỉ phần tử [i][k]
- 3.3 Bộ nhớ động – Dynamic memory • Cho đến lúc này ta chỉ dùng bộ nhớ tĩnh : tức là khai báo mảng, biến và các đối tượng # 1 cách tường minh trước khi thực hiện ct. • Trong thực tế nhiều khi ta không thể xđịnh trước được kích thước bộ nhớ cần thiết để làm việc, và phải trả giá bằng việc khai báo dự trữ quá lớn • Nhiều đối tượng có kích thước thay đổi linh hoạt • Việc dùng bộ nhớ động cho phép xác định bộ nhớ cần thiết trong quá trình thực hiện của CT, đồng thời giải phóng chúng khi không còn cần đến để dùng bộ nhớ cho việc khác • Trong C ta dùng các hàm malloc, calloc, realloc và free để xin cấp phát, tái cấp phát và giải phóng bộ nhớ. Trong C++ ta chỉ dung new và delete
- Giải phóng bộ nhớ • delete ptr; // xóa 1 biến đơn • delete [] ptr; // xóa 1 biến mảng • ví dụ : #include int main() { int i,n; long total=100,x,*l; cout > n; l = new long [n]; if (l==NULL) exit(1); for (i=0;i > l[i] } Cout << “Danh sach cac so : \n” for (i=0;i<n;i++) cout << l[i] << “,”; delete []l; return 0; }
- Dùng bộ nhớ động cho mảng • 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; ??? } / Khai bao xong. Su dung : mt[i][j] như bình thường. cuối cùng để giải phóng: delete [] mt[0]; // xoá ? Tại sao? delete [] mt;
- //Cấp phát vùng nhớ cho ma trận C if (!AllocMatrix(&C,M,N)) { cout<<"Khong con du bo nho!"<<endl; FreeMatrix(A);//Giải phóng vùng nhớ A FreeMatrix(B);//Giải phóng vùng nhớ B return 1; } 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(); cout<<"Ma tran thu 1"<<endl; DisplayMatrix(A,M,N); cout<<"Ma tran thu 2"<<endl; DisplayMatrix(B,M,N); AddMatrix(A,B,C,M,N); cout<<"Tong hai ma tran"<<endl; DisplayMatrix(C,M,N); FreeMatrix(A);//Giải phóng vùng nhớ A FreeMatrix(B);//Giải phóng vùng nhớ B FreeMatrix(C);//Giải phóng vùng nhớ C return 0; }
- //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 >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) { out.width(7);//canh le phai voi chieu dai 7 ky tu cout<<A[I*N+J]; } cout<<endl; } }
- Dùng tham chiếu với c++ void Swap(int &X, int &Y); { int Temp = X; X = Y; Y = Temp ; } • Chúng ta gọi hàm như sau : Swap(A, B); • Với cách gọi hàm này, C++ tự gởi địa chỉ của A và B làm tham số cho hàm Swap().
- Phép đa năng hóa (Overloading) • Với ngôn ngữ C++, chúng ta có thể đa năng hóa các hàm và các toán tử (operator). Đa năng hóa là phương pháp cung cấp nhiều hơn một định nghĩa cho tên hàm đã cho trong cùng một phạm vi. Trình biên dịch sẽ lựa chọn phiên bản thích hợp của hàm hay toán tử dựa trên các tham số mà nó được gọi. • Với C, tên hàm phải là duy nhất
- #include #include int MyAbs(int X) { return abs(X); } long MyAbs(long X) { return labs(X); } double MyAbs(double X) { return fabs(X); } int main() { int X = -7; long Y = 200000l; double Z = -35.678; cout<<"Tri tuyet doi cua so nguyen "<<X<<" la “ <<MyAbs(X)<<endl; cout<<"Tri tuyet doi cua so nguyen "<<Y<<" la " <<MyAbs(Y)<<endl; cout<<"Tri tuyet doi cua so thuc "<<Z<<" la " <<MyAbs(Z)<<endl; return 0; }
- #include /* Dinh nghia so phuc */ struct SP { double THUC; double Image; } ; SP SetSP(double R,double I); 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,2.0); C2 = SetSP(-3.0,4.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; }
- C++ • Trong ví dụ trên, ta dùng hàm để cài đặt các phép toán cộng và trừ hai số phức ; => phức tạp,không thoải mái khi sử dụng , vì thực chất thao tác cộng và trừ là các toán tử chứ không phải là hàm. • 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 hơn rất nhiều. Đ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ó).
- SetSP(double R,double I) { SP Tmp; Tmp.THUC = R; Tmp.AO = I; return Tmp; } //Cong hai so phuc SP operator + (SP C1,SP C2) { SP Tmp; Tmp.THUC = C1.THUC+C2.THUC; Tmp.AO = C1.AO+C2.AO; return Tmp; } //Tru hai so phuc SP operator - (SP C1,SP C2) { SP Tmp; Tmp.THUC = C1.THUC-C2.THUC; Tmp.AO = C1.AO-C2.AO; return Tmp; } //Hien thi so phuc void DisplaySP(SP C) { cout<<"("<<C.THUC<<","<<C.AO<<")"; }