Bài giảng Kỹ Thuật lập trình - Chương 2: Một số vấn đề trong kỹ thuật lập trình - Lương Mạnh Bá

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à chồng 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ó). #include //Dinh nghia so phuc struct { double THUC; double AO; }SP; SP SetSP(double R,double I); 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.1,2.0); C2 = SetSP(-3.0,4.0); cout<<"\nSo phuc thu nhat:"; DisplaySP(C1); cout<<"\nSo phuc thu hai:"; DisplaySP(C2); C3 = C1 + C2; C4 = C1 - C2; cout<<"\nTong hai so phuc nay:"; DisplaySP(C3); cout<<"\nHieu hai so phuc nay:"; DisplaySP(C4); return 0; }

ppt72 trang | Chia sẻ: hachi492 | Ngày: 07/01/2022 | Lượt xem: 281 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Bài giảng Kỹ Thuật lập trình - Chương 2: Một số vấn đề trong kỹ thuật lập trình - Lương Mạnh Bá, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Last update 8-2010 SE-SoICT KTLT-2. 1 Ch ươ ng II Một số vấn đề trong KTLT (6LT – 2BT) Last update 8-2010 SE-SoICT KTLT-2. 2 Ch ươ ng II Tổ chức chương trình Biến cục bộ/Biến toàn cục Cấu trúc dữ liệu động Cấp phát tĩnh và cấp phát động Last update 8-2010 SE-SoICT KTLT-2. 3 2.1 Tổ chức CT Khi giải quyết BT, theo phương pháp tiếp cận cấu trúc, BT được phân chia thành các BT con và các CT con sẽ đảm nhiệm. Quá trình phân rã cứ tiếp tục đến 1 mức đủ chi tiết. Mọi NNLT đều cung cấp phương tiện để tổ chức chương trình con. Gọi là CT con để phân biệt với CT ở mức ngoài (main) được gọi thực hiện bởi ND. CT con có cấu trúc giống như main song được gọi và có thể gọi các CT con khác . Last update 8-2010 SE-SoICT KTLT-2. 4 CT con (tiếp) CT con thường gồm 2 loại tùy theo cách sử dụng: CT con Hàm CT con Thủ tục CT con Hàm là CT con dùng để thực hiện 1 tính toán và trả về 1 giá trị của 1 kiểu DL hợp lệ qui định bởi NNLT đó. CT con thủ tục thực hiện 1 nhiệm vụ phức tạp hơn (không nhất thiết là tính toán) và không trả về 1 kết quả nào. Trong C không phân biệt 2 khái niệm này. Tuy nhiên vẫn được dùng theo phong cách chung . Last update 8-2010 SE-SoICT KTLT-2. 5 Thí dụ CT con (tiếp) CT con Hàm trong C int GT(int n) { if ( n==0) return 1; else return n * GT(n-1); } CT con thủ tục trong C void nhapmang(Mang V, int n) {int i; for (i=0;i <n;i++) { printf("\n V[ %d ]=",i); scanf("%d",&V[i]); } } Last update 8-2010 SE-SoICT KTLT-2. 6 CT con (tiếp) Trong hàm, để trả về kết quả tính toán người ta dùng lệnh/biểu thức. Lệnh/biểu thức phụ thuộc vào NNLT. Trong C, lệnh trả về kết quả tính toán: return Trong thủ tục không trả về kết quả nên dùng dạng “void” có nghĩa là rỗng, không có: không có giá trị, không có tham số. Thí dụ void main(void) == main() Lệnh gọi CT con hàm xuất hiện trong bt; lệnh gọi CT con thủ tục là 1 lệnh hợp lệ. Last update 8-2010 SE-SoICT KTLT-2. 7 Tham số thực và tham số hình thức Tham số thực là tham số xuất hiện trong lời gọi CT con. Tham số hình thức là tham số xuất hiện trong chữ ký (header) của CT con. int GT(int n) { if ( n==0) return 1; else return n * GT(n-1); } . Printf(“\3!= %d”,GT(3)); N là tham số hình thức 3 là tham số thực Last update 8-2010 SE-SoICT KTLT-2. 8 Quan hệ giữa CT gọi và CT được gọi Khi CT con gọi (calling) 1 CT con khác (called) “ giá trị” của tham số thực sẽ truyền cho tham số hình thức. Như vậy mối quan hệ “gọi & được gọi” thông qua tham số (cách thức chuẩn trong LT). Có nhiều cách truyền tham số: Truyền trị (by val): giá trị của th/số thực sẽ truyền cho th/số hình thức. Mọi thay đổi giá trị của tham số hình thức trong CT con không làm thay đổi giá trị của tham số thực khi quay về CT gọi. Cú pháp, ngữ nghĩa của Th/số hình thức!!! Last update 8-2010 SE-SoICT KTLT-2. 9 Quan hệ giữa CT gọi và được gọi (tiếp) Truyền theo tham chiếu (by reference) hay địa chỉ (address) hay : địa chỉ của th/số thực truyền cho th/số hình thức. Mọi thay đổi giá trị của th/số hình thức trong CT con sẽ tác động đến th/số thực khi quay về CT con gọi ( hiệu ứng lề – side effect ). => C ú pháp, ngữ nghĩa của Th/số thực!!! TD: Minh họa 1 số ví dụ Truyền theo tên (macro) Trong NN C chỉ có 1 kiểu truyền là theo trị. Tuy nhiên LTV vẫn có thể dùng thủ thuật để truyền theo địa chỉ (xem phần sau). Last update 8-2010 SE-SoICT KTLT-2. 10 Tham số truyền trị #include #include swap (int a, int b ); { int temp = a; a = b; b= temp); } main() { int c = 5; int d = 7; clrscr(); swap (c,d); printf("\n c= %d d=%d",c,d); getch(); } Kết quả in ra c = 5 d = 7 Last update 8-2010 SE-SoICT KTLT-2. 11 Tham số truyền theo địa chỉ #include #include swap (int *a, int *b ); { int temp = *a; *a = *b; *b= temp; } main() { int c = 5; int d = 7; clrscr(); swap (&c,&d); printf("\n c= %d d=%d",c,d); getch(); } Kết quả in ra c = 7 d = 5 Last update 8-2010 SE-SoICT KTLT-2. 12 Tổ chức CT con Để tiện sử dụng CT con được tổ chức theo nhiều hình thức khác nhau: Trong cùng 1 chương trình với CT chính Ghép thành đơn vị CT Ghép thành mô đun (đơn thể chương trình) Cách tổ chức thứ 2 và 3 tiện dụng hơn vì tính tái sử dụng. Các CT con của ND có thể chuyển vào Thư Viện chương trình của NNLT đó (các NNLT đều có công cụ hỗ trợ việc này. TD: minh họa qua C/ Pascal. Last update 8-2010 SE-SoICT KTLT-2. 13 Ch ươ ng II Tổ chức chương trình Biến cục bộ/Biến toàn cục Cấu trúc dữ liệu thay đổi Cấp phát tĩnh và cấp phát động Last update 8-2010 SE-SoICT KTLT-2. 14 2.2 Biến cục bộ (local) và toàn cục (global) Khi 1 CT được gọi nó được nạp vào bộ nhớ và thường trú trong bộ nhớ đến khi kết thúc thực hiện. Đó chính là vòng đời của CT => Do vậy các đại lượng định nghĩa trong CT đó cũng kết thúc vòng đời của mình. => Nảy sinh khái niệm biến cục bộ và biến toàn cục. Biến cục bộ (local variables): Các biến được định nghĩa trong 1 CT và chỉ được sử dụng trong CT con đó. Nó có cùng vòng đời với CT sinh ra nó . Khái niệm cục bộ cũng là tương đối và phụ thuộc vào cách tổ chức CT. Nó là cục bộ của CT con đó song là toàn cục với CT con của nó. Last update 8-2010 SE-SoICT KTLT-2. 15 Biến cục bộ và toàn cục (tiếp) Biến toàn cục (global variables): Các biến được định nghĩa trong 1 CT và được sử dụng trong CT con đó và các CT con của nó. => Đ/n ở 1 nơi và sử dụng ở nơi khác. Một loại nữa là static : Nó là cục bộ của 1 CT song lại duy trì vòng đời cùng với mức cao nhất: Khi CT nơi nó đ/n kết thúc (loại khỏi bộ nhớ) nó vẫn còn tồn tại. => Ưu điểm: tránh hiệu ứng lề. Chú ý cách dùng Last update 8-2010 SE-SoICT KTLT-2. 16 Thí dụ int V[]; void inmang(int n) { int i; for (i=0;i <n;i++) printf(" %4d",V[i]); } main () {int i,j,n, tam; clrscr(); printf(" So Phan tu cua mang:\n"); scanf("%d",&n); nhapmang(n); printf("\n Day so vua nhap:"); inmang(n); } => Lưu ý sử dụng biến toàn cục để liên kết với CT con!!! Biến cục bộ của main Biến cục bộ của CT con inmang Last update 8-2010 SE-SoICT KTLT-2. 17 Ch ươ ng II Tổ chức chương trình Biến cục bộ/Biến toàn cục Cấu trúc dữ liệu thay đổi Cấp phát tĩnh và cấp phát động Last update 8-2010 SE-SoICT KTLT-2. 18 2.3 Cấu trúc dữ liệu thay đổi Trong quá trinh lập trình nhiều khi ta cần có kiểu dữ liệu đáp ứng được tình huống cụ thể. TD: Học sinh phải học các môn học văn hóa như nhau. Tuy nhiên việc học nghề lại phụ thuộc vào giới tính: Nam học Mộc, Cơ khí; Nữ học Thêu, Cắm hoa, Vậy cấu trúc nào hỗ trợ biểu diễn đáp ứng tình huống trên. Pascal: cấu trúc mẫu tin thay đỏi C: dùng cấu trúc Union Last update 8-2010 SE-SoICT KTLT-2. 19 Mẫu tin biến thể struct WORDREGS { unsigned int ax, bx, cx, dx, si, di, cflag, flags; }; struct BYTEREGS { unsigned char al, ah, bl, bh, cl, ch, dl, dh; }; union REGS { struct WORDREGS x; struct BYTEREGS h; }; Last update 8-2010 SE-SoICT KTLT-2. 20 Mẫu tin biến thể (tiếp) Typedef union { struct { long abscisse ; long ordonne; }cart; struct { float rho; float theta; }pol; coord p1,p2;// định nghĩa 2 điểm p1 và p2 => Truy nhập tới thành phần của các điểm: P1.cart.abscisse, p2.pol.theta Last update 8-2010 SE-SoICT KTLT-2. 21 Tệp (file) 2- Trong CT để lưu các thông tin lâu dài, người ta sử dụng các tệp. Có nhiều loại tệp khác nhau: tệp văn bản (text file), tệp có cấu trúc thí dụ như tệp lưu danh sách SV 1 lớp, tệp nhị phân không cấu trúc (tệp ảnh, tệp mã máy). Cũng có thể cùng 1 kiểu tệp song do cách dùng khác nhau mà ta coi chúng khác nhau. Trong C chỉ có 2 loại tệp là text file và tệp nhị phân (binary file). Last update 8-2010 SE-SoICT KTLT-2. 22 TD đọc ghi tệp nhị phân void main() { int i , j, n; mang A, B; FILE *fp; char filename[]="Mang.Txt"; clrscr(); if ((fp=fopen(filename,"w+"))==NULL) printf("\n Khong mo duoc tep!"); else { do { printf("\n so phan tu (1 <n <10)"); scanf("%d",&n); } while ((n 10)); printf("\n nhap cac phan tu:"); for (i =0;i<n;i++) for (j=0;j<n;j++) {printf("\n A[%d,%d]=",i,j); scanf("%d",&A[i][j]); } fwrite(&A,sizeof(int), MAX * MAX,fp); Last update 8-2010 SE-SoICT KTLT-2. 23 TD đọc ghi tệp nhị phân if ((fp=fopen(filename,"r+"))==NULL) printf("\n Khong mo duoc tep!"); else { fread(&B,sizeof(int), MAX * MAX,fp); fclose(fp); }// ket thuc else printf("\n Mang doc ra tu tep:"); for (i = 0;i<n;i++) { for (j = 0;j<n;j++) printf("\ %d ",B[i][j]); printf("\n"); } Last update 8-2010 SE-SoICT KTLT-2. 24 TD đọc ghi tệp văn bản void main() { char c; FILE *fv, *fr; char *filename="D_ghitep.cpp"; clrscr(); if ((fv=fopen(filename,"r+"))==NULL) printf("\n Khong mo duoc tep!"); else { filename= "D_ghitep.Txt"; if ((fr = fopen(filename,"w+")) == NULL); do { c = fgetc(fv); fputc(c,fr); } while (c != EOF); fclose(fv); fclose(fr); } Last update 8-2010 SE-SoICT KTLT-2. 25 Ch ươ ng II Tổ chức chương trình Biến cục bộ/Biến toàn cục Cấu trúc dữ liệu thay đổi Cấp phát tĩnh và cấp phát động Last update 8-2010 SE-SoICT KTLT-2. 26 Cấu trúc bộ nhớ cơ bản Bộ nhớ tĩnh (Stack) Bộ nhớ động (Heap) Vùng nhớ thấp Vùng nhớ cao Vùng nhớ tĩnh còn gọi là stack dùng để lưu các CT hệ thống, các CT người dùng. Kích thước cố định (biết trước). Đ ược cấp phát từ địa chỉ thấp nhất và đi dần lên. Vùng nhớ động gọi là Heap: vùng nhớ dành cho cấp phát động (không biết trước về số lượng và kích thước). Các NNLT có các dẫn hướng cho phép LTV điều chỉnh/xin cấp phát vùng nhớ cho Heap và Stack. Last update 8-2010 SE-SoICT KTLT-2. 27 Cấp phát tĩnh Cấp phát tĩnh: dùng cho các biến tĩnh (khai báo trong chương trình và kích thước cố định), thí dụ như khai báo mảng, biến và các đối tượng 1 cách tường minh. => Mọi đại lượng trong CT đều phải khai báo trước khi sử dụng. Hạn chế: kém mềm dẻo, không linh hoạt và lãng phí bộ nhớ vì nhiều đối tượng có kích thước thay đổi linh hoạt. Giải pháp: cấp phát theo yêu cầu sử dụng hay cấp phát động. Chỉ thực hiện khi CT được gọi thực hiện. Các đại lượng được cấp phát động không có tên (không được khai báo trong CT). Last update 8-2010 SE-SoICT KTLT-2. 28 Cấp phát động Các biến này được cấp phát tại vùng nhớ động: Heap. Mỗi khi có yêu cầu sẽ được cấp phát. Mỗi NNLT có cơ chế riêng: 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++ là new và delete . Việc cấp phát có thể cho từng biến (biến động có kiểu) hay cho cả 1 vùng nhớ (biến động không kiểu). Khi vùng nhớ động bị tràn, HT sẽ thông báo heap overflow. TD: xem trong phần bổ sung trong C/C++ Last update 8-2010 SE-SoICT KTLT-2. 29 Cấp phát/ Giải phóng bộ nhớ : new và delete Để xin cấp phát bộ nhớ ta dùng : = new ; hoặc = new [số ftử]; dòng trên xin cấp phát một vùng nhớ cho một biến đơn, còn dòng dưới : cho một mảng các phần tử có cùng kiểu với kiểu dữ liệu. Bộ nhớ động được quản lý bởi hệ điều hành, và với môi trường đa nhiệm (multitask interface) thì bộ nhớ này sẽ được chia sẻ giữa hàng loạt các ứng dụng, vì vậy có thể không đủ bộ nhớ. Khi đó toán tử new sẽ trả về con trỏ NULL. ví dụ : int *pds; pds = new int [200]; if (pds == NULL) { // thông báo lỗi và xử lý Last update 8-2010 SE-SoICT KTLT-2. 30 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<n;i++){ cout > l[i] } Cout << “Danh sach cac so : \n” for (i=0;i<n;i++) cout << l[i] << “,”; delete []l; return 0; } Last update 8-2010 SE-SoICT KTLT-2. 31 Bài tập 1 Viết chương trình nhân 2 ma trận. Yêu cầu: Sử dụng các qui tắc viết mã hiệu quả. Mảng khai báo tĩnh Mô đun hóa: dùng các chương trình con cần thiết Viết chú giải hợp lý Truy nhập bộ nhớ Đặt tên biến dễ nhớ, nhất quán Nộp listing CT kèm theo kết quả (ít nhất 3 kết quả). CT viết trên C hoặc C++. Last update 8-2010 SE-SoICT KTLT-2. 32 Bài tập 2 Viết lại chương trình nhân hai ma trận của bài tập trước: ma trận được cấp phát động theo 2 cách. Nộp listing CT kèm theo kết quả (ít nhất 3 kết quả). CT viết trên C hoặc C++. Chú ý in ma trận cùng với phân phối địa chỉ các phần tử Đối sánh kết quả với bài tập trước Last update 8-2010 SE-SoICT KTLT-2. 33 Một số kiến thức nâng cao trong C/ C++ Last update 8-2010 SE-SoICT KTLT-2. 34 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 cú pháp sau : DataType ArrayName [size]; hooặc DataType ArrayN [Size1][Size2]...; Last update 8-2010 SE-SoICT KTLT-2. 35 Khởi tạo giá trị cho mảng theo 2 cách: Khi khai báo : float y[5]={3.2, 1.2, 4.5 ,6.0, 3.6} int m[6][2] = {{1,1},{1,2},{2,1},{2,2},{3,1},{3,2}}; char s1[6] ={‘H’,’a’,’n’,’o’,’i’,’\0’}; hoặc char s1[6] = “Hanoi”; char s1[] =“Dai hoc Bach Khoa Hanoi”; L=24 int   m [][] ={{1,2,3},{4,5,6}}; Khai báo rồi gán giá trị cho từng phần tử của mảng . Ví dụ : int m[4]; m[0] = 1; m[1] = 2; m[2] = 3; m[3] = 4; Last update 8-2010 SE-SoICT KTLT-2. 36 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ớ (CTDL>). 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. Last update 8-2010 SE-SoICT KTLT-2. 37 Khai báo con trỏ : Cú pháp : dataType * PointerName; Chỉ rằng đây là con trỏ trỏ về kiểu dataType. Sau khi khai báo, ta được con trỏ NULL (chưa trỏ tới 1 đối tượng nào). Để sử dụng con trỏ, ta dùng toán tử lấy địa chỉ & PointerName = & VarName Ví dụ : int a; int *p; a=10; p= &a; => *p = 10 Để lấy nội dung biến do con trỏ trỏ tới, ta dùng toán tử lấy nội dung *: * PointerName Last update 8-2010 SE-SoICT KTLT-2. 38 Ví dụ : int i,j, *p; i= 5; p= & i; j= *p; *p= j+2; 100 i 102 j 104 p Gán i=5 100 5 i 102 j 104 p 100 5 i 102 j 104 100 p gán p = & i gán j = *p 100 5 i 102 5 j 104 100 p *p = j+2 100 7 i 102 5 j 104 100 p Đ/c bộ nhớ Giá trị Biến Last update 8-2010 SE-SoICT KTLT-2. 39 Chú ý Một con trỏ chỉ có thể trỏ tới 1 đối tượng cùng kiểu. Toán tử 1 ngôi * và & có độ ưu tiên cao hơn các toán tử số học. Ta có thể viết *p mọi nơi có đối tượng mà nó trỏ tới xuất hiện. int x = 5, *p; p = &x; => x=x+10; ~ *p = *p+10; Ta cũng có thể gán nội dung 2 con trỏ cho nhau : khi đó cả hai con trỏ cùng trỏ tới 1 đối tượng. int x=10, *p, *q; p = &x; q = p; => p và q cùng trỏ tới x Last update 8-2010 SE-SoICT KTLT-2. 40 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. Last update 8-2010 SE-SoICT KTLT-2. 41 Nếu viết tiếp : plong += 5; => plong = 324 (304 + 5 x 4) pchar -=10; => pchar = 91 pshort +=5; => pshort = 212 Chú ý : ++ và – có độ ưu tiên cao hơn * => *p++ ~ *(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 : *p = *q; p=p+1; q=q+1; => Cần dùng dấu () để tránh nhầm lẫn Vì cả 2 phép tăng đều diễn ra sau khiphép gán được thực hiện Last update 8-2010 SE-SoICT KTLT-2. 42 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 Last update 8-2010 SE-SoICT KTLT-2. 43 Con trỏ và mảng Giả sử ta có : int a[30]; thì & a[0] là địa chỉ phần tử đầu tiên của mảng đó, đồng thời là địa chỉ của mảng. Trong C, tên của mảng chính là 1 hằng địa chỉ = địa chỉ của phần tử đầu tiên của mảng. a = &a[0] = a+0; a+i = &a[i]; Tuy vậy cần chú ý rằng a là 1 hằng => không thể dùng nó trong câu lệnh gán hay toán tử tăng, giảm như a++; Xét con trỏ : int *pa; pa = & a[0]; => pa trỏ vào ftử thứ nhất của mảng và : pa +1 sẽ trỏ vào phần tử thứ 2 của mảng *(pa+i) sẽ là nội dung của a[i] Last update 8-2010 SE-SoICT KTLT-2. 44 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 Last update 8-2010 SE-SoICT KTLT-2. 45 Mảng các con trỏ Con trỏ cũng là một loại dữ liệu nên ta có thể tạo một mảng các phần tử là con trỏ theo dạng thức. * [ ] ;  vd : ch ar *ds[10]; ds là 1 mảng gồm 10 ftử, mỗi ftử là 1 con trỏ kiểu char, dùng để lưu trữ địa chỉ của 10 xâu ký tự nào đó. Cũng có thẻ khởi tạo trực tiếp các giá trị khi khai báo char * ma[10] = {“mot”,”hai”,”ba”...}; Chú ý : cần phân biệt mảng con trỏ và mảng nhiều chiều. Mảng nhiều chiều là mảng thực sự được khai báo và có đủ vùng nhớ dành sẵn cho các ftử. Mảng con trỏ chỉ dành không gian nhớ cho các biến trỏ ( chứa địa chỉ). Khi khởi tạo hay gán giá trị : cần thêm bộ nhớ cho các ftử sử dụng => tốn nhiều hơn. Last update 8-2010 SE-SoICT KTLT-2. 46 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 Last update 8-2010 SE-SoICT KTLT-2. 47 int main () { int i, j, count = 0; char ds[MAXHS][MAXLEN]; char *ptr[MAXHS], *tmp; while ( count < MAXHS) { printf(“ Hoc sinh thu : %d “,count+1); gets(ds[count]); if (strlen(ds[count] == 0) break; ptr[count] = ds +count; ++count; } for ( i=0;i<count-1;i++) for ( j =i+1;j < count; j++) if (strcmp(ptr[i],ptr[j])>0) { tmp=ptr[i]; ptr[i] = ptr[j]; ptr[j] = tmp; } for (i=0;i<count; i++) printf(“\n %d : %s”, i+1,ptr[i]); } Last update 8-2010 SE-SoICT KTLT-2. 48 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] Last update 8-2010 SE-SoICT KTLT-2. 49 Ví dụ : in ra 1 ma tran vuông và cộng mỗi ptử của MT với 10 # include #define hang 3 #define cot 3 int main() { int mt[hang][cot] = {{7,8,9},{10,13,15},{2,7,8}}; int i,j; for (i=0; i<hang; i++) { for (j=0; j<cot; j++) printf(“ %d ”,mt[i][j]); printf(“\n”); } // cách dùng địa chỉ (con trỏ tương đương) for (i=0; i<hang;i++) { for (j=0;j<cot;j++) { *(*(mt+i)+j)=*(*(mt+i)+j) +10;// Tăng giá trị ptử lên 10 đơn vị printf(“ %d “, *(*(mt+i)+j); } printf(“\n”); } } Last update 8-2010 SE-SoICT KTLT-2. 50 Dùng bộ nhớ động cho mảng Ta có thể coi một mảng 2 chiều là 1 mảng 1 chiều như hình sau : 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 ,thì X[i][j] = A[ i*n + j] nếu phân bố theo hàng. Last update 8-2010 SE-SoICT KTLT-2. 51 Dùng bộ nhớ động cho mảng (cách 1) 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; Last update 8-2010 SE-SoICT KTLT-2. 52 Dùng bộ nhớ động cho mảng (tiếp) void main() { int *A; int row, col, i, j; clrscr(); printf("\n so hang"); scanf("%d",&row); printf("\n so cot"); scanf("%d",&col); // cap phat vung nho cho mang A A = (int *) malloc (row * col *sizeof(int)); // nhap mang 1 for (i=0;i<row;i++) for (j=0;j<col;j++) { printf("M1[%d][%d]=",i,j); scanf("%d",A +i*row+j); } Last update 8-2010 SE-SoICT KTLT-2. 53 Dùng bộ nhớ động cho mảng (tiếp) // In mảng printf("\n mang 1\n"); for (i=0;i<row;i++) { for (j=0;j<col;j++) printf (" %p %d ",A +i *row+j, *(A +i *row+j)); printf("\n"); } getch(); } Last update 8-2010 SE-SoICT KTLT-2. 54 Dùng bộ nhớ động cho mảng (cách 2) // chuong trinh thu cap phat dong cho mang 2 chieu row x col phan tu. // CT cap phat lan dau theo hang: Spt bang so hang voi con tro *A. // CT cap phat tiep theo cot voi con tro *(A+i) void main() { int **A; int row, col, i, j; clrscr(); printf("\n so hang"); scanf("%d",&row); printf("\n so cot"); scanf("%d",&col); // cap phat vung nho cho mang 1, mang 2 *A = (int *) malloc (row *sizeof(int)); for (i=0;i <row;i++) *( A+i) = (int *) malloc (col *sizeof(int)); Last update 8-2010 SE-SoICT KTLT-2. 55 Dùng bộ nhớ động cho mảng (tiếp) // nhap mang 1 for (i=0;i<row;i++) for (j=0;j<col;j++) { printf("M1[%d][%d]=",i,j); scanf("%d ", *(A +i)+j); } // In mang printf("\n mang 1\n"); for (i=0;i<row;i++) {for (j=0;j<col;j++) printf (" %p %d ", *(A +i)+j, *(*(A +i) +j)); printf("\n"); } Last update 8-2010 SE-SoICT KTLT-2. 56 CT cộng hai ma trận với mỗi ma trận được cấp phát động #include #include int main() { int M,N; int *A = NULL,*B = NULL,*C = NULL; clrscr(); cout>M; cout>N; //Cấp phát vùng nhớ cho ma trận A if (!AllocMatrix(&A,M,N)) { cout<<"Khong con du bo nho!"<<endl; return 1; } //Cấp phát vùng nhớ cho ma trận B if (!AllocMatrix(&B,M,N)) { cout<<"Khong con du bo nho!"<<endl; FreeMatrix(A);//Giải phóng vùng nhớ A return 1; } Last update 8-2010 SE-SoICT KTLT-2. 57 //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; } Last update 8-2010 SE-SoICT KTLT-2. 58 //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; } Last update 8-2010 SE-SoICT KTLT-2. 59 //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) { out.width(7);//canh le phai voi chieu dai 7 ky tu cout<<A[I*N+J]; } cout<<endl; } } Last update 8-2010 SE-SoICT KTLT-2. 60 Phép tham chiếu Trong C, hàm nhận tham số là con trỏ đòi hỏi chúng ta phải thận trọng khi gọi hàm. Chúng ta cần viết hàm hoán đổi giá trị giữa hai số như sau: void Swap(int *X, int *Y); { int Temp = *X; *X = *Y; *Y = *Temp; } Để hoán đổi giá trị hai biến A và B thì chúng ta gọi hàm: Swap(&A, &B); Rõ ràng cách viết này không được thuận tiện lắm. Last update 8-2010 SE-SoICT KTLT-2. 61 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() . Last update 8-2010 SE-SoICT KTLT-2. 62 Khi một hàm trả về một tham chiếu, chúng ta có thể gọi hàm ở phía bên trái của một phép gán. #include int X = 4; int & MyFunc() { return X; } int main() { cout<<"X="<<X<<endl; cout<<"X="<<MyFunc()<<endl; MyFunc() = 20; //Nghĩa là X = 20 cout<<"X="<<X<<endl; return 0; } Last update 8-2010 SE-SoICT KTLT-2. 63 Phép đa năng hóa/chồng (Overloading) Với ngôn ngữ C++, chúng ta có thể chồng các hàm và các toán tử (operator). Chồng là phương pháp cung cấp nhiều hơn một định nghĩa cho tên hàm/toán tử đã 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 Last update 8-2010 SE-SoICT KTLT-2. 64 Chồng hàm (Functions overloading) Trong c phải dùng 3 hàm để tính trị tuyệt đối ? : int abs(int i); long labs(long l); double fabs(double d); C++ cho phép chúng ta tạo ra các hàm khác nhau có cùng một tên. int abs(int i); long abs(long l); double abs(double d); Last update 8-2010 SE-SoICT KTLT-2. 65 #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; } Last update 8-2010 SE-SoICT KTLT-2. 66 Chồng to án t ử Trong ngôn ngữ C, khi chúng ta tự tạo ra một kiểu dữ liệu mới, chúng ta thực hiện các thao tác liên quan đến kiểu dữ liệu đó thường thông qua các hàm, điều này trở nên không thoải mái. Ví dụ : cài đặt các phép toán cộng và trừ số phức Last update 8-2010 SE-SoICT KTLT-2. 67 #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; } Last update 8-2010 SE-SoICT KTLT-2. 68 SP SetSP(double R,double I) { SP Tmp; Tmp.THUC = R; Tmp.Image = I; return Tmp; } SP AddSP(SP C1,SP C2) { SP Tmp; Tmp.THUC = C1.THUC+C2.THUC; Tmp.Image = C1.Image+C2.Image; return Tmp; } SP SubSP(SP C1,SP C2) { SP Tmp; Tmp.THUC = C1.THUC-C2.THUC; Tmp.Image = C1.Image-C2.Image; return Tmp; } void DisplaySP(SP C) { cout << C.THUC <<‘ i ’ << C.Image; } Last update 8-2010 SE-SoICT KTLT-2. 69 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à chồng 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ó). Last update 8-2010 SE-SoICT KTLT-2. 70 #include //Dinh nghia so phuc struct { double THUC; double AO; }SP; SP SetSP(double R,double I); 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.1,2.0); C2 = SetSP(-3.0,4.0); cout<<"\nSo phuc thu nhat:"; DisplaySP(C1); cout<<"\nSo phuc thu hai:"; DisplaySP(C2); C3 = C1 + C2; C4 = C1 - C2; cout<<"\nTong hai so phuc nay:"; DisplaySP(C3); cout<<"\nHieu hai so phuc nay:"; DisplaySP(C4); return 0; } Last update 8-2010 SE-SoICT KTLT-2. 71 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<<")"; } Last update 8-2010 SE-SoICT KTLT-2. 72 Các giới hạn của chồng toán tử K hông thể định nghĩa các toán tử mới. Hầu hết các toán tử của C++ đều có thể được chồng. Các toán tử sau không được chồng là : :: Toán tử định phạm vi. .* Truy cập đến con trỏ là trường của struct hay class . . Truy cập đến trường của struct hay class . ?: Toán tử điều kiện sizeof   C ác ký hiệu tiền xử lý . K hông thể thay đổi thứ tự ưu tiên của một toán tử c ũng nh ư s ố các toán hạng của nó. K hông thể thay đổi ý nghĩa của các toán tử khi áp dụng cho các kiểu có sẵn. Chồng các toán tử không thể có các tham số có giá trị mặc định.

Các file đính kèm theo tài liệu này:

  • pptbai_giang_ky_thuat_lap_trinh_chuong_2_mot_so_van_de_trong_ky.ppt