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 để định nghĩa số phần tử mảng..
Trang 1Chương 10 Mảng, con trỏ và xâu ký tự
Học phần: LẬP TRÌNH CƠ BẢN
Trang 2Tài liệu tham khảo
2
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
Trang 3Nội dung
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ỏ
Trang 4PHẦN 1 MẢNG MỘT CHIỀU VÀ NHIỀU CHIỀU
4
Trang 6 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.
6
Trang 7Khai báo biến mảng (tường minh)
<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> ] ;
7
Trang 80 1 2
Khai báo biến mảng (tường minh)
Trang 9Số 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 để định nghĩa số phần tử mảng
Trang 10Khở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
Trang 11Khởi tạo giá trị cho mảng lúc khai báo
Trang 12 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];
12
Trang 13for(i=0; i<n; i++) {
printf(“\n Enter value: %d : ”, i+1);scanf(“%d”,&ary[i]);
}for(i=0; i<n; i++) printf(“a[%d]=%d\n“,i, ary[i]);
Trang 14for(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=0; i<n; i++)sum[i]=ary1[i]+ary2[i];
for(i=0; i<n; i++) printf(“a[%d]=%d\n“,i, sum[i]) }
Trang 18Khai 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>;
18
Trang 19Khai báo biến mảng 2 chiều
typedef int MaTran10x20 [10][20];
typedef int MaTran5x10 [5][10];
MaTran10x20 a, b;
MaTran11x11 c;
MaTran10x20 d;
19
Trang 20 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
20
Trang 23Ví 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)
23
// In cac phan tu cua mang
for (i=0; i<m; i++)
Trang 25Ví 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)
25
// 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++)
Trang 26Bà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
26
Trang 27Bà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?
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?
27
Trang 28Bà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 giới hạn
trước)
28
Trang 29PHẦN 2 CON TRỎ VÀ CÁC PHÉP
TOÁN
29
Trang 30 RAM 512MB được đánh địa chỉ từ 0 đến 2 29 – 1
RAM 2GB được đánh địa chỉ từ 0 đến 2 31 – 1
30
Trang 31 Quy trình xử lý của trình biên dịch
Dành riêng một vùng nhớ với địa chỉ duy nhất để lưu biến đó
Liên kết địa chỉ ô nhớ đó với tên biến
Khi gọi tên biến, nó sẽ truy xuất tự động đến ô nhớ đã liên kết với tên biến
Ví dụ: int a = 0x1234; // Giả sử địa chỉ 0x0B
Trang 32…
Khái niệm con trỏ
Khái niệm
Địa chỉ của biến là một con số.
Ta có thể tạo biến khác để lưu địa chỉ của biến này Con trỏ.
32
Trang 33Khai báo con trỏ
p1 là biến con trỏ, trỏ tới vùng nhớ kiểu int (4 bytes) còn p2
là biến kiểu int bình thường.
<kiểu dữ liệu> * <tên biến con trỏ>;
char * ch1, * ch2;
int * p1, p2;
33
Trang 34Khai báo con trỏ
Sử dụng từ khóa typedef
Ví dụ
Lưu ý khi khai báo kiểu dữ liệu mới
Giảm bối rối khi mới tiếp xúc với con trỏ
Nhưng dễ nhầm lẫn với biến thường
typedef <kiểu dữ liệu> * <tên kiểu con trỏ>;
<tên kiểu con trỏ> <tên biến con trỏ>;
typedef int *pint;
int *p1;
pint p2, p3;
34
Trang 35Con trỏ NULL
Khái niệm
Con trỏ NULL là con trỏ không trỏ và đâu cả.
Khác với con trỏ chưa được khởi tạo.
Trang 36Khởi tạo kiểu con trỏ
Khởi tạo
Khi mới khai báo, biến con trỏ được đặt ở địa chỉ nào đó
(không biết trước)
chứa giá trị không xác định
trỏ đến vùng nhớ không biết trước
Đặt địa chỉ của biến vào con trỏ (toán tử &)
Trang 37Sử dụng con trỏ
Truy xuất đến ô nhớ mà con trỏ trỏ đến
Con trỏ chứa một số nguyên chỉ địa chỉ
Vùng nhớ mà nó trỏ đến, sử dụng toán tử *
Ví dụ
int a = 5, *pa = & a;
printf(“%d\n”, pa); // Giá trị biến pa
printf(“%d\n”, *pa); // Giá trị vùng nhớ pa trỏ đến printf(“%d\n”, &pa); // Địa chỉ biến pa
05 00 00 00 00 00 00
37
Trang 38printf("The value of i is %d\n", i);
printf("The pointer ptr1 contains the value %d\n",*ptr1); }
Kết quả:
The value of i is 3
The pointer ptr1 contains the value 3
38
Trang 40Gán con trỏ
Có thể gán biến con trỏ:
int *p1, *p2;
p2 = p1;
– Gán một con trỏ cho con trỏ khác
– «Chỉ định p2 trỏ tới nơi mà p1 đang trỏ tới»
Dễ bị lẫn với:
*p2 = *p1;
– Gán “giá trị trỏ bởi p1” cho “giá trị trỏ bởi p2”
40
Trang 41Các phép toán với con trỏ
Phép so sánh hai con trỏ
Trên con trỏ tồn tại các phép so sánh (= =, !=, <, <=,
>,>=) hai con trỏ bằng nhau là hai con trỏ cùng trỏ tới một đối tượng (có giá trị bằng nhau), ngược lại là khác nhau Con trỏ trỏ tới vùng nhớ có địa chỉ nhỏ hơn là con trỏ nhỏ hơn và ngược lại.
41
Trang 42Các phép toán với con trỏ (tiếp)
Phép cộng, trừ con trỏ với số nguyên
Giả sử p là con trỏ kiểu T, k là số nguyên thì (p + k) cũng là con trỏ kiểu T, không mất tổng quát giả sử p trỏ tới phần tử t thì:
1 p+1 là con trỏ trỏ tới một phần tử kiểu T kế tiếp sau t và p+2 trỏ tới một phần tử kiểu T kế tiếp sau t 2 phần tử,
2 p -1 là con trỏ trỏ tới một phần tử kiểu T kế tiếp trước t
3 p -2 trỏ tới một phần tử kiểu T kế tiếp trước t hai phần tử,
4 tổng quát: p+k trỏ tới phần tử cách t một khoảng k phần tử kiểu T (nếu k >0 dịch về phía địa chỉ lớn, k<0 thì dịch về phía địa chỉ nhỏ).
42
Trang 43Các phép toán với con trỏ (tiếp)
Phép cộng, trừ con trỏ với số nguyên
Ví dụ:
int *p, *q;
giả sử p trỏ tới phần tử có địa chỉ 180, q trỏ tới phần tử có địa chỉ 160 thì
(p-q) = = 10; (kiểu int chiếm 4 byte bộ nhớ)
float *r1, *r2;
giả sử r1 trỏ tới phần tử có địa chỉ 120, r2 trỏ tới phần tử có địa chỉ 100 thì
(r1-r2) = = 5; (kiểu float chiếm 4 byte bộ nhớ).
Lưu ý: Đây là ANSI C, với C chạy trên Win32 có thể kích thước kiểu int, float sẽ lớn
hơn.
Ngoài các phép toán +, - với số nguyên, còn có thể áp dụng các phép toán ++, cùng các phép toán số học khác với con trỏ.
43
Trang 44Kích thước của con trỏ
Kích thước của con trỏ
Con trỏ chỉ lưu địa chỉ nên kích thước của mọi con trỏ là như nhau:
Môi trường MD-DOS (16 bit): 2 bytes
Môi trường Windows (32 bit): 4 bytes
Trang 45Con trỏ và mảng một chiều
(Tự đọc)
Mảng một chiều
Tên mảng array là một hằng con trỏ
không thể thay đổi giá trị của hằng này
array là địa chỉ đầu tiên của mảng
Trang 4646
Trang 47 Truy xuất đến phần tử thứ n của mảng (không sử dụng biến mảng)
Trang 50 Đối số mảng một chiều truyền cho hàm là địa chỉ
phần tử đầu tiên của mảng.
Con trỏ và mảng một chiều
50
Trang 51Cấp phát bộ nhớ
Hàm malloc() là một trong các hàm được sử dụng thường xuyên nhất để thực hiện việc cấp phát bộ nhớ từ vùng nhớ còn tự do
Tham số của hàm malloc() là một số nguyên xác định số bytes cần cấp phát
51
Trang 52for(i=0; i<n; i++) {
printf(“\n Enter value: %d : ”, i+1);scanf(“%d”,p+i);
Trang 53 ptr phải được dùng trước đó với lời gọi hàm
malloc(), calloc(), hoặc realloc()
53
Trang 54Hà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ụ
54
float *calloc1, *calloc2;
calloc1 = (float *)calloc(3,sizeof(float));
Trang 55Bài tập thảo luận trên lớp
Bài 1: Cho đoạn chương trình sau:
Trang 56Bài tập thảo luận trên lớp
Trang 57Câu hỏi ôn tập lý thuyết
1.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 trỏ đến phần
tử thứ 4 của mảng int p2 – p1 = ?
9.Giống như câu trên nhưng đối với mảng float?
57
Trang 58Bà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á trị, số nguyên tố
bé nhất
58
Trang 59PHẦN 3 XÂU KÝ TỰ
59
Trang 60Khá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ự
60
Trang 61char s[] = {‘T’, ‘H’, ‘C’, ‘S’, ‘ ’, ‘A’, ‘\0’}; char s[] = “THCS A”; // Tự động thêm ‘\0’
Trang 62Xuấ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”;
Trang 63Nhậ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 A
Chuoi nhan duoc la: Tin_
63
Trang 64Nhậ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 A
Chuoi nhan duoc la: Tin hoc co so A _
64
Trang 65Một số hàm thao tác trên xâu ký tự
Trang 66Hà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ự
strcpy(s, “Tin hoc co so A”); // đúng
char *strcpy(char dest[], const char src[])
char * strcpy (char dest [], const char src [])
66
Trang 67Hà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 [])
67
Trang 70Hà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!!!”;
Trang 71char 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 )
71
Trang 72char 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 )
72
Trang 73Hà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 )
73
Trang 74Hàm tính độ dài xâu ký tự
Tính độ dài xâu ký tự s
size_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 )
74
Trang 75Hà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ề nullchar 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 )
75
Trang 76Bài tập
1 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.
76
Trang 77Bài tập
8 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 đầu xâu viết hoa, các chữ còn lại viết thường,…)
77