Upload
others
View
5
Download
0
Embed Size (px)
Citation preview
Lập trình cơ bản:Ngôn ngữ lập trình C
Đỗ Thị Mai HườngBộ môn Hệ thống thông tinKhoa 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áovà không bao giờ thay đổi.
– NNLT C luôn chỉ định một khối nhớ liên tụccho một biến kiểu mảng.
7
Khai báo biến mảng (tường minh)
• Tường minh
– <N1>, …, <Nn> : số lượng phần tử của mỗi chiều.
• Lưu ý– Phải xác định <số phần tử> cụ thể (hằng) khi khai
báo.
– Mảng nhiều chiều: <tổng số phần tử> = N1*N2*…*Nn
– Bộ nhớ sử dụng = <tổng số phần tử>*sizeof(<kiểu cơ
<kiểu cơ sở> <tên biến mảng>[<số phần tử>];<kiểu cơ sở> <tên biến mảng>[<N1>][<N2>]…[<Nn>];
8
0
1
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 <kiểu cơ sở> <tên kiểu mảng>[<số phần tử>];typedef <kiểu cơ sở> <tên kiểu mảng>[<N1>]…[<Nn>];
<tên kiểu mảng> <tên biến mảng>;
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 20int 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};
29122912 17061706 15061506 19041904
0 1 2 3
a
int a[4] = {2912, 1706};
29122912 17061706 00 00
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};
00 00 00 00
0 1 2 3
a
int a[] = {2912, 1706, 1506, 1904};
29122912 17061706 15061506 19041904
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!
<tên biến mảng>[<gt cs1>][<gt cs2>]…[<gt csn>]
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 3typedef int MangSo[MAX];MangSo a = {1, 2, 3}, b;
b = a; // Saifor (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 <stdio.h>#define MAX 1000void 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 <stdio.h>#define MAX 1000void 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ận0
…
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-120
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)
<kiểu cơ sở> <tên biến>[<N1>][<N2>];
typedef <kiểu cơ sở> <tên kiểu>[<N1>][<N2>];
<tên kiểu> <tên biến>;<tên kiểu> <tên biến 1>, <tên biến 2>;
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]
<tên biến mảng>[<giá trị cs1>][<giá trị cs2>]
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; // Saiint 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 <stdio.h>#define MAX 1000void main(){int a[MAX][MAX];int i,n,m;//Nhap mangprintf(“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 mangfor (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 <stdio.h>#define MAX 1000void main(){
int a[MAX][MAX], b[MAX][MAX], c[MAX][MAX];int i,n,m;//Nhap 2 mangprintf(“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 Cfor (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 Cfor (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ớp1. 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ện29
Bài tập5. 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ập10.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
KhaiKhai bbááoo bibiếếnn con con trtrỏỏ..
33
• Cú pháp: <Kiểu dữ liệu> * <Tên con trỏ>
• Ý 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ểuint.
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.
CCáác thao tc thao táác trên con trc 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: <Tên biến con trỏ>=&<Tên biến> • 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ả
CCáác thao tc thao táác trên con trc trên con trỏỏ (t)(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
CCáác thao tc thao táác trên con trc 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:
*<Tên biến con trỏ>
• Với cách truy cập này thì *<Tên biến con trỏ> 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)
VVíí ddụụvvềề
con con trtrỏỏ
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 cuabien 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 trotoi=%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 moicua bien b=%d “ ,a,b); /* a, b thay doi theo*/getch();
}
Con Con trtrỏỏ vvàà mmảảngng mmộộtt chichiềềuu
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 Con trtrỏỏ trtrỏỏ ttớớii ccáácc phphầầnn ttửử ccủủaa mmảảngng mmộộtchichiềềuu::
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)
VVíí ddụụ::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];
VVíí ddụụ::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);
}
VVíí ddụụ::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 Con trtrỏỏ vvàà mmảảngng nhinhiềềuu chichiềềuu
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ầntử a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]
Địachỉ 1 2 3 4 5 6
Con Con trtrỏỏ vvàà mmảảngng nhinhiềềuu chichiềềuu (t)(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]
VVíí ddụụ::Dùn
g con trỏđể vào sốliệu cho
mảng haichiề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);
KiKiểểuu con con trtrỏỏ, , kikiểểuu đđịịaa chchỉỉ, , ccáácc phphéépp totoáánn trêntrên con con trtrỏỏ
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ệ.
VVíí ddụụvvềề ssửửddụụngngcon con trtrỏỏ
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();
}
CCáác phc phéép top toáán trên con trn 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áccon 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]
CCáác phc phéép top toáán trên con trn trên con trỏỏ (t)(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.
VVíí ddụụ
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 trCon trỏỏ kikiểểu voidu 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.
MMảảng con trng 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:
<Kiểu dữ liệu> *<Tên_mảng_con_trỏ>[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ỉ.
CCấấp php pháát bt bộộ nhnhớớ cho bicho biếến con trn 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*/
GiGiảảii phphóóngng vvùùngng nhnhớớ do do bibiếếnn con con trtrỏỏ ququảảnnlýlý
54
• Một vùng nhớ đã cấp phát cho biến con trỏ, khi không cầnsử dụng nữa, ta sẽ thu hồi lại vùng nhớ này nhờ hàmfree().
• 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óngvù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ử dụng con trỏ
55
#include <stdio.h>#include <malloc.h>void main(){int *p,n,i,j,temp;printf(“Nhap n = ”);scanf(“%d”,&n);p = (int *)malloc(n*sizeof(int));for(i=0; i<n; i++) {
printf(“\n Enter value: %d : ”, i+1);scanf(“%d”,p+i);
}for(i=1; i<10; i++) printf(“%d\n“,i, *(p+i))
}
1.2.3.4.5.6.7.8.9.10.11.12.13.
Hàm free()• Hàm free() được sử dụng để giải phóng bộ
nhớ khi nó không cần dùng nữa.
• Cú pháp:
void free(void*ptr);
• Hàm này giải phóng không gian được trỏ bởi
ptr, để dùng cho tương lai.
• ptr phải được dùng trước đó với lời gọi hàm malloc(), calloc(), hoặc realloc(). 56
Hàm calloc() • calloc tương tự như malloc, nhưng điểm khác biệt
chính là mặc nhiên giá trị 0 được lưu vào không gian bộnhớ vừa cấp phát.
• calloc yêu cầu hai tham số
Tham số thứ nhất là số lượng các biến cần cấp phát bộnhớ
Tham số thứ hai là kích thước của mỗi biến
• Cú pháp:void *calloc( size_t num, size_t size );
• Ví dụ
57
float *calloc1, *calloc2;calloc1 = (float *)calloc(3,sizeof(float));
Bài tập thảo luận trên lớp• Bài 1: Cho đoạn chương trình sau:
float pay;
float *ptr_pay;
pay=2313.54;
ptr_pay = &pay;
• Hãy cho biết giá trị của:a. pay
b. *ptr_pay
c. *pay
d. &pay58
Bài tập thảo luận trên lớp• Bài 2: Tìm lỗi
#include<stdio.h> #include<conio.h>
void main() {
int *x, y = 2;
*x = y;*x += y++;
printf("%d %d",*x,y);getch();
}
59
Câu hỏi ôn tập lý thuyết1.Toán tử nào dùng để xác định địa chỉ của một
biến?2.Toán tử nào dùng để xác định giá trị của biến do
con trỏ trỏ đến?3.Phép lấy giá trị gián tiếp là gì?4.Các phần tử trong mảng được sắp xếp trong bộ nhớ
như thế nào?5.Cho mảng một chiều data. Trình bày 2 cách lấy địa
chỉ phần tử đầu tiên của mảng này.6.Nếu ta truyền cho hàm đối số là mảng một chiều.
Trình bày hai cách nhận biết phần tử cuối của mảng?
7.Trình bày 6 phép toán có thể thực hiện trên con trỏ?
8.Cho con trỏ p1 trỏ đến phần tử thứ 3 còn con trỏ p2
60
Bài tập về nhàSử dụng con trỏ viết cac chương trình:
1.Nhập vào một dãy số thực, tìm dãy con tăng cónhiều phần tử nhất.
2.Nhập vào một dãy và kiểm tra xem dãy đã cho là tăng hay không, nếu không hãy sắp xếp lại dãy theo chiều tăng dần
3.Nhập vào một dãy số nguyên, kiểm tra xem dãy là dãy giảm hay không? Nếu không hãy sắp xếp lại dãy
4.Nhập dãy số nguyên dương. Xét xem trong dãy có số nguyên tố hay không? Nếu có, hãy in ra giá
61
PHẦN 3. XÂU KÝ TỰ
62
Khái niệm• Khái niệm
– Kiểu char chỉ chứa được một ký tự. Để lưu trữmột xâu ký tự (nhiều ký tự) ta sử dụng mảng (một chiều) các ký tự.
– Xâu ký tự kết thúc bằng ký tự ‘\0’ (null)
Độ dài xâu ký tự = kích thước mảng – 1
• Ví dụchar hoten[30]; // Dài 29 ký tựchar ngaysinh[9]; // Dài 8 ký tự
63
Khởi tạo• Khởi tạo như mảng thông thường
– Độ dài cụ thể
– Tự xác định độ dài
char s[10] = {‘T’, ‘H’, ‘C’, ‘S’, ‘A’, ‘ ’, ‘\0’};char s[10] = “THCS A”; // Tự động thêm ‘\0’
char s[] = {‘T’, ‘H’, ‘C’, ‘S’, ‘ ’, ‘A’, ‘\0’};char s[] = “THCS A”; // Tự động thêm ‘\0’
‘T’‘T’ ‘H’‘H’ ‘C’‘C’ ‘S’‘S’ ‘ ’‘ ’ ‘A’‘A’0 1 2 3 4 5 6 7 8 9
‘T’‘T’ ‘H’‘H’ ‘C’‘C’ ‘S’‘S’ ‘ ’‘ ’ ‘A’‘A’0 1 2 3 4 5
‘\0’‘\0’
‘\0’‘\0’6
64
Xuất xâu ký tự• Sử dụng hàm printf với đặc tả “%s”
• Sử dụng hàm puts
char monhoc[50] = “Tin hoc co so A”;printf(“%s”, monhoc); // Không xuống dòng
char monhoc[50] = “Tin hoc co so A”;puts(monhoc); // Tự động xuống dòng printf(“%s\n”, monhoc);
Tin hoc co so A
Tin hoc co so A
_
_
65
Nhập xâu ký tự• Sử dụng hàm scanf với đặc tả “%s”
– Chỉ nhận các ký tự từ bàn phím đến khi gặp ký tự
khoảng trắng hoặc ký tự xuống dòng.
– Xâu nhận được không bao gồm ký tự khoảng
trắng và xuống dòng.char monhoc[50];printf(“Nhap mot chuoi: “);scanf(“%s”, monhoc);printf(“Chuoi nhan duoc la: %s”, monhoc);
Nhap mot chuoi: Tin hoc co so AChuoi nhan duoc la: Tin_
66
Nhập xâu ký tự• Sử dụng hàm gets
– Nhận các ký tự từ bàn phím đến khi gặp ký tự
xuống dòng.
– Xâu nhận được là những gì người dùng nhập
(trừ ký tự xuống dòng).char monhoc[50];printf(“Nhap mot chuoi: “);gets(monhoc);printf(“Chuoi nhan duoc la: %s”, monhoc);
Nhap mot chuoi: Tin hoc co so AChuoi nhan duoc la: Tin hoc co so A _
67
Một số hàm thao tác trên xâu ký tự
• Thuộc thư viện <string.h>
– strcpy
– strdup
– strlwr/strupr
– strrev
– strcmp/stricmp
– strcat
– strlen
– strstr68
Hàm sao chép xâu ký tự
Sao chép xâu ký tự src sang xâu ký tự dest, dừng khi ký tự kết thúc xâu ký tự ‘\0’ vừa được chép.! dest phải đủ lớn để chứa src
Địa chỉ xâu ký tự dest
char s[100];s = “Tin hoc co so A”; // saistrcpy(s, “Tin hoc co so A”); // đúng
char *strcpy(char dest[], const char src[])char *strcpy(char dest[], const char src[])
69
Hàm tạo bản sao
Tạo bản sao của một xâu ký tự s cho trước. Hàm sẽ tự tạo vùng nhớ đủ chứa xâu ký tựs.
Thành công: Địa chỉ xâu ký tự kết quảThất bài: null
char *s;s = strdup(“Tin hoc co so A”);
char *strdup(const char s[])char *strdup(const char s[])
70
Hàm chuyển xâu ký tự thành chữ thường
Chuyển xâu ký tự s thành xâu ký tự thường (‘A’ thành ‘a’, ‘B’ thành ‘b’, …, ‘Z’ thành ‘z’)
Địa chỉ xâu ký tự s
char s[] = “Tin hoc co so A!!!”;strlwr(s);puts(s); // tin hoc co so a!!!
char *strlwr(char *s)char *strlwr(char *s)
71
Hàm chuyển xâu ký tự thành chữ IN
Chuyển xâu ký tự s thành xâu ký tự in (‘a’thành ‘A’, ‘b’ thành ‘B’, …, ‘z’ thành ‘Z’)
Địa chỉ xâu ký tự s
char s[] = “Tin hoc co so A!!!”;strupr(s);puts(s); // TIN HOC CO SO A!!!
char *strupr(char *s)char *strupr(char *s)
72
Hàm đảo ngược xâu ký tự
Đảo ngược thứ tự các ký tự trong xâu ký tự(trừ ký tự kết thúc xâu ký tự)
Địa chỉ xâu ký tự kết quả
char s[] = “Tin hoc co so A!!!”;strrev(s);puts(s); // !!!A os oc coh niT
char *strrev(char *s)char *strrev(char *s)
73
Hàm so sánh hai xâu ký tự
So sánh hai xâu ký tự s1 và s2 (phân biệt hoa thường)
< 0 nếu s1 < s2== 0 nếu s1 == s2>0 nếu s1 > s2
char s1[] = “tin hoc co so A!!!”;char s2[] = “hoc tin co so A!!!”;int kq = strcmp(s1, s2); // => kq > 0
int strcmp(const char *s1, const char *s2)int strcmp(const char *s1, const char *s2)
74
Hàm so sánh hai xâu ký tự
So sánh hai xâu ký tự s1 và s2 (không phân biệt hoa thường)
< 0 nếu s1 < s2== 0 nếu s1 == s2>0 nếu s1 > s2
char s1[] = “tin hoc co so A!!!”;char s2[] = “TIN HOC CO SO A!!!”;int kq = stricmp(s1, s2);// => kq == 0
int stricmp(const char *s1, const char *s2)int stricmp(const char *s1, const char *s2)
75
Hàm nối hai xâu ký tự
Nối xâu ký tự src vào sau xâu ký tự dest.! Xâu dest phải đủ chứa kết quả
Địa chỉ của xâu ký tự được nối
char s1[100] = “Tin hoc”;char s2[] = “co so A!!!”;strcat(s1, “ ”); // => “Tin hoc ”strcat(s1, s2); // => “Tin hoc co so A!!!”
char* strcat(char *dest, const char *src)char* strcat(char *dest, const char *src)
76
Hàm tính độ dài xâu ký tự
Tính độ dài xâu ký tự ssize_t thay cho unsigned (trong <stddef.h>) dùng để đo các đại lượng không dấu.
Độ dài xâu ký tự s
char s[] = “Tin hoc co so A!!!”;int len = strlen(s); // => 18
size_t* strlen(const char *s)size_t* strlen(const char *s)
77
Hàm tìm xâu ký tự trong xâu ký tự
Tìm vị trí xuất hiện đầu tiên của s2 trong s1
Thành công: trả về con trỏ đến vị trí xuất hiện đầu tiên của s2 trong s1.
Thất bại: trả về null
char s1[] = “Tin hoc co so A!!!”;char s2[] = “hoc”;if (strstr(s1, s2) != null)
printf(“Tim thay!”);
char* strstr(const char *s1, const char *s2)char* strstr(const char *s1, const char *s2)
78
Bài tập1. Xem thêm một số hàm khác như
atoi, atol, atof : đổi xâu ký tự thành số
itoa, ltoa, ultoa: đổi số thành xâu ký tự
strtok
2. Xóa tất cả các khoảng trắng của xâu ký tự s
3. Đếm xem có bao nhiêu từ trong xâu s. Xuất các từ trên các dòng liên tiếp.
4. Tìm từ có chiều dài dài nhất và in ra.
5. Trích ra n ký tự đầu tiên/cuối cùng/bắt đầu tại vị trí pos.
6. Tìm kiếm và thay thế xâu con trong xâu ký tự lớn
7. Viết chương trình nhập vào một xâu ký tự bất kỳ và xoá k ký tự của xâu ký tự bắt đầu từ vị trí thứ n.79
Bài tập8. Nhập xâu họ tên (không quá 40 kí tự), chuẩn
hoá xâu đó (kí tự đầu từ viết hoa, các kí tự
khác viết thường, các từ cách nhau 1 dấu
cách)
9. Nhập 3 xâu s1, s2, s3 (không quá 40 kí tự),
thay xâu s2 bằng s3 trong s1
10.Nhập xâu kí tự. Đưa xâu đó về dạng chuẩn
(các từ cách nhau bởi 01 dấu cách, chữ cái 80