Bài giảng Ngôn ngữ lập trình C - Mảng và con trỏ
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
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<y+n; p++) scanf("%d",p); return y; } void show(int *y, int n){ int *p; for (p=y; p<y+n; p++) printf("%5d",*p); } int ktnt(int k){ int i; for (i=2; i<k; i++) if (k%i==0) return 0; return k>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ả
File đính kèm:
- Bài giảng Ngôn ngữ lập trình C - Mảng và con trỏ.pdf