Bài giảng Lập trình cơ bản: Ngôn ngữ lập trình C - Đỗ Thị Mai Hường

Dữ liệu kiểu mảng • Khái niệm – Là một kiểu dữ liệu có cấu trúc do người lập trình định nghĩa. – Biểu diễn một dãy các biến có cùng kiểu. Ví dụ: dãy các số nguyên, dãy các ký tự – Kích thước được xác định ngay khi khai báo và không bao giờ thay đổi. – NNLT C luôn chỉ định một khối nhớ liên tục cho một biến kiểu mảng. 7Khai báo biến mảng (tường minh) • Tường minh – , , : số lượng phần tử của mỗi chiều. • Lưu ý – Phải xác định cụ thể (hằng) khi khai báo. – Mảng nhiều chiều: = N1*N2* *Nn – Bộ nhớ sử dụng = *sizeof(

pdf80 trang | Chia sẻ: thanhle95 | Lượt xem: 407 | Lượt tải: 1download
Bạn đang xem trước 20 trang tài liệu Bài giảng Lập trình cơ bản: Ngôn ngữ lập trình C - Đỗ Thị Mai Hường, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Lập trình cơ bản: Ngôn ngữ lập trình C Đỗ Thị Mai Hường Bộ môn Hệ thống thông tin Khoa Công nghệ thông tin Mảng, con trỏ và xâu ký tự Tài liệu tham khảo Chương 7 - Phần 13 • Kỹ thuật lập trình C: cơ sở và nâng cao, Phạm Văn Ất, Nhà xuất bản KHKT – Chương 6 • The C programming language 2nd Edition, Brian Kernighan and Dennis Ritchie, Prentice Hall Software Series – Chương 4 • The C programming language 2nd Edition, Brian Kernighan and Dennis Ritchie, Prentice Hall Software Series – Chương 5 Nội dung Chương 7 - Phần 1 • Mảng một chiều • Mảng hai chiều • Con trỏ và phép toán trên con trỏ – Khai báo con trỏ – Phép gán con trỏ – Truy xuất giá trị qua con trỏ • Con trỏ và mảng • Cấp phát vùng nhớ cho con trỏ • Xâu ký tự – Khái niệm – Khởi tạo – Các thao tác trên xâu ký tự 4 PHẦN 1. MẢNG MỘT CHIỀU VÀ NHIỀU CHIỀU 5 Mảng một chiều • Khái niệm • Khai báo • Truy xuất dữ liệu 6 Dữ liệu kiểu mảng • Khái niệm – Là một kiểu dữ liệu có cấu trúc do người lập trình định nghĩa. – Biểu diễn một dãy các biến có cùng kiểu. Ví dụ: dãy các số nguyên, dãy các ký tự – Kích thước được xác định ngay khi khai báo và không bao giờ thay đổi. – NNLT C luôn chỉ định một khối nhớ liên tục cho một biến kiểu mảng. 7 Khai báo biến mảng (tường minh) • Tường minh – , , : số lượng phần tử của mỗi chiều. • Lưu ý – Phải xác định cụ thể (hằng) khi khai báo. – Mảng nhiều chiều: = N1*N2**Nn – Bộ nhớ sử dụng = *sizeof(<kiểu cơ []; [][][]; 8 01 2 Khai báo biến mảng (tường minh) • Ví dụint Mang1Chieu[10]; 0 1 2 3 4 7 85 6 9 Mang1Chieu int Mang2Chieu[3][4]; 0 1 2 3 4 7 85 6 9 Mang2Chieu 10 11 9 Khai báo biến mảng (không tường minh) • Cú pháp – Không tường minh (thông qua khai báo kiểu) • Ví dụ Chương 7 - Phần 1 typedef []; typedef [][]; ; typedef int Mang1Chieu[10]; typedef int Mang2Chieu[3][4]; Mang1Chieu m1, m2, m3; Mang2Chieu m4, m5; 10 Số phần tử của mảng • Phải xác định cụ thể số phần tử ngay lúc khai báo, không được sử dụng biến hoặc hằng thường • Nên sử dụng chỉ thị tiền xử lý #define để int n1 = 10; int a[n1]; const int n2 = 20; int b[n2]; #define n1 10 #define n2 20 int a[n1]; //  int a[10]; int b[n1][n2]; //  int b[10][20]; 11 Khởi tạo giá trị cho mảng lúc khai báo • Gồm các cách sau – Khởi tạo giá trị cho mọi phần tử của mảng – Khởi tạo giá trị cho một số phần tử đầu mảng int a[4] = {2912, 1706, 1506, 1904}; 2912 1706 1506 1904 0 1 2 3 a int a[4] = {2912, 1706}; 2912 1706 0 0 0 1 2 3 a 12 Khởi tạo giá trị cho mảng lúc khai báo • Gồm các cách sau – Khởi tạo giá trị 0 cho mọi phần tử của mảng – Tự động xác định số lượng phần tử int a[4] = {0}; 0 0 0 0 0 1 2 3 a int a[] = {2912, 1706, 1506, 1904}; 2912 1706 1506 1904 0 1 2 3 a 13 Truy xuất đến một phần tử • Thông qua chỉ số • Ví dụ – Cho mảng như sau – Các truy xuất • Hợp lệ: a[0], a[1], a[2], a[3] • Không hợp lệ: a[-1], a[4], a[5], => Cho kết thường không như mong muốn! [][][] int a[4]; 0 1 2 3 14 Gán dữ liệu kiểu mảng • Không được sử dụng phép gán thông thường mà phải gán trực tiếp giữa các phần tử tương ứng • Ví dụ Chương 7 - Phần 1 #define MAX 3 typedef int MangSo[MAX]; MangSo a = {1, 2, 3}, b; b = a; // Sai for (int i = 0; i < 3; i++) b[i] = a[i]; 15 Ví dụ • Nhập mảng có n phần tử kiểu nguyên, in ra các phần tử của mảng 16 #include #define MAX 1000 void main() { int ary[MAX]; int i,n; printf(“Nhap n = ”); scanf(“%d”,&n); for(i=0; i<n; i++) { printf(“\n Enter value: %d : ”, i+1); scanf(“%d”,&ary[i]); } for(i=1; i<10; i++) printf(“a[%d]=%d\n“,i, ary[i]) } 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. Ví dụ • Nhập 2 mảng có n phần tử kiểu nguyên, tính và in ra mảng tổng 17 #include #define MAX 1000 void main() { int ary1[MAX], ary2[MAX], sum[MAX]; int i,n; printf(“Nhap n = ”);scanf(“%d”,&n); for(i=0; i<n; i++) { printf(“\n Enter value: %d : ”, i+1); scanf(“%d”,&ary1[i]); } for(i=0; i<n; i++) { printf(“\n Enter value: %d : ”, i+1); scanf(“%d”,&ary2[i]); } for(i=1; i<10; i++)sum[i]=ary1[i]+ary2[i]; for(i=1; i<10; i++) printf(“a[%d]=%d\n“,i, sum[i]) } 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. Mảng hai chiều • Khái niệm • Khai báo • Truy xuất dữ liệu 18 Ma Trận 0 m-1 0 1 n-1 Am,n 0 n-1 An 0 n-1 19 Ma Trận 0 n-1 An 0 n-1 0 n-1 0 n-1 0 n-1 0 n-1 dòng = cột dòng > cột dòng < cột 0 n-1 An 0 n-1 0 n-1 0 n-1 0 n-1 0 n-1 dòng + cột = n-1 dòng + cột > n-1 dòng + cột < n-1 20 Khai báo biến mảng 2 chiều • Cú pháp – Tường minh – Không tường minh (thông qua kiểu) [][]; typedef [][]; ; , ; 21 Khai báo biến mảng 2 chiều • Ví dụ – Tường minh – Không tường minh (thông qua kiểu) int a[10][20], b[10][20]; int c[5][10]; int d[10][20]; typedef int MaTran10x20[10][20]; typedef int MaTran5x10[5][10]; MaTran10x20 a, b; MaTran11x11 c; MaTran10x20 d; 22 Truy xuất đến một phần tử • Thông qua chỉ số • Ví dụ – Cho mảng 2 chiều như sau – Các truy xuất • Hợp lệ: a[0][0], a[0][1], , a[2][2], a[2][3] • Không hợp lệ: a[-1][0], a[2][4], a[3][3] [][] int a[3][4]; 0 1 2 0 1 2 3 23 Gán dữ liệu kiểu mảng • Không được sử dụng phép gán thông thường mà phải gán trực tiếp giữa các phần tử • Ví dụ int a[5][10], b[5][10]; b = a; // Sai int i, j; for (i = 0; i < 5; i++) for (j = 0; j < 10; j++) b[i][j] = a[i][j]; 24 Ví dụ • Nhập mảng có n dòng, m cột các phần tử kiểu nguyên, in các phần tử của mảng ra màn hình 25 #include #define MAX 1000 void main() { int a[MAX][MAX]; int i,n,m; //Nhap mang printf(“Nhap n = ”);scanf(“%d”,&n); printf(“Nhap m = ”);scanf(“%d”,&m); for (i=0; i<m; i++) for (j=0; j<n; j++) { printf(“Nhap a[%d][%d]: ”, i, j); scanf(“%d”, &a[i][j]); } 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. Ví dụ • Nhập mảng có n dòng, m cột các phần tử kiểu nguyên, in các phần tử của mảng ra màn hình (tiếp) 26 // In cac phan tu cua mang for (i=0; i<m; i++) { for (j=0; j<n; j++)printf(“%d ”, a[i][j]); printf(“\n”);} } 16. 17. 18. 19. 20. 21. Ví dụ • Nhập 2 mảng A, B có n dòng, m cột các phần tử kiểu nguyên, tính và in các phần tử của mảng C = A + B 27 #include #define MAX 1000 void main(){ int a[MAX][MAX], b[MAX][MAX], c[MAX][MAX];int i,n,m; //Nhap 2 mang printf(“Nhap n = ”);scanf(“%d”,&n); printf(“Nhap m = ”);scanf(“%d”,&m); for (i=0; i<m; i++) for (j=0; j<n; j++){ printf(“Nhap a[%d][%d]: ”, i, j); scanf(“%d”, &a[i][j]); } for (i=0; i<m; i++) for (j=0; j<n; j++){ printf(“Nhap a[%d][%d]: ”, i, j); scanf(“%d”, &b[i][j]); } 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. Ví dụ • Nhập 2 mảng A, B có n, m cột các phần tử kiểu nguyên, tính và in các phần tử của mảng C = A + B (tiếp) 28 // Tinh cac phan tu cua mang C for (i=0; i<m; i++) for (j=0; j<n; j++) c[i][j] = a[i][j] + b[i][j]; // In cac phan tu cua mang C for (i=0; i<m; i++) { for (j=0; j<n; j++)printf(“%d ”, c[i][j]); printf(“\n”);} } 16. 17. 18. 19. 20. 21. 22. 23. 24. Bài tập thảo luận trên lớp 1. Nhập mảng có n phần tử kiểu nguyên, tìm phần tử lớn nhất, nhỏ nhất của mảng. 2. Nhập mảng có n dòng, m cột phần tử kiểu nguyên, tìm phần tử lớn nhất, nhỏ nhất của mảng 3. Nhập mảng có n phần tử kiểu nguyên, nhập giá trị x, tìm xem x có trong mảng không, xác định vị trí xuất hiện đầu tiên. 4. Nhập mảng có n dòng, m cột phần tử kiểu nguyên, nhập giá trị x, tìm xem x có xuất hiện trong mảng không, xác định các vị trí xuất hiện 29 Bài tập 5. Nhập 2 vector có n phần tử kiểu nguyên, kiểm tra 2 vector đó có vuông góc với nhau không? 6. Sắp xếp mảng một chiều bằng các phương pháp • Phương pháp chọn • Phương pháp nổi bọt 7. Nhập vào một dãy, tìm phần tử lớn nhất, nhỏ nhất và các vị trí có phần tử đạt giá trị lớn nhất, nhỏ nhất. 8. Tính và in ra tích hai ma trận vuông cấp n x n. 9. Nhập ma trận A (n x m) và kiểm tra xem có hai cột đứng cạnh nhau có tổng bằng nhau hay không?30 Bài tập 10.Nhập 2 mảng A(n,m), B(m,n) phần tử kiểu số thực, tính và in mảng C=A*B 11.Nhập 2 mảng A(n,m), B(m,n) phần tử kiểu số thực, kiểm tra A có là chuyển vị của B hay không 12.Nhập A(n,n) với n không giới hạn trước, kiểm tra A có là ma trận đơn vị không? 13.Xây dụng ma trận A(n,m), sao cho các phần tử có giá trị theo dạng xoắn ốc (n, m không 31 PHẦN 2. CON TRỎ VÀ CÁC PHÉP TOÁN 32 Khai báo biến con trỏ. 33 • Cú pháp: * • Ý nghĩa: Khai báo một biến có tên là Tên con trỏ dùng để chứa địa chỉ của các biến có kiểu Kiểu dữ liệu. • Ví dụ 1: Khai báo 2 biến a,b có kiểu int và 2 biến pa, pb là 2 biến con trỏ kiểu int. int a, b, *pa, *pb; • Ví dụ 2: Khai báo biến f kiểu float và biến pf là con trỏ float float f, *pf; • Chú ý: Nếu chưa muốn khai báo kiểu dữ liệu mà con trỏ ptr đang chỉ đến, ta sử dụng: void *ptr; Sau đó, nếu ta muốn con trỏ ptr chỉ đến kiểu dữ liệu gì cũng được. Tác dụng của khai báo này là chỉ dành ra 2 bytes trong bộ nhớ để cấp phát cho biến con trỏ ptr. Các thao tác trên con trỏ . 34 Gán địa chỉ của biến cho biến con trỏ: • Toán tử & dùng để định vị con trỏ đến địa chỉ của một biến đang làm việc. • Cú pháp: =& • Giải thích: Ta gán địa chỉ của biến Tên biến cho con trỏ Tên biến con trỏ. Ví dụ: Gán địa chỉ của biến a cho con trỏ pa, gán địa chỉ của biến b cho con trỏ pb. pa=&a; pb=&b; • Lúc này, hình ảnh của các biến trong bộ nhớ được mô tả Các thao tác trên con trỏ (t) 35 Lưu ý: • Khi gán địa chỉ của biến tĩnh cho con trỏ cần phải lưu ý kiểu dữ liệu của chúng. • Ví dụ sau đây không đúng do không tương thích kiểu: int Bien_Nguyen; float *Con_Tro_Thuc; ... Con_Tro_Thuc=&Bien_Nguyen; • Phép gán ở đây là sai vì Con_Tro_Thuc là một con trỏ kiểu float (nó chỉ có thể chứa được địa chỉ của biến kiểu float); trong khi đó, Bien_Nguyen có kiểu int Các thao tác trên con trỏ . 36 Nội dung của ô nhớ con trỏ chỉ tới. • Để truy cập đến nội dung của ô nhớ mà con trỏ chỉ tới, ta sử dụng cú pháp: * • Với cách truy cập này thì * có thể coi là một biến có kiểu được mô tả trong phần khai báo biến con trỏ. Ví dụ: Ví dụ sau đây cho phép khai báo, gán địa chỉ cũng như lấy nội dung vùng nhớ của biến con trỏ: int x=100; int *ptr; ptr=&x; int y= *ptr; • Lưu ý: Khi gán địa chỉ của một biến cho một biến con trỏ, mọi sự thay đổi trên nội dung ô nhớ con trỏ chỉ tới sẽ làm giá trị của biến thay đổi theo (thực chất nội dung ô nhớ và biến chỉ là một) Ví dụ về con trỏ 37 #include “stdio.h” #include “conio.h” void main() { int a,b,*pa,*pb; a=2; b=8; printf("\nGia tri cua bien a=%d \nGia tri cua bien b=%d ",a,b); pa=&a; pb=&b; printf("\nNoi dung cua o nho con tro pa tro toi=%d",*pa); printf("\nNoi dung cua o nho con tro pb tro toi=%d ",*pb); *pa=20; /* thay doi gia tri cua *pa*/ *pb=25; /* thay doi gia tri cua *pb*/ printf("\nGia tri moi cua bien a=%d \nGia tri moi cua bien b=%d “ ,a,b); /* a, b thay doi theo*/ getch(); } Con trỏ và mảng một chiều 38 • Trong C có mối quan hệ chặt chẽ giữa con trỏ và mảng: các phần tử của mảng có thể được xác định nhờ chỉ số hoặc thông qua con trỏ. Phép toán lấy địa chỉ: • Giả sử ta có khai báo: double b[20];  phép toán: &b[9] sẽ cho địa chỉ của phần tử b[9]. Tên mảng là một hằng địa chỉ: • Khi chúng ta khai báo: float a[10]; máy sẽ bố trí bố trí cho mảng a mười khoảng nhớ liên tiếp. • Mỗi khoảng nhớ là 4 byte. • Như vậy, nếu biết địa chỉ của một phần tử nào đó của mảng a, thì ta có thể dễ dàng suy ra địa chỉ của các phần tử khác của mảng. • Trong C ta có: a  &a[0] a+i  &a[i] *(a+i)  a[i] Con trỏ trỏ tới các phần tử của mảng một chiều: 39 • Khi con trỏ pa trỏ tới phần tử a[k] của mảng a thì:  pa+i trỏ tới phần tử thứ i sau a[k], có nghĩa là nó trỏ tới a[k+i].  pa-i trỏ tới phần tử thứ i trước a[k], có nghĩa là nó trỏ tới a[k-i].  *(pa+i) tương đương với pa[i]. • Ví dụ: sau hai câu lệnh: float a[20],*pa; pa=&a; // pa=&a[0]; • Khi đó, 4 cách viết sau có tác dụng như nhau và cùng truy cập đến phần tử thứ i của mảng a: a[i] *(a+i) pa[i] *(pa+i) Ví dụ: Vào số liệu cho các phần tử của một mảng và tính tổng các phần tử của chúng. 40 Cách 1: #include "stdio.h" void main() { float a[4],tong; int i; for (i=0;i<4;++i) { printf("\n a[%d]=",i); scanf("%f",(a+i)); } tong=0; for (i=0;i<4;++i) tong+=a[i]; Ví dụ: Vào số liệu cho các phần tử của một mảng và tính tổng các phần tử của chúng. 41 Cách 2: #include "stdio.h" void main() { float a[4],tong, *troa; int i; troa=a; for (i=0;i<4;++i) { printf("\n a[%d]=",i); scanf("%f",troa+i); } tong=0; for (i=0;i<4;++i) tong+=a[i]; printf("\n Tong cac phan tu mang la:%8.2f ",tong); } Ví dụ: Vào số liệu cho các phần tử của một mảng và tính tổng các phần tử của chúng. 42 Cách 3: #include "stdio.h" void main() { float a[4],tong,*troa; int i; troa=a; for (i=0;i<4;++i) { printf("\n a[%d]=",i); scanf("%f",troa+i); } tong=0; for (i=0;i<4;++i) tong+=*(troa+i); printf("\n Tong cac phan tu mang la:%8.2f ",tong); } Con trỏ và mảng nhiều chiều 43 Phép cộng địa chỉ trong mảng hai chiều: • Giả sử ta có mảng hai chiều a[2][3] có 6 phần tử ứng với sáu địa chỉ liên tiếp trong bộ nhớ được xếp theo thứ tự sau: • Tên mảng a biểu thị địa chỉ đầu tiên của mảng. • Phép cộng địa chỉ : C coi mảng hai chiều là mảng (một chiều) của mảng, như vậy khai báo float a[2][3]; thì a là mảng mà mỗi phần tử của nó là một dãy 3 số thực (một hàng của mảng). • Vì vậy: a trỏ phần tử thứ nhất của mảng: phần tử a[0][0] a+1 trỏ phần tử đầu hàng thứ hai của mảng: phần tử a[0][1], ... a+i trỏ phần tử đầu hàng thứ i của mảng: phần tử a[0][i]. Phần tử a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]Địa chỉ 1 2 3 4 5 6 Con trỏ và mảng nhiều chiều (t) 44 Con trỏ và mảng hai chiều:Để lần lượt duyệt trên các phần tử của mảng hai chiều ta có thể dùng con trỏ như minh hoạ ở ví dụ sau: float *pa,a[2][3]; pa=(float*)a; Khi đó: – pa trỏ tới a[0][0] – pa+1 trỏ tới a[0][1] – pa+2 trỏ tới a[0][2] – pa+3 trỏ tới a[1][0] – pa+4 trỏ tới a[1][1] – pa+5 trỏ tới a[1][2] Ví dụ: Dùn g con trỏ để vào số liệu cho mảng hai chiều 45 Cách 1: #include "stdio.h" void main() { float a[2][3],*pa; int i; pa=(float*)a; for (i=0;i<6;++i) scanf("%f",pa+i); } Cách 2: #include "stdio.h" void main() { float a[2][3],*pa; int i; for (i=0;i<6;++i) scanf("%f",(float*)a+i); Kiểu con trỏ, kiểu địa chỉ, các phép toán trên con trỏ 46 Kiểu con trỏ và kiểu địa chỉ: • Con trỏ dùng để lưu địa chỉ của biến. Mỗi kiểu địa chỉ của biến cần có kiểu con trỏ tương ứng. Phép gán địa chỉ cho con trỏ chỉ có thể thực hiện được khi kiểu địa chỉ phù hợp với kiểu con trỏ. • Theo khai báo: float a[20][30], *pa,*pn[30],(*pm)[30]; • Ta có: • a là mảng 2 chiều, có 600 phần tử kiểu float. a là địa chỉ kiểu float[30]. • pa là con trỏ float. • pn là mảng 30 con trỏ kiểu float. • pm là con trỏ kiểu float [30]. • Như vậy, phép gán: pa=a; là không hợp lệ (tuy nhiên sẽ có quá trình chuyển kiểu tự động). • Nhưng phép gán: pm=a; là hợp lệ. Ví dụ về sử dụng con trỏ 47 #include "conio.h" #include "stdio.h" void main() { float a[2][3]={{1.0,2.0,3.0},{4.0,5.0,6.0}}, *p, (*pm3)[3]; float *b; int i; p=(float *)a; pm3=a; for (i=0;i<6;i++) printf("%f", *(p+i)); printf("\n"); b=(float*)pm3; for (i=0;i<3;i++) printf("%f", b[i]); printf("\n"); b=(float*)(pm3+1); for (i=0;i<3;i++) printf("%f", b[i]); getch(); } Các phép toán trên con trỏ 48 Có 4 phép toán liên quan đến con trỏ và đại chỉ là:  Phép gán.  Phép tăng giảm địa chỉ.  Phép truy cập bộ nhớ.  Phép so sánh. • Phép gán: Phép gán chỉ thực hiện với các con trỏ cùng kiểu. Muốn gán các con trỏ khác kiểu phải dùng phép ép kiểu. int x; char *pc; pc=(char*)(&x); • Phép tăng giảm địa chỉ: Sử dụng phép toán này để di chuyển giữa các ô nhớ: • Ví dụ: Các câu lệnh: float x[30],*px; px=&x[10]; • Cho con trỏ px là con trỏ float trỏ tới phần tử x[10]. Ta có: px+i trỏ tới phần tử x[10+i] px-i trỏ tới phần tử x[10-i] Các phép toán trên con trỏ (t) 49 • Phép truy cập bộ nhớ: Con trỏ float trỏ tới địa chỉ dài 4 byte, con trỏ int trỏ tới địa chỉ dài 2 byte, con trỏ char trỏ tới địa chỉ dài 1 byte. • Ví dụ: khai báo: float *pf; int *pi; char *pc; • Khi đó ta có các nhận xét sau:  Nếu trỏ pf trỏ đến byte thứ 100 thì *pf biểu thị vùng nhớ 4 byte liên tiếp từ byte 100 đến 103.  Nếu trỏ pi trỏ đến byte thứ 100 thì *pi biểu thị vùng nhớ 2 byte liên tiếp từ byte 100 đến 101.  Nếu trỏ pc trỏ đến byte thứ 100 thì *pc biểu thị vùng nhớ 1 byte chính là byte 100. • Phép so sánh: Cho phép so sánh các con trỏ cùng kiểu. • Ví dụ nếu p1 và p2 là các con trỏ cùng kiểu thì nếu:  p1<p2 nếu địa chỉ p1 trỏ tới thấp hơn địa chỉ p2 trỏ tới.  p1==p2 nếu địa chỉ p1 trỏ tới cũng là địa chỉ p2 trỏ tới.  p1>p2 nếu địa chỉ p1 trỏ tới cao hơn địa chỉ p2 trỏ tới. Ví dụ 50 Ví dụ 1: Đoạn chương trình tính tổng các số thực dùng phép so sánh con trỏ: float a[100],*p,*pcuoi,tong=0.0; pcuoi=(float*)(a+99); /* Địa chỉ cuối dãy*/ for (p=(float*)a;p<=pcuoi;++p) tong+=*p; Ví dụ 2: Dùng con trỏ char để tách các byte của một biến nguyên, ta làm như sau: Giả sử ta có biến nguyên n được khai báo như sau: unsigned int n=0xABCD; /* Số nguyên hệ 16*/ char *pc; pc=(char*)(&n); Khi đó: *pc=0xAB (byte thứ nhất của n) *(pc+1)=0xCD (byte thứ hai của n) Con trỏ kiểu void 51 • Cú pháp: void *tên_con_trỏ; • Đây là con trỏ đặc biệt, con trỏ không kiểu, nó có thể nhận địa chỉ kiểu bất kỳ. • Ví dụ: Câu lệnh sau là hợp lệ: void *pa; float a[20][30]; pa=a; • Con trỏ void thường dùng làm tham số hình thức để nhận bất kỳ địa chỉ kiểu nào từ tham số thực. Trong thân hàm phải dùng phép chuyển đổi kiểu để chuyển sang dạng địa chỉ cần xử lý. • Chú ý: Các phép toán tăng giảm địa chỉ, so sánh và truy cập bộ nhớ không dùng được trên con trỏ void. Mảng con trỏ 52 • Mảng con trỏ: là một mảng mà mỗi phần tử của nó là một con trỏ. • Cú pháp: *[N]; • Khi gặp khai báo trên, máy sẽ cấp phát N khoảng nhớ liên tiếp cho N phần tử của mảng. • Ví dụ: double *pa[100]; Khai báo một mảng con trỏ kiểu double gồm 100 phần tử. Mỗi phần tử pa[i] có thể dùng để lưu trữ một địa chỉ kiểu double. • Chú ý : • Bản thân các mảng con trỏ không dùng để lưu trữ số liệu. • Trước khi sử dụng một mảng con trỏ ta cần gán cho mỗi phần tử của nó một địa chỉ. Cấp phát bộ nhớ cho biến con trỏ 53 • Trước khi sử dụng biến con trỏ, ta phải cấp phát vùng nhớ cho biến con trỏ này quản lý địa chỉ. Việc cấp phát được thực hiện nhờ các hàm malloc(), calloc(), realloc() trong thư viện alloc.h, hoặc stdlib.h. • Cú pháp các hàm: void *malloc(size_t size); Cấp phát vùng nhớ có kích thước là size byte. void *calloc(size_t nitems, size_t size); Cấp phát vùng nhớ có kích thước là nitems*size byte. void *realloc( void *memblock, size_t size ); Cấp phát lại ô nhớ cho con trỏ memblock size byte. • Ví dụ: Giả sử ta có khai báo: int a, *pa, *pb; pa = (int*)malloc(sizeof(int)); /* Cấp phát vùng nhớ có kích thước bằng với kích thước của một số nguyên */ pb= (int*)calloc(10, sizeof(int)); /* Cấp phát vùng nhớ có thể chứa được 10 số nguyên*/ pa= (int*) realloc(pa, 10*sizeof(int)); /* Cấp phát lại vùng nhớ có thể chứa được 10 số nguyên*/ Giải phóng vùng nhớ do biến con trỏ quản lý 54 • Một vùng nhớ đã cấp phát cho biến con trỏ, khi không cần sử dụng nữa, ta sẽ thu hồi lại vùng nhớ này nhờ hàm free(). • Cú pháp: void free(void *block) • Ý nghĩa: Giải phóng vùng nhớ được quản lý bởi con trỏ block. • Ví dụ: Ở ví dụ trên, sau khi thực hiện xong, ta giải phóng vùng nhớ cho 2 biến con trỏ pa & pb: free(pa); free(pb); Ví dụ Cấp phát động mảng 1 chiều, nhập và in mảng sử
Tài liệu liên quan