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
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:
- bai_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
- 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
- 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
- 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
- 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
- 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];
- 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;
- 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;
- 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(); }
- Kết quả
- 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
- 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];
- 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;
- 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;
- 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(); }
- Kết quả
- 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
- 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’)
- 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]
- 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
- 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(); }
- Kết quả
- 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);
- 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(); }
- Kết quả
- 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'};
- 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*/
- 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
- 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
- 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ỉ
- 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ỉ
- Ví dụ int *pi, *qi; float *pf, *qf; char *pc, *qc;
- 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
- 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
- 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
- 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
- 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
- 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;
- 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
- 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(); }
- Kết quả
- 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ế)
- 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*
- Con trỏ void* (tiếp) Ví dụ void *px, *py; int x = 1; float y = 0.1; px = &x; py = &y;
- 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ỏ
- Con trỏ đa cấp (tiếp) Ví dụ: int *pi; int ppi = π
- 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ự
- 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
- 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;
- 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
- #include #include #include int *init(int n){ int *p, *y; y = (float *)calloc(n, sizeof(int)); for (p=y; p 1; }
- 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); }
- int main(){ int *x, n; printf("Nhap n="); scanf("%d",&n); x = init(n); caua(x,n); caub(x,n); getch(); return 0; }
- 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
- 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
- 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
- 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(""); } }
- 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(); }
- 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.
- 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(); }
- 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); }
- 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(); }
- 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; }
- 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(); }
- 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; }
- 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); }
- 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; }
- 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
- Hàm cấp phát bộ nhớ Hàm malloc() Hàm calloc() Hàm free()
- 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
- 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));
- 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
- 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));
- 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()
- Ví dụ free(pi); free(qi); free(pc); free(pd); for (i=1; i<10; i++) free(ppd[i]); free(ppd);
- 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));
- 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(); }
- Kết quả