Bài giảng Ngôn ngữ lập trình C - Chương 4: Mảng và con trỏ - Ninh Thị Thanh Tâm

pdf 77 trang phuongnguyen 5660
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Ngôn ngữ lập trình C - Chương 4: Mảng và con trỏ - Ninh Thị Thanh Tâm", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfbai_giang_ngon_ngu_lap_trinh_c_chuong_4_mang_va_con_tro_ninh.pdf

Nội dung text: Bài giảng Ngôn ngữ lập trình C - Chương 4: Mảng và con trỏ - Ninh Thị Thanh Tâm

  1. NGÔN NGỮ LẬP TRÌNH C Mảng và con trỏ Ninh Thị Thanh Tâm Khoa CNTT – HV Quản lý Giáo dục
  2. Mục đích & Nội dung  Giới thiệu kiểu dữ liệu mảng  Biết cách sử dụng mảng (lưu, sắp xếp, tìm kiếm)  Khai báo một mảng, truy cập đến các thành phần của mảng  Mảng nhiều chiều  Giới thiệu khái niệm con trỏ  Biết cách sử dụng con trỏ trong lập trình  Quan hệ tương hỗ giữa con trỏ, mảng và xâu ký tự  Mảng các con trỏ, mảng các xâu
  3. Mảng  Khái niệm: Là một dãy liên tiếp các phần tử cùng kiểu trong bộ nhớ  Kích thước: Là số các phần tử trong mảng Phải được khai báo tường minh  Số chiều: Một chiều, hai chiều, C không giới hạn số chiều
  4. Mảng một chiều  Khai báo mảng  Truy cập vào các phần tử của mảng  Nhập dữ liệu cho biến mảng
  5. Khai báo mảng  Cú pháp: [size];  Ý nghĩa type là kiểu dữ liệu của các phần tử trong mảng name là tên của mảng size là số thành phần trong mảng (hằng số)  Ví dụ: int a[7]; char ch[20];
  6. Truy cập các phần tử mảng  Cú pháp: [index] Ví dụ: a[0], ch[10]  Chỉ số: Được đánh số từ 0 Có thể là hằng, biến, biểu thức Chỉ số có thể nhận giá trị nguyên hoặc thực  Mỗi phần tử của mảng được xem như một biến Ví dụ: a[2] = a[1]+1;
  7. Nhập dữ liệu cho biến mảng  Trực tiếp: Sử dụng hàm nhập scanf() để nhập giá trị cho phần tử cần nhập Ví dụ: scanf(“%d”,&a[i]);  Gián tiếp: Sử dụng một biến trung gian có cùng kiểu với kiểu các phần tử mảng  Nhập giá trị cho biến trung gian  Gán giá trị của biến cho phần tử cần nhập giá trị Ví dụ: scanf(“%d”,&temp); a[i] = temp;
  8. Ví dụ /*arr1.c*/ /*CT nhap va in day so*/ #include #include void main() { int n, i; int arr[100]; do { printf("Nhap so phan tu n="); scanf("%d",&n); } while (n 100); for (i=0; i<n; i++) { printf("Phan tu thu %d:",i); scanf("%d",&arr[i]); } for (i=0; i<n; i++) printf("%5d",arr[i]); getch(); }
  9. Kết quả
  10. Mảng hai chiều  Khai báo mảng  Truy cập vào các phần tử của mảng  Nhập dữ liệu cho biến mảng
  11. Khai báo mảng  Cú pháp: [size1] [size2];  Ý nghĩa  type là kiểu dữ liệu của các phần tử trong mảng  name là tên của mảng  size1, size2 là số thành phần mỗi chiều (hằng số)  Ví dụ: int matrix[5][10]; float b[20][20];
  12. Truy cập các phần tử mảng  Cú pháp: [row][column] Ví dụ: matrix[0][0], b[5][10]  Mỗi phần tử của mảng được xem như một biến Ví dụ: matrix[0][0] = matrix[0][0]+10;
  13. Nhập dữ liệu cho biến mảng  Trực tiếp: Sử dụng hàm nhập scanf() để nhập giá trị cho phần tử cần nhập Ví dụ: scanf(“%d”,&matrix[i][j]);  Gián tiếp: Sử dụng một biến trung gian có cùng kiểu với kiểu các phần tử mảng  Nhập giá trị cho biến trung gian  Gán giá trị của biến cho phần tử cần nhập giá trị Ví dụ: scanf(“%d”,&temp); b[i][j] = temp;
  14. Ví dụ /*matrix2.c*/ #include #include void main() { float matrix[3][4]; int i,j; clrscr(); for (i=0; i<3; i++) for (j=0; j<4; j++) matrix[i][j] = i+j; for (i=0; i<3; i++) { for (j=0; j<4; j++) printf("%5.1f",matrix[i][j]); printf("\n"); } getch(); }
  15. Kết quả
  16. Xâu kí tự  Khai báo xâu kí tự  Vào ra với xâu kí tự Sử dụng printf() và scanf() Sử dụng puts() và gets()  Hàm thao tác trên xâu
  17. Khai báo xâu kí tự  Cú pháp: char [size]; Ví dụ: char str[10];  Ý nghĩa: name là tên của xâu kí tự size là kích thước thực của xâu kí tự + 1  So sánh với mảng kí tự Giống: khai báo Khác: trong xâu kí tự có kí tự kết thúc (NULL hay ‘\0’)
  18. str[0] ‘a’  Một biến xâu kí tự muốn str[1] ‘b’ có chiều dài n phải được khai báo như mảng kí tự str[2] ‘c’ có n+1 phần tử str[3] ‘d’  ‘\0’ là kí tự đánh dấu kết str[4] ‘e’ thúc xâu str[5] ‘f’  So sánh ‘A’ và “A”  ‘A’ là kí tự A được mã hóa str[6] ‘g’ bằng 1 byte str[7] ‘h’  “A” là xâu kí tự chứa kí tự A và kí tự ‘\0’, được mã str[8] ‘\0’ hóa bằng 2 byte str[9]
  19. Vào ra với xâu kí tự (1)  Sử dụng hàm print() printf(“%s”, ); Ví dụ: printf(“%s”,str);  Hàm scanf() scanf(“%s”, ); Ví dụ: scanf(“%s”,str); Sử dụng chỉ thị fflush(stdin) nếu đi sau câu lệnh nhập khác
  20. Ví dụ /*scanf4.c*/ #include #include void main() { int n; double d; char c; char str[20]; clrscr(); printf("Nhap gia tri cho cac bien\n"); scanf("%d%lf",&n,&d); fflush(stdin); scanf("%c",&c); scanf("%s",str); printf("So int %d\n",n); printf("So double %lf\n",d); printf("Ki tu %c\n",c); printf("Xau ki tu %s\n",str); getch(); }
  21. Kết quả
  22. Vào ra với xâu kí tự (2)  Sử dụng hàm puts()  puts( );  In nội dung xâu kí tự ra màn hình và thay thế kí hiệu kết thúc xâu ‘\0’ bằng kí hiệu xuống dòng  Ví dụ: puts(str);  Hàm gets()  gets( );  Đọc từ bàn phím tất cả các kí tự và điền vào xâu  Kết thúc khi số kí tự đọc bằng chiều dài của xâu hoặc gặp kí tự xuống dòng  Sử dụng chỉ thị fflush(stdin) nếu gets() đi sau câu lệnh nhập khác  Ví dụ: gets(str);
  23. Ví dụ /*gets.c*/ #include #include void main() { char str[81]; clrscr(); printf("Nhap vao dong ki tu:"); gets(str); printf("Xau vua nhap:\n"); puts(str); getch(); }
  24. Kết quả
  25. Khai báo và khởi đầu giá trị  Mảng một chiều int arr1[7] = {1, -1, 3, 4, 5, 6, 7};  Mảng hai chiều int arr2[3][4] = { {-2, 3, 4, 5}, {6, 7, 8, 9}, {0, -1, 2, 3}};  Xâu kí tự Cách 1: char str[6] = "abc"; Cách 2: char str[6] = {'a','b','c'};
  26. Lưu ý  Không cần mô tả đủ danh sách giá trị Gán giá trị cho những phần tử đầu Phần tử còn lại lấy giá trị ngẫu nhiên  Không cần mô tả kích thước của biến mảng Chương trình dịch tự động xác định kích thước dựa vào danh sách giá trị mô tả kèm theo Ví dụ: t[ ] = {1, 2, 3}; /*khai báo mảng KT 3*/
  27. Hàm thao tác trên xâu  Chứa trong tệp tiêu đề: string.h  strlen Cú pháp: strlen( ) Trả về độ dài xâu  strcpy Cú pháp: strcpy(w,s); Sao chép xâu s vào trong xâu w  strcmp Cú pháp: strcmp(w,s); So sánh 2 xâu có giống nhau hay không
  28. Con trỏ  Khái niệm và cách khai báo  Toán tử & và *  Các phép toán trên con trỏ Phép gán trên hai con trỏ cùng kiểu Phép cộng con trỏ với số nguyên Phép trừ hai con trỏ cùng kiểu  Con trỏ void* Kiểu dữ liệu void Con trỏ kiểu void*  Con trỏ đa cấp
  29. Khái niệm  Con trỏ là một biến/hằng có giá trị (nội dung) là địa chỉ của một đối tượng khác  Đối tượng: biến, hàm  Cho phép tham chiếu đến biến thông qua địa chỉ
  30. Khai báo  Cú pháp: *ptr_name;  type là kiểu dữ liệu của biến mà con trỏ chứa địa chỉ  ptr_name là tên của biến trỏ  type* là một kiểu dữ liệu con trỏ  NULL là hằng số, giá trị của con trỏ khi chưa được gán địa chỉ
  31. Ví dụ int *pi, *qi; float *pf, *qf; char *pc, *qc;
  32. Lưu ý  Kích thước của biến con trỏ phụ thuộc cách biểu diễn địa chỉ bộ nhớ Thường sử dụng 2 thanh ghi 16bit ~ 4 byte  Mỗi kiểu dữ liệu có một kiểu biến con trỏ riêng biệt Con trỏ int* chứa địa chỉ các biến nguyên Không thể gán giá trị một con trỏ nguyên cho con trỏ thực mà không thực hiện phép chuyển kiểu
  33. Toán tử & và *  Toán tử &: cho ta địa chỉ của một biến, biến con trỏ có giá trị là địa chỉ của một biến  Ví dụ: int m, n, p, *pi, *qi; pi = &n; Gán địa chỉ của biến nguyên n cho con trỏ nguyên pi pi “chỉ đến” n
  34. Toán tử & và * (tiếp)  Toán tử * pi “chỉ đến” n, có 2 cách mô tả giá trị của n  Sử dụng tên của biến n  Sử dụng toán tử * lên biến trỏ pi *pi ~ n  Ví dụ: *pi = 3; /*Gán giá trị 3 cho n*/ *pi = *pi + 5; /*thêm 5 vào n*/  Toán tử & và * có độ ưu tiên cao hơn toán tử số học
  35. Các phép toán trên con trỏ  Phép gán trên hai con trỏ cùng kiểu  Phép cộng con trỏ với số nguyên  Phép trừ hai con trỏ cùng kiểu
  36. Gán hai con trỏ cùng kiểu  Ta có thể Gán địa chỉ một biến nguyên cho một biến trỏ nguyên Gán hai con trỏ cùng kiểu với nhau  Ví dụ: int n, *pi, *qi; pi = &n; qi = pi; Cho phép pi và qi cùng trỏ đến biến n *pi, *qi và n là một
  37. Gán hai con trỏ cùng kiểu (tiếp)  Ví dụ: int n, *pi; float *pf;  Câu lệnh không hợp lệ pf = &n; pf = pi;  Phép chuyển đổi kiểu (thực hiện trước phép gán) là hợp lệ pf = (float *)&n; pf = (float *)pi;
  38. Cộng con trỏ với số nguyên  Cộng một con trỏ với một số nguyên cho ta một con trỏ cùng kiểu  Ví dụ: int m, n, p; pi = &n;  Khi đó m, n, p tương ứng với 3 vùng nhớ 2 bytes liên tiếp nhau  pi trỏ đến n  pi+1 cho ta địa chỉ của số nguyên m đi ngay trước n  pi-1 cho ta địa chỉ của số nguyên p đi ngay sau n
  39. Ví dụ /*pointer2.c*/ #include #include void main() { int m=2, n=100, p=20; int *pi = &n; clrscr(); printf("m = %d n = %d p = %d\n",m,n,p); printf("&n = %p &m = %p &p = %p\n",&n,&m,&p); printf("pi = %p pi+1 = %p pi-1 = %p\n", pi, pi+1, pi-1); printf("*pi = %d\n", *pi); printf("*(pi+1) = %d\n", *(pi+1)); printf("*(pi-1) = %d\n", *(pi-1)); getch(); }
  40. Kết quả
  41. Phép trừ hai con trỏ cùng kiểu  Cho kết quả là một số nguyên biểu thị “khoảng cách” giữa hai biến con trỏ  Ví dụ int m, n, *pi, *qi; pi = &n; qi = &m; qi – pi == 1  Không có phép cộng, nhân, chia giữa hai con trỏ (không có ý nghĩa thực tế)
  42. Con trỏ void*  Kiểu dữ liệu void Là kiểu dữ liệu rỗng Một biến kiểu void không tương ứng với bất kỳ vùng nhớ nào  Con trỏ kiểu void* Có thể nhận địa chỉ của bất kỳ vùng nhớ nào Không thể thực hiện các phép tính số học trên con trỏ void*
  43. Con trỏ void* (tiếp)  Ví dụ void *px, *py; int x = 1; float y = 0.1; px = &x; py = &y;
  44. Con trỏ đa cấp  Bản thân biến trỏ cũng có địa chỉ Có thể chứa địa chỉ của nó trong một đối tượng khác  Những đối tượng có giá trị là địa chỉ của một biến con trỏ là con trỏ đa cấp  Sử dụng thêm một số dấu ‘*’ Số lượng dấu ‘*’ xác định cấp của con trỏ
  45. Con trỏ đa cấp (tiếp)  Ví dụ: int *pi; int ppi = π
  46. Liên hệ giữa con trỏ và mảng  Con trỏ và mảng một chiều  Con trỏ đa cấp và mảng nhiều chiều  Con trỏ và xâu kí tự
  47. Con trỏ và mảng một chiều  Xét câu lệnh: int a[10]; Xin cấp phát một vùng nhớ 10 số nguyên a[0], a[1], ,a[9] có địa chỉ xác định bởi a a là địa chỉ của biến nguyên a[0] Nếu pi là một con trỏ nguyên thì pi = a là hợp lệ  Sau câu lệnh: pi = a; có thể Sử dụng pi như một tên mảng Thực hiện các phép toán số học trên con trỏ a
  48. Con trỏ và mảng một chiều (tiếp)  pi[0], , pi[9] tương ứng là a[0], , a[9]  a, a+1, ,a+9 tương ứng là địa chỉ của a[0], a[1], , a[9]  Có mối liên hệ mật thiết giữa việc đánh chỉ số trên mảng và các tính toán trên con trỏ Khác  Tên mảng là hằng số  Biến trỏ là biến Không thể sử dụng câu lệnh: int b[10]; b = a;
  49. Ví dụ  Nhập một dãy số nguyên vào bộ nhớ động Xét xem trong dãy có ít nhất 2 số nguyên tố bằng nhau hay không? Tìm số nguyên tố max của dãy nếu có Chương trình nguồn trong tệp array1.c
  50. #include #include #include int *init(int n){ int *p, *y; y = (float *)calloc(n, sizeof(int)); for (p=y; p 1; }
  51. void caua(int *y, int n){ int *p, *q; for (p=y; p<y+n-1; p++) for (q=p+1; q<y+n; q++) if (ktnt(*p)&& *p==*q){ printf("Co 2 so nguyen to bang nhau\n"); return; } printf("Khong co 2 so nguyen to bang nhau\n"); } void caub(int *y, int n){ int *p, max; max = 0; for (p=y; p<y+n; p++) if(ktnt(*p)&&(max<*p)) max = *p; if (!max) printf("Khong co so nguyen to nao\n"); else printf("So NT lon nhat la %d\n",max); }
  52. int main(){ int *x, n; printf("Nhap n="); scanf("%d",&n); x = init(n); caua(x,n); caub(x,n); getch(); return 0; }
  53. Con trỏ đa cấp và mảng nhiều chiều  Mảng hai chiều là mảng một chiều của các mảng một chiều  Con trỏ hai cấp là con trỏ chỉ đến con trỏ một cấp Con trỏ hai cấp và mảng hai chiều có nhiều điểm tương đồng Có thể suy rộng cho các mảng và con trỏ nhiều mức
  54. Con trỏ đa cấp và mảng nhiều chiều (tiếp)  Ví dụ int ppi; int a2[10][10];  Ta có thể thực hiện phép gán: ppi=a2; Có thể sử dụng ppi như tên một biến mảng
  55. Ví dụ  Nhập dữ liệu của một ma trận vào bộ nhớ động, in ma trận vừa nhập
  56. Ví dụ - nhập in ma trận /*array21.c*/ #include #include float *init(int n, int m){ float *p, *x; x = (float *)calloc(n*m,sizeof(float)); for (p=x; p<x+n*m; p++) scanf("%f",p); return x; } void show(float *x, int n, int m){ int i, j; for (i=0; i<n; i++){ for (j=0; j<m; j++) printf("%5.2f",x[i*m+j]); puts(""); } }
  57. void main(){ float *a; int n, m; printf("Nhap n, m="); scanf("%d%d",&n,&m); a = init(n,m); show(a,n,m); getch(); }
  58. Con trỏ và xâu kí tự  Xây dựng lại các hàm so sánh sao chép xâu kiểm tra tính đối xứng của xâu  Nhập một xâu kí tự Các ô trống kề nhau chỉ giữ lại một.
  59. Ví dụ - so sánh hai xâu kí tự /*strcmp.c*/ #include #include void init(char *w); void show(char *w); int sosanh(char *w,char *s); void main(){ char *w, *s; clrscr(); init(w); init(s); show(w); show(s); if (sosanh(w,s)) printf("Hai xau khac nhau\n"); else printf("Hai xau giong nhau\n"); getch(); }
  60. void init(char *w){ printf("Nhap xau:"); gets(w); } void show(char *w){ printf("Xau = %s\n",w); } int sosanh(char *w, char *s){ char *p, *q; p = w; for(q = s; *p||*q; p++, q++) if (*p != *q) return (*p - *q); return (*p - *q); }
  61. Ví dụ - sao chép xâu /*strcpy.c*/ #include #include void init(char *w); void show(char *w); void saochep(char *w,char *s); void main(){ char *w, *s; clrscr(); init(w); /* show(w);*/ saochep(s,w); show(s); getch(); }
  62. void init(char *w){ printf("Nhap xau:"); gets(w); } void show(char *w){ printf("Xau = %s\n",w); } void saochep(char *w, char *s) { char *p, *q; p = w; for(q = s; *q; q++, p++) *p = *q; *p = 0; }
  63. Ví dụ - Kiểm tra xâu đối xứng /*strsym.c*/ #include #include void init(char *w); void show(char *w); int ktdx(char *w); void main(){ char *w; clrscr(); init(w); if (ktdx(w)) printf("Xau doi xung\n"); else printf("Xau khong doi xung\n"); getch(); }
  64. void show(char *w){ printf("Xau = %s\n",w); } int ktdx(char *w) { char *q; q = w + strlen(w) - 1; while (w <q) { if (*w != *q) return 0; w ++; q ; } return 1; }
  65. Ví dụ - các ô trống kề nhau chỉ giữ lại một /*xoakt.c*/ #include #include #include #define tr ' ' /*void init(char *w){ printf("Nhap xau ="); gets(w); } */ char *init(){ char *w; printf("Nhap xau"); gets(w); return w; } void show(char *w){ printf("%s\n",w); }
  66. void del(char *p){ for(; *p; p++) *p = *(p+1); } void del_space(char *w){ char *p; for (p=w; *p; ) if ((*p==tr)&&*(p+1)==tr) del(p); else p++; } int main(){ char *w; // init(w); w = init(); show(w); del_space(w); show(w); getch(); return 0; }
  67. Cấp phát động  Cho phép chương trình sử dụng vừa đúng khối lượng bộ nhớ chương trình cần Khi không cần dùng có thể giải phóng cho các công việc khác Cùng một vùng nhớ có thể được sử dụng cho các mục đích khác nhau trong thời gian thực hiện chương trình
  68. Hàm cấp phát bộ nhớ  Hàm malloc()  Hàm calloc()  Hàm free()
  69. Hàm malloc()  Tệp tiêu đề liên quan:  alloc.h  stdlib.h  Cú pháp: malloc(size);  Xin cấp phát một vùng nhớ có kích thước size byte từ vùng nhớ heap  Giá trị trả về  Thành công: trả về một con trỏ tới khối nhớ mới được cung cấp  Lỗi: trả về giá trị NULL  Nếu size = 0, malloc() trả về con trỏ NULL
  70. Ví dụ int *pi, *qi; char *pc; double *pd; double ppd; int i; /*cấp phát vùng nhớ một số nguyên*/ qi = (int *)malloc(sizeof(int)); /*cấp phát một mảng các số nguyên – mảng động*/ pi = (int *)malloc(12*sizeof(int)); /*cấp phát xâu ký tự*/ pc = (char *)malloc(20); pd = (double *)malloc(10*sizeof(double)); /*cấp phát ma trận*/ ppd = (double )malloc(10*sizeof(double *)); for (i=0; i<10; i++) ppd[i] = (double *)malloc(10*sizeof(double));
  71. Hàm calloc()  Tệp tiêu đề liên quan  alloc.h  stdlib.h  Cú pháp: calloc(nitems, size);  Xin cấp phát một vùng nhớ kích thước nitems*size byte và xóa trắng vùng nhớ này  Muốn xin cấp phát vùng nhớ >64KB, phải sử dụng hàm faralloc (cấp phát xa)  Giá trị trả về  Thành công: trả về con trỏ trỏ tới vùng nhớ mới được cấp  Lỗi: hàm trả về NULL
  72. Ví dụ int *pi; double *pd; double ppd; int i; /*cấp phát một mảng các số nguyên – mảng động*/ pi = (int *)calloc(12,sizeof(int)); pd = (double *)calloc(10, sizeof(double)); /*cấp phát ma trận*/ ppd = (double )calloc(10, sizeof(double*)); for (i=0; i<10; i++) ppd[i] = (double *)calloc(10, sizeof(double));
  73. Hàm free()  Tệp tiêu đề liên quan: alloc.h stdlib.h  Cú pháp: free(address); Giải phóng một khối nhớ đã được cấp phát trước đó bằng hàm calloc() hoặc malloc() hoặc realloc() Địa chỉ vùng nhớ được giải phóng được truyền làm tham số cho hàm free()
  74. Ví dụ free(pi); free(qi); free(pc); free(pd); for (i=1; i<10; i++) free(ppd[i]); free(ppd);
  75. Ví dụ /*38.c*/ #include "stdio.h" #include "conio.h" #include "stdlib.h" #include "alloc.h" void main() { double p; int m, n; int i, j; double temp; clrscr(); printf("Kich thuoc ma tran"); scanf("%d%d",&m,&n); p = (double *)calloc(m,sizeof(double *)); for (i=0; i<m; i++) p[i] = (double *)calloc(n, sizeof(double));
  76. Ví dụ for (i=0; i<m; i++) for (j=0; j<n; j++) { printf("p[%d,%d]=",i,j); scanf("%lf", &temp); p[i][j] = temp; } for (i=0; i<m; i++) { for (j=0; j<n; j++) printf("%6.2f", p[i][j]); printf("\n"); } for (i=0; i<m; i++) free(p[i]); free(p); getch(); }
  77. Kết quả