Professional Documents
Culture Documents
Lập trình C - Việt Nhật 2007 3 Lập trình C - Việt Nhật 2007 4
1
Thuật toán - Algorithm Thuật giải tốt
Tập các lệnh được tổ chức có thứ tự nhằm giải quyết một bài Một thuật giải tốt là thuật giải:
toán hoặc đạt đến một mục tiêu nào đó. chính xác
Ví dụ: rõ ràng
hướng dẫn chế biến một món ăn, đúng
hướng dẫn sửa chữa xe máy, hiệu quả
cách giải một bài toán. và có thể bảo trì được.
…
Algorithm –Thuật toán - Thuật giải Chúng ta có thể viết một thuật giải cho máy tính bằng ngôn
ngữ bình thường nhưng có thể không rõ ràng. Thay vào đó,
chúng ta sẽ dùng ngôn ngữ lập trình (hoặc một ngôn ngữ giả
lập ngôn ngữ lập trình gọi là mã giả pseudocode)
Lập trình C - Việt Nhật 2007 5 Lập trình C - Việt Nhật 2007 6
2
Ngôn ngữ C - Lịch sử phát triển Ngôn ngữ C – Đặc điểm chính
C được ra đời tại Bell Lab vào 1972, cha đẻ là Dennis Ritchie. Tính hiệu quả cao, đặc biệt là trong lập trình hệ thống
Cho phép truy nhập trực tiếp đến từng bit, byte, các cổng dữ liệu.
C và mối liên hệ với hệ điều hành Unix
Linh hoạt và mạnh
Kernighan và Ritchie đưa ra phiên bản C chuẩn đầu tiên năm Bản thân ngôn ngữ không đặt ra giới hạn đối với người lập trình
1978. Các ứng dụng phát triển từ C rất đa dạng.
Nằm giữa lớp ngôn ngữ lập trình bậc cao và bậc thấp
ANSI C - C được chuẩn hóa vào năm 1989 bởi American Tính khả chuyển cao
National Standards Institute. Một chương trình C viết trên một hệ thống máy tính này, có thể
dịch và chạy trên một hệ thống khác mà không hay ít phải sửa
đổi.
Lập trình C - Việt Nhật 2007 9 Lập trình C - Việt Nhật 2007 10
3
Giới thiệu chung về Linux Hệ thống file trên Linux
Linux là phiên bản phát triển có mã nguồn mở của hệ điều Trong Linux, các tập tin được
hành Unix sắp xếp theo một dạng tương tự
cấu trúc cây. Thư mục gốc là /
Linux ban đầu được tạo ra bởi Linus Toward, năm 1991
Trên góc độ kỹ thuật, tên gọi Linux chỉ tương ứng với nhân Linux phân biệt chữ hoa, chữ
của hệ điều hành (kernel), cung cấp các dịch vụ truy nhập thường đối với tên file
quản lý tài nguyên phần cứng cho người sử dụng và các Mỗi người sử dụng khi được
chương trình ứng dụng tạo tài khoản sẽ có một thư mục,
Các bản phân phối Linux: RedHat, SuSE, Debian, Mandrake. gọi là thư mục home. Ký hiệu ~.
Linux kế thừa các ưu điểm của dòng hệ điều hành Unix: môi Thông thường người dùng
trường đa người dùng, đa nhiệm, chạy trên nhiều platform userid sẽ có thư mục home là
khác nhau. /home/userid
Linux có thể chạy tốt trên nhưng hệ thống cấu hình rất thấp
nhưng cũng chạy ổn định trên cả các máy chủ.
Lập trình C - Việt Nhật 2007 13 Lập trình C - Việt Nhật 2007 14
Lập trình C - Việt Nhật 2007 15 Lập trình C - Việt Nhật 2007 16
4
Các lệnh cơ bản trong emacs Các lệnh trong emacs (tiếp)
Quy ước: Di chuyển con trỏ theo từ:
viết "C-x " là bấm nút Ctrl và phím "x", viết M-x có nghĩa là bấm phím Về sau: M-f. Về trước M-b
Meta(phím Alt hoặc Escape ) và phím "x"
Viết C-x-h nghĩa là bấm Ctrl và x sau đó bấm h. Theo câu: Chú ý emacs phân biệt câu với dòng.
Trở về trạng thái chờ lệnh: C-g Về đầu : M-a, về cuối câu: M-e.
Thao tác với file: Theo bộ đệm: Về đầu: M-v. Về cuối: C-v
Tìm và mở file: C-x C-f tên_file. Có thể sử dụng phím Tab để xem danh
sách tập tin. Thao tác soạn thảo, cắt dán
Lưu nội dung bộ đệm vào file: C-x C-s Xóa ký tự: Del hoặc C-d
Lưu với tên khác: C-x C-w tên Xóa từ con trỏ đến hết từ: M-d. Xóa từ con trỏ đến hết dòng: C-k
Di chuyển con trỏ Xóa một dòng: C-a C-k
Sử dụng các phím mũi tên
Chọn tất cả: C-x h. Undo : C-x u
Lên dòng trên C-p, xuống dòng dưới C-n
Phía trước một ký tự C-b, sau một ký tự C-f M-w: copy vùng được đánh dấu.
Về đầu dòng C-a, về cuối dòng C-e C-w: cắt vùng đánh dấu. C-y dán nội dung.
Lập trình C - Việt Nhật 2007 17 Lập trình C - Việt Nhật 2007 18
Các lệnh trong Emacs Biên dịch chương trình C trên Linux
Tìm kiếm trong văn bản: Hầu hết các bản phân phối của Linux đều tích hợp với trình
Tìm xuôi chiều C-s từ_cần_tìm biên dịch ngôn ngữ C: gcc.
Tìm ngược chiều C-r từ_cần_tìm Sử dụng gcc
gcc mặc định sẽ biên dịch tập tin mã nguồn thành mã đối tượng,
Soạn thảo trên nhiều cửa sổ sau đó liên kết mã đối tượng để tạo ra tập tin thực thi
Cho phép theo dõi nội dung ở những phần khác nhau của file Nếu không chỉ định tên tập tin thực thi, mặc định kết quả sẽ là
C-x-2 Chia hai cửa sổ theo chiều ngang a.out
C-x-3 Chia hai cửa sổ theo chiều dọc Các tham số biên dịch
C-x-0 di chuyển giữa hai cửa sổ -Wall : bật tất cả các cảnh báo
-c: chỉ biên dịch tạo tập tin mã đối tượng (object) a.o
C-x-1 Xóa tất cả các cửa sổ và về lại cửa sổ đầu tiên
-o: chỉ định tên file thực thi mong muốn
Thoát khỏi Emacs -g: thêm thông tin gỡ rối (debug)
C-x-c -l: chỉ định thư viện liên kết.
gcc –Wall hello.c –o runhello
./runhello
Lập trình C - Việt Nhật 2007 19 Lập trình C - Việt Nhật 2007 20
5
Biên dịch chương trình C trên Linux Một số lỗi thường gặp khi biên dịch
Các tập tin makefile cho phép: Segfaults
Tạo các script biên dịch, tiết kiệm thời gian gõ lệnh Khi có lỗi truy nhập bộ nhớ. Ví dụ: cố gắng truy nhập thông tin từ
Tiết kiệm thời gian biên dịch vì chỉ dịch lại những tập tin có thay một con trỏ NULL.
đổi Linker errors
Rất phức tạp và dễ gây lỗi. Lỗi trong dòng lệnh gcc hay makefile
Có thể sử dụng lại Multiple definitions of functions
EXEC_NAME = helloWorld Định nghĩa sai trong cái chỉ thị tiền xử lý trong tập tin header như:
all: main.o #ifndef, #define
gcc –g –o $(EXEC_NAME) main.o
main.o: main.c
gcc –g –c main.c –o main.o
clean:
rm –rf main.o $(EXEC_NAME)
Lập trình C - Việt Nhật 2007 21 Lập trình C - Việt Nhật 2007 22
3. int main()
4. {
5. printf(“Hello\n");
6. return 0;
Chương 2: Khái niệm cơ sở
Biến, Hằng, Toán tử, Kiểu dữ liệu cơ sở, Các phép toán và Các từ
khóa
7. }
6
Chương trình C Mở rộng 1
#include <stdio.h> 1. #include <stdio.h>
khai báo sử dụng thư viện xuất/nhập chuẩn (standard I/O library).
2.
Các thư viện khác: string, time, math…
int main() 3. int main()
khai báo hàm main(). Chương trình C phải khai báo (duy nhất) 4. {
một hàm main(). Khi chạy, chương trình sẽ bắt đầu thực thi ở
câu lệnh đầu tiên trong hàm main(). 5. int a, b, c;
{…} 6. a = 5;
mở và đóng một khối mã. 7. b = 7;
printf
8. c = a + b;
hàm printf() gửi kết xuất ra thiết bị xuất chuẩn (màn hình). Phần
nằm giữa “…“ gọi là chuỗi định dạng kết xuất (format string) 9. printf(“%d + %d = %d\n“, a, b, c);
return 0; 10. return 0;
ngừng chương trình. Mã lỗi 0 (error code 0) – không có lỗi khi
chạy chương trình. 11. }
Lập trình C - Việt Nhật 2007 25 Lập trình C - Việt Nhật 2007 26
Sử dụng biến:
Tên sai:x^2, y-2, 23ant printf(“%d + %d = %d\n“, a, b, c);
Lập trình C - Việt Nhật 2007 27 Lập trình C - Việt Nhật 2007 28
7
Mở rộng 2 Chú ý
1. #include <stdio.h> 12 c C phân biệt chữ hoa/chữ thường do đó phải viết đúng tên
2. 7 b lệnh.
3. int main() 5 a vd: printf chứ không phải là Printf, pRintf, PRINTF.
4. {
5. int a, b, c; Trong câu lệnh scanf() để lấy giá trị vào biến, phải luôn dùng
6. printf(“Nhap so thu nhat: “); dấu & trước tên biến.
7. scanf(“%d”, &a); /ngonnguC/bin/tong
8. printf(“Nhap so thu hai: “); Khi gọi các hàm phải khai báo các tham số đúng vị trí và đầy
Nhap so thu nhat: 5 đủ.
9. scanf(“%d”, &b);
Nhap so thu hai: 7
10. c = a + b;
11. printf(“%d + %d = %d\n“, a, b, c); Phải khai báo biến trước khi sử dụng trong chương trình.
5 + 7 = 12
12. return 0;
/ngonnguC/bin/
13. }
Lập trình C - Việt Nhật 2007 29 Lập trình C - Việt Nhật 2007 30
8
Hằng số Kiểu và chuyển kiểu (typecasting)
Hằng số (constant) giá trị không thay đổi trong quá trình sử C cho phép chuyển đổi kiểu dữ liệu cơ bản trong khi đang
dụng. tính toán (chuyển kiểu tường minh).
Khai báo hằng: ví dụ:
#define <constantname> <value> void main()
{
vd: float a;
#define TRUE 1 int b;
#define FALSE 0 b = 10/3;
a = (float)10/3;
Giá trị hằng printf(“a = %f \n b = %d\n”, a, b);
Hằng nguyên: 3423, 0x48 = 4*16+8=72 }
Hằng dấu phẩy động: -231.292, 123.456E-4
Hằng ký tự: ‘a’, ‘b’ Chuyển kiểu tự động: khi biểu thức gồm hai toán hạng khác
‘9’ – ‘0’ = 57 – 48 = 9 kiểu thì kiểu thấp hơn sẽ được nâng thành kiểu cao hơn
1.5*(11/3)=4.5
Lập trình C - Việt Nhật 2007 33 Lập trình C - Việt Nhật 2007 34
Lập trình C - Việt Nhật 2007 35 Lập trình C - Việt Nhật 2007 36
9
Các toán tử so sánh và toán tử logic Số nhị phân
Relational and Quality Operators Possible Mistakes Hệ đếm thập phân:
X Y X<Y X <= Y X>Y X >= Y X != Y X == Y X=Y X<<Y X>>Y 5426=5*103 + 4*102 + 2*101 + 6*100
3 3 0 1 0 1 0 1 3 24 0 254,68 = 2*102 + 5*101 + 4*100 + 6*10-1 + 8*10-2
3 4 1 1 0 0 1 0 4 48 0
Hệ đếm nhị phân (cơ số hai): Hệ chỉ có hai chữ số 0 và 1.
4 3 0 0 1 1 1 0 3 32 0
1000 10112 = 1*27 + 0*26 + 0*25 + 0*24 + 1*23 + 0*22 + 1*21 + 1*20 =
27 + 23 + 21 + 20 = 139
1011.012 = 1*23 + 0*22 + 1*21 + 1*20 + 0*2-1 + 1*2-2 = 11.25
Logical Operators Possible Mistakes Một chữ số nhị phân – BIT – là đơn vị lưu trữ thông tin nhỏ nhất
X Y X && Y X || Y !X !Y X&Y X|Y của máy tính.
0 0 0 0 1 0 0 0
0 7 0 1 1 0 0 7
5 0 0 1 0 1 0 5
5 7 1 1 0 1 5 7
8 7 1 1 0 1 0 15
Lập trình C - Việt Nhật 2007 37 Lập trình C - Việt Nhật 2007 38
10
Các phép gán tắt
Lập trình C - Việt Nhật 2007 43 Lập trình C - Việt Nhật 2007 44
11
Hàm printf Ký tự Escape
Khi không có mặt dấu trừ: kết quả dồn về bên phải. Ngược lại, kết Đánh dấu các ký tự đặc biệt: \’, \”, \n, \t, \r, \\, \0
quả dồn về bên trái Dùng trong hàm printf để định dạng dòng ra
Ký tự chuyển dạng bắt đầu = 0, nếu độ dài của đối số nhỏ hơn
width thì kết quả sẽ điền các ký tự 0 vào bên trái.
Ví dụ: int i = -124;
Lệnh Kết quả
printf("%-5d", i); "-124 "
printf("%5d", i); " -124"
printf("%-05d", i); "-124"
printf("%05d", i); "-0124"
char c='j'; float x=28.932, y= 4.061;
printf("%c nam o toa do %2.3f %f", c, x, y);
Sẽ hiển thị: j nam o toa do 28.932 4.061000
Lập trình C - Việt Nhật 2007 45 Lập trình C - Việt Nhật 2007 46
Lập trình C - Việt Nhật 2007 47 Lập trình C - Việt Nhật 2007 48
12
Câu lệnh điều kiện if
if (<dieu kien>)
Ví dụ Ví dụ
1. #include <stdio.h> Mã giả (pseudo code): Nếu điểm thi lớn hơn hay bằng 5 thì sinh viên
đã vượt qua kỳ thi.
Mã chương trình C: if ( diemthi >= 5 )
2. int main() { printf( "Vuot qua ky thi\n" );
3. int b;
4. printf("Enter a value:");
5. scanf("%d", &b); true
diemthi >= 5 print “Qua”
6. if (b < 0)
7. printf("The value \
false
is negative\n");
8. return 0;
9. }
Lập trình C - Việt Nhật 2007 51 Lập trình C - Việt Nhật 2007 52
13
if … else … Ví dụ
if (<dieu kien>) …
{ printf(“1/X is: “);
/* cac lenh thuc hien neu dieu kien dung */
if(X)
}
printf(“ %f \n”, 1/X);
else
{ else
/* cac lenh thuc hien neu dieu kien sai */ printf(“ undefined \n”);
} True False …
… expression
statement1 statement2
Next statement
Lập trình C - Việt Nhật 2007 53 Lập trình C - Việt Nhật 2007 54
4. printf("Enter a value:");
5. scanf("%d", &b);
false true
diemthi >= 5 6. if (b == 5)
print “Truot” print “Qua” 7. printf(“b is "); printf( “5 \n”);
8. return 0;
9. }
Lập trình C - Việt Nhật 2007 55 Lập trình C - Việt Nhật 2007 56
14
Lỗi đơn giản nhưng dễ phạm Lệnh if else if
1. printf(“1/X is: “); Khi muốn thực hiện 1 trong n quyết định
2. if(X < 0) ; if (biểu thức1)
3. printf(“ X is negative \n”); lệnh 1;
+
4. … else if (biểu thức2)
Biểu thức 1 Lệnh 1
lệnh 2;
-
…
+
else if (biểu thức_n_1) Lệnh 1
Biểu thức 2
lệnh n -1;
-
else lệnh n;
Lệnh ứng với else cuối cùng
Lập trình C - Việt Nhật 2007 57 Lập trình C - Việt Nhật 2007 58
15
Điều kiện lồng nhau Lặp - lệnh while
Câu lệnh if có thể được lồng vào nhau. while (bieu thuc dieu kien)
1. if ( X >= 0 ) {
{cac lenh}
2. if ( Y < 0 )
3. Y = Y + sqrt(X); Khi biểu thức điều kiện (expression) còn khác 0 (TRUE), lệnh
4. } (statement) tiếp tục được thực hiện. Nếu expression bằng 0
5. else (FALSE), lệnh while dừng và chương trình sẽ gọi lệnh kế tiếp
6. Y = Y + sqrt(-X); sau while.
Nếu lúc đầu expression bằng 0 thì (statement) trong while không
Tuy nhiên, cần chú ý đến thứ tự các cặp lệnh if … else … khi lồng
các lệnh if. Nếu không sẽ phát sinh lỗi. bao giờ được gọi thực hiện.
1. if ( X >= 0 )
2. if ( Y < 0 )
False
3. Y = Y + sqrt(X); expression
4. else
5. Y = Y + sqrt(-X); True
Bài tập: Viết chương trình giải phương trình bậc 2: statement(s)
Next statement
ax^2 + bx +c = 0. Chú ý các điều kiện có nghiệm.
Lập trình C - Việt Nhật 2007 61 Lập trình C - Việt Nhật 2007 62
16
1 /*
2 Tinh diem trung binh kiem tra
3
4
Dieu khien vong lap bang bien dem */
#include <stdio.h>
Thiết kế giải thuật Top-down
5
1. Khởi tạo biến
6 int main()
Mở rộng bài toán:
7 {
2. Thực hiện vòng
8 int counter, grade, total, average;
lặp Viết chương trình tính điểm trung bình mà số sinh viên không
9
10 /* Khoi tao gia tri bien */
được biết trước.
11 total = 0; Làm cách nào để kết thúc chương trình?
12 counter = 1;
3. Xuất kết quả
13
Sử dụng giá trị có vai trò "lính canh"
14 /* Xu ly */ Còn có tên gọi khác là cờ
15 while ( counter <= 120 ) {
16 printf( "Nhap diem: " );
Chỉ ra dấu hiệu “kết thúc nhập dữ liệu.”
17 scanf( "%d", &grade ); Vòng lặp kết thúc khi người sử dụng nhập vào giá trị lính canh.
18 total = total + grade;
19 counter = counter + 1;
Giá trị này được chọn sao cho không thể nhầm lẫn nó với các giá
20 } trị nhập vào thông thường khác.
21
22 /* Kêt thuc */
23 average = total / 120;
24 printf( "Diem trung binh cua ca lop la %d\n", average );
25
26 return 0; /* Chuong trinh ket thuc thanh cong */
27 } Lập trình C - Việt Nhật 2007 65 Lập trình C - Việt Nhật 2007 66
1 /*
Tiếp cận top-down, giải quyết từng bước vấn đề 6 int main()
7 { 2. Nhập liệu
Bắt đầu với vấn đề cần giải quyết từ đỉnh (top): 8 float average;
Xác định điểm trung bình kiểm tra của cả lớp 9 int counter, grade, total;
2.1 Thi hành vòng
Phân chia bài toán thành các công việc nhỏ hơn và liệt kê chúng 10
lặp
theo trình : 11 /* khởi tạo */
12 total = 0;
Khởi tạo biến
13 counter = 0;
Nhập dữ liệu, tính tổng và đếm số sinh viên
14
Tính điểm trung bình
15 /* xử lý */
Nhiều chương trình trải qua ba giai đoạn: 16 printf( "Nhập điểm, -1 để thoát ra: " );
Khởi tạo: khởi tạo giá trị các biến 17 scanf( "%d", &grade );
18
Xử lý: nhập giá trị dữ liệu và thao tác trên chúng 19 while ( grade != -1 ) {
Kết thúc: Tính toán và hiển thị kết quả cuối cùng 20 total = total + grade;
21 counter = counter + 1;
22 printf( "Enter grade, -1 to end: " );
23 scanf( "%d", &grade );
24 }
Lập trình C - Việt Nhật 2007 67 Lập trình C - Việt Nhật 2007 68
17
1 /* Fig. 3.10: fig03_10.c
2 Phân tích kết quả thi */
5 int main()
6 {
Bài toán 7
8
/* Khai báo khởi tạo biến */
int passes = 0, failures = 0, student = 1, result;
Một học viện có một danh sách kết quả kiểm tra của 120 sinh 9
Chú ý 15
16
if ( result == 1 )
passes = passes + 1;
/* if/else lồng trong while */
Lập trình C - Việt Nhật 2007 71 Lập trình C - Việt Nhật 2007 72
18
Lệnh for (tiếp theo) Ví dụ
Giữa hai dấu ; có thể có nhiều hơn một biểu thức Bài toán đổi nhiệt độ. Yêu cầu: hiển thị nhiệt độ chính xác đến con
số thập phân sau dấu phẩy.
Phân cách nhau bởi dấu phảy.
Example: 1. #include <stdio.h>
for (i = 0, j = 0; j + i <= 10; j++, i++)
printf( "%d\n", j + i );
2. int main() {
3. float a = 0;
4. int i;
5. for(i=0; i<=100; i+=10) {
6. printf("%6.2f degrees F = %6.2f degrees C\n",
a, (a - 32.0) * 5.0 / 9.0);
7. a = a + 10;
8. }
9. return 0;
10. }
Lập trình C - Việt Nhật 2007 73 Lập trình C - Việt Nhật 2007 74
Lập trình C - Việt Nhật 2007 75 Lập trình C - Việt Nhật 2007 76
19
Ví dụ - giao diện chương trình
1. #include <stdio.h> 14. printf(“ 3. Thoat chuong trinh \n\n”);
2. #define PTB1 1 15. printf(“ Chon muc so (1/2/3) ? “);
3. #define PTB2 2 16. scanf(“%d”, &i);
4. #define STOP 3 17. if(i == PTB1)
18. printf(“Giai phuong trinh bac 1: hien chua co\n”);
19. else if(i == PTB2)
5. int main()
20. printf(“Giai phuong trinh bac 2: chua cai dat\n\n”);
6. {
21. } while (i != STOP);
7. int i;
8. do { 22. return 0;
9. printf(“ Chuong trinh giai phuong trinh bac thap \n”); 23. }
10. printf(“ 1. Giai phuong trinh bac 1: ax + b = 0 \n”);
11. printf(“ 2. Giai phuong trinh bac 2 : ax^2 + bx + c = 0 \n”); Bài tập: Ghép chương trình trên với hai chương trình trong bài tập
1 và 2
Lập trình C - Việt Nhật 2007 77 Lập trình C - Việt Nhật 2007 78
Lập trình C - Việt Nhật 2007 79 Lập trình C - Việt Nhật 2007 80
20
Tìm số nguyên tố lớn
1. #include <stdio.h> 16. for( ; ; ucv_nguyento += 2)
2. #define TRUE 1 17. {
3. main(void) 18. la_nguyento = !TRUE;
4. { 19. for(sochia = 3; ucv_nguyento % sochia; sochia += 2)
5. unsigned long int sochia, ucv_nguyento;
20. if(sochia * sochia > ucv_nguyento)
6. int la_nguyento;
21. { la_nguyento = TRUE;
7. printf(“Nhap vao so khoi dau: “); 22. break;
8. scanf(“%lu”, & ucv_nguyento); 23. }
9. if(ucv_nguyento <= 2) 24. if (la_nguyento)
10. ucv_nguyento = 2; 25. break;
11. else if(ucv_nguyento !=3 ) 26. }
12. { 27. }
13. if(ucv_nguyento %2 == 0) 28. printf(“So nguyen to lon hon gan nhat la %lu\n”, ucv_nguyento);
14. ucv_nguyento ++; /* Phai la so le */ 29. }
Lập trình C - Việt Nhật 2007 81 Lập trình C - Việt Nhật 2007 82
if ((grade<0) || (grade>100)) 6 {
7 int x;
cú pháp: { 8
continue; printf(“Illegal grade – try again\n”); 9 for ( x = 1; x <= 10; x++ ) {
continue; 10
} 11 if ( x == 5 )
chỉ áp dụng với lệnh lặp. ……. 12 continue;
} 13
14
Bài tập: Viết chương trình 15 printf( "%d ", x );
nhập vào một số và tìm ra 16 }
tất cả các thừa số nguyên 17
18 printf( "\nDung lenh continue de bo qua gia tri
tố của số đó. 5\n" );
19 return 0;
20 }
1 2 3 4 6 7 8 9 10
Lập trình C - Việt Nhật 2007 83 Lập trình C - Việt Nhật 2007 84
Dung lenh continue de bo qua gia tri 5
21
Tìm thừa số nguyên tố
1. #include <stdio.h> 12. { /* Tim ra mot thua so */
13. printf(“%lu”, thuasonguyento);
2. main(void) 14. phanconlai /= thuasonguyento;
3. { 15. continue;
4. unsigned long N, thuasonguyento, phanconlai; 16. }
17. /* Khong phai la thua so nguyen to */
5. printf(“Nhap vao mot so tu nhien: “); 18. if(thuasonguyento == 2) thuasonguyento = 3;
6. scanf(“%lu”, &N); 19. else
20. thuasonguyento += 2;
7. thuasonguyento = 2; 21. }
phanconlai = N; 22. /* thua so nguyen to cuoi cung */
23. printf(“%lu\n”, phanconlai);
8. while(thuasonguyento * thuasonguyento <= phanconlai) 24. }
9. {
10. if(phanconlai % thuasonguyento == 0)
Lập trình C - Việt Nhật 2007 85 Lập trình C - Việt Nhật 2007 86
Lập trình C - Việt Nhật 2007 87 Lập trình C - Việt Nhật 2007 88
22
Lệnh switch Sơ đồ xử lý lệnh switch
Dùng lệnh switch để cài đặt cơ chế rẽ nhánh nhiều chiều.
cú pháp:
switch(<expression>) case a
true
case a action(s) break
{ false
case case1:
case case2: case b
true
case b action(s) break
<statements>; false
break;
/* … */ .
.
case casen: .
<statements>;
break; true
case z case z action(s) break
default:
false
<statements>;
break; default action(s)
}
Lập trình C - Việt Nhật 2007 89 Lập trình C - Việt Nhật 2007 90
Lập trình C - Việt Nhật 2007 91 Lập trình C - Việt Nhật 2007 92
23
1 /*
2 Thống kê điểm số */
Thống kê điểm số (American system) 3
4
#include <stdio.h>
33
Bài tập
34 case 'F': /* điểm là hạng F */
35 ++fCount;
36 break;
37
3. In kết quả
38 case '\n': case' ': /* ignore these in input */ 1) Viết chương trình tính giá trị biểu thức "a op b" như "2 + 3"
39 break;
40
a, b toán hạng – op : toán tử
41 default: /* catch all other characters */
42 printf( "Nhập sai ký tự phân hạng kết quả." );
43 printf( " Làm ơn nhập lại.\n" );
2) Yêu cầu người sử dụng nhập vào một tháng. In ra số ngày
44 break; trong tháng đó.
45 }
46 }
47 Tháng có 31 ngày: 1, 3, 5, 7, 8, 10, 12
48 printf( "\Kết quả phân loại sinh viên:\n" ); Tháng có 30 ngày: 4, 6, 9, 10
49 printf( "A: %d\n", aCount );
50 printf( "B: %d\n", bCount ); Tháng có 28 hoặc 29 ngày : 2
51 printf( "C: %d\n", cCount );
52 printf( "D: %d\n", dCount );
53 printf( "F: %d\n", fCount );
54
55 return 0;
56 }
Lập trình C - Việt Nhật 2007 95 Lập trình C - Việt Nhật 2007 96
24
In số ngày trong một tháng Tính ex với độ chính xác 10-5
#include <stdio.h> case 4: ex = 1+x/1!+x2/2!+……………..+ xn/n!
int main () case 6: #include <stdio.h>
{ case 9: void main() {
int thang; case 11: float epsilon = 0.00001;
printf("\n Nhap vao thangs trong printf("\n Thang %d co 30 float x,add;
nam "); ngay ",thang); int i=1;
scanf("%d",&thang); break; float result = 0.0;
switch(thang) { case 2: add = 1;
case 1: printf ("\ Thang 2 co 28 hoac printf("Chương trình tính giá trị e mũ x. \n");
case 3: 29 ngay");
printf("x="); scanf("%f", &x);
case 5: break;
while (add > epsilon) {
case 7: default :
result = result + add;
case 8: printf("\n Khong co thang
%d", thang); add = add*x/i;
case 10:
break; i = i+1;
case 12:
} }
printf("\n Thang %d co 31
ngay ",thang); return 0; printf ("Ket qua la:%f", result);
break; } }
Lập trình C - Việt Nhật 2007 97 Lập trình C - Việt Nhật 2007 98
Ví dụ:
Max = (Y > Z) ? Y : Z;
Lập trình C - Việt Nhật 2007 99 Lập trình C - Việt Nhật 2007 100
25
Từ khóa của C Từ khóa của C
auto break case char #define để khai báo hằng số và …
const continue default do typedef để khai báo kiểu dữ liệu riêng
double else enum extern
float for goto if Toán tử sizeof xác định số byte được dùng để chứa một đối
int long register return tượng
short signed sizeof static
ví dụ:
struct switch typedef union typedef unsigned long int Int32;
unsigned void volatile while /* ... */
int x;
x = sizeof(Int32); // x = 4
Lập trình C - Việt Nhật 2007 101 Lập trình C - Việt Nhật 2007 102
Mảng
Mảng là tập hợp các giá trị cùng kiểu.
Ngôn ngữ lập trình C
Dãy liên tục các ô nhớ lưu các phần tử có liên quan đến nhau
Khai báo:
typename arrayname[array_size];
int a[array_size];
Chương 4: Mảng n = array_size;
26
Mảng Tên mảng
(Các phần tử mảng Chú ý
có cùng chung một
tên, c)
Các phần tử mảng hoạt động như các C không kiểm tra giới hạn của chỉ số truy cập phần tử mảng.
biến thông thường Truy cập đến phần tử i>=array_size không có cảnh báo,
c[ 0 ] = 3; c[0] -45
nhưng giá trị không kiểm soát được.
printf( "%d", c[ 0 ] ); c[1] 6
Lập trình C - Việt Nhật 2007 107 Lập trình C - Việt Nhật 2007 108
27
Chú ý Bảng lượng mưa (tiếp)
Không thể thực hiện các thao tác chép nội dung một mảng #include <stdio.h>
sang mảng khác.
int main()
Chép từng phần tử mảng
{
char A[3]={‘a’,’b’,’c’};
int thang;
char B[3]; int bangmua[12] = { 30, 40, 45, 95, 130, 220,
B = A; // ??? 210, 185, 135, 80, 40, 45 };
for(int i=0; i<3; i++)
B[i] = A[i]; printf("Nhap thang: ");
hoặc chép khối bộ nhớ (sẽ được đề cập sau) scanf("%d", &thang);
Không dùng phép so sánh trực tiếp (==) nội dung trong hai
printf("Luong mua trung binh cua thang: %d mm.\n",
mảng. bangmua[thang-1]);
Phép so sánh (A==B) so sánh địa chỉ hai vùng nhớ mà A và B
chỉ đến. return 0;
}
Lập trình C - Việt Nhật 2007 109 Lập trình C - Việt Nhật 2007 110
Lập trình C - Việt Nhật 2007 111 Lập trình C - Việt Nhật 2007 112
28
Khởi tạo mảng gồm các số chẵn từ 2 đến Nhập xuất dữ liệu cho mảng
20
#include <stdio.h>
#include <stdio.h> #define SOTHANG 12
#define arraySize 10
/* Lưu và hiển thị lượng mưa */
int main()
int main()
{
{
int s[ arraySize ]; // mảng S có 10 phần tử
int solieu[SOTHANG];
int i;
int thang;
for ( i = 0; i < arraySize; i++ ) // khởi tạo giá trị
s[ i ] = 2 + 2 * i; for ( thang=0; thang < SOTHANG; thang++ )
printf("Phan tu \t Gia tri\n"); {
for ( i = 0; i < arraySize; i++ ) scanf("%d", &solieu[thang] );
printf("%d\t%d\n", i, s[i]); }
return 0;
} ...
Lập trình C - Việt Nhật 2007 113 Lập trình C - Việt Nhật 2007 114
1 /*
2 Histogram printing program */
29
Đổi số nguyên dương sang nhị phân Đổi số nguyên dương sang nhị phân
Nguyên lý:
Chia liên tiếp số cần chuyển đổi cho 2 cho đến khi thương số là 0
Lấy các số dư theo chiều ngược lại
23 2
1 11 2
1 5 2
1 2 2
0 1 2
10111 1 0
Lập trình C - Việt Nhật 2007 117 Lập trình C - Việt Nhật 2007 118
Lập trình C - Việt Nhật 2007 119 Lập trình C - Việt Nhật 2007 120
30
Sắp xếp mảng Sắp xếp mảng
Lập trình C - Việt Nhật 2007 121 Lập trình C - Việt Nhật 2007 122
31
Mảng nhiều chiều Ví dụ: Lượng mưa hàng năm
thang
Khai báo mảng 2 chiều:
0 1 2 3 4 5 6 7 8 9 10 11
type name[row_size][column_size]; 0 30 40 75 95 130 220 210 185 135 80 40 45
SumSquares
1 25 25 80 75 115 270 200 165 85 5 10 0
nam
0 1 4 2 35 45 90 80 100 205 135 140 170 75 60 95
1 2 5
3 30 40 70 70 90 180 180 210 145 35 85 80
4 30 35 30 90 150 230 305 295 60 95 80 30
0 1 4 1 2 5
Lượng mưa trung bình năm (mm)
Khởi tạo
int SumSquares[2][3] = { {0,1,4}, {1,2,5} };
int SumSquares[2][3] = { {0,1,4} }; Bài toán: thao tác trên bảng vũ biểu
int SumSquares[ ][3] = { {0,1,4}, {1,2,5} }; • input: tháng và năm
int SumSquares[ ][3] = { {0,1, }, {1} };
int SumSquares[ ][3]; • output: lượng mưa trung bình vào năm, tháng đó
Lập trình C - Việt Nhật 2007 125 Lập trình C - Việt Nhật 2007 126
#define NYEARS 5
#define
int main()
NMONTHS 12
Nhập Mảng 2 chiều
{
int table[NYEARS][NMONTHS] =
{ 1. #define MAX_STUDENT 5
{30,40,75,95,130,220,210,185,135,80,40,45}, 2. #define MAX_SUBJECT 6
{25,25,80,75,115,270,200,165, 85, 5,10, 0},
{35,45,90,80,100,205,135,140,170,75,60,95},
{30,40,70,70, 90,180,180,210,145,35,85,80},
{30,35,30,90,150,230,305,295, 60,95,80,30}
3. int StudentScore[MAX_STUDENT][MAX_SUBJECT];
};
int year, month;
printf("Enter year and month: "); scanf("%d %d", &year, &month); 4. void read_Score(int Score[MAX_STUDENT][MAX_SUBJECT],
int nStudents, int nSubjects)
if ((0 <= year) && (year < NYEARS) && 5. {
(0 <= month) && (month < NMONTHS))
printf("Rainfall for year %d month %d is %d", year, month, 6. int i,j;
table[year-1][month-1]);
else 7. for(i=0; i<nStudents; i++)
{
printf("Data non available");
8. for(j=0; j<nSubjects; j++)
} 9. scanf(“%d”, &Score[i][j]);
return 0;
} 10. }
Lập trình C - Việt Nhật 2007 127 Lập trình C - Việt Nhật 2007 128
32
Biểu diễn mảng 2 chiều Bài tập
StudentScores
Viết chương trình nhập vào một dãy số theo thứ tự tăng, nếu
Student1 0 1 4 ? ? ? nhập sai quy cách thì yêu cầu nhập lại. In dãy số sau khi
nhập xong. Nhập thêm một số và chèn số đó vào dãy số sao
Student2 1 2 5 ? ? ?
cho không thay đổi tính chất của dãy. In lại dãy mới để kiểm
Student3 ? ? ? ? ? ? tra.
Student4 ? ? ? ? ? ? Viết chương trình nhập vào một ma trận nguyên m hàng n
Student5 ? ? ? ? ? ?
cột. In ma trận ra màn hình. Nhập vào một số nguyên và kiểm
tra xem có phần tử nào trong ma trận có cùng giá trị hay
không. Ở vị trí nào và có bao nhiêu phần tử.
Viết chương trình đảo một mảng một chiều. Ví dụ:
1, 3, 5, 8, 7, 9, 6 thành 6, 9, 7, 8, 5, 3, 1
0 1 4 ? ? ? 1 2 5 ? ? ?
Student1 Student2
Lập trình C - Việt Nhật 2007 129 Lập trình C - Việt Nhật 2007 130
Chương trình
Chương trình con A
Với chương trình máy tính Các biến
Với chương trình đơn giản, tất Câu lệnh
Hàm (function) cả các xử lý nên được đặt
Kết thúc chương trình con A
trong hàm main.
Chương trình con B
Cao Tuấn Dũng Chia để trị: Phân chia chương Các biến
2007 trình thành các phần nhỏ - các Câu lệnh
chương trình con (routine) hay
Kết thúc chương trình con B
còn gọi là các hàm (function)
33
Hàm – Mô đun hóa chương trình Hàm
Trong C mọi chương trình con là hàm, không có sự phân biệt giữa Là một đoạn mã lệnh độc lập, được đặt tên, thực hiện một
hàm và thủ tục. nhiệm vụ cụ thể và có thể trả về một giá trị cho chương trình
Cách tiếp cận phân tích bài toán theo hướng top-down: xác định gọi hàm
chức năng của các hàm. Sử dụng hàm trong chương trình giúp:
Chia nhỏ chương trình thành những mô đun nhỏ dễ quản lý
Một chương trình C là một tập hợp các hàm tương tác bằng cách
Thống nhất các đoạn mã tương tự nhau, sử dụng nhiều lần trong
gọi lẫn nhau và truyền các thông tin qua lại giữa các hàm.
một chương trình
Các hàm có thể được dùng lại nhiều lần Æ thành lập các thư viện
Tái sử dụng mã lệnh trong nhiều hơn một chương trình
hàm. (vd: stdio, stdlib, conio, math, string,…)
Hai loại hàm trong C:
Hàm chuẩn trong các thư viện C: printf, scanf, các hàm tính toán toán
học, xử lý xâu ký tự, …
Hàm do người dùng định nghĩa.
Lập trình C - Việt Nhật 2007 133 Lập trình C - Việt Nhật 2007 134
34
Hoạt động của hàm Khai báo hàm
Các lệnh trong hàm chưa được Nguyên mẫu hàm (prototype): nguyên mẫu hàm cho chương
thực hiện cho tới khi hàm được danh sách đối số trình dịch biết sự tồn tại của một hàm với tên, danh sách đối
gọi. Khi hàm được gọi điều số, kiểu giá trị trả về cụ thể
khiển thực hiện các lệnh được Chương trình
chuyển cho hàm. Khai báo Biến; Cú pháp:
Chương trình gửi thông tin đến Lệnh; Kiểu giá trị trả về Tênhàm(kiểu đối số 1, kiểu đối số 2,…);
…. int Max(int x, int y);
hàm: các đối số Hàm
Lời gọi hàm
Hàm gửi lại thông tin: giá trị trả Lệnh int Min(int x, int y);
về ……. Nguyên mẫu hàm chỉ cần thiết khi phần định nghĩa hàm nằm
Bốn bước cơ bản khi gọi hàm: Kết thúc phía sau lời gọi hàm trong chương trình.
Giá trị trả về Nguyên mẫu hàm được chương trình dịch sử dụng để kiểm
Cấp phát bộ nhớ cho các đối số và biến cục bộ tra tính hợp lệ của hàm
Gán giá trị của các tham số thực cho đối số tương ứng Nguyên mẫu hàm phải khớp với định nghĩa hàm
Thi hành lệnh
Gặp câu lệnh return: Xóa các đối số, biến cục bộ, thoát khỏi hàm
Lập trình C - Việt Nhật 2007 137 Lập trình C - Việt Nhật 2007 138
Lập trình C - Việt Nhật 2007 139 Lập trình C - Việt Nhật 2007 140
35
Các thành phần của hàm Hàm radian
Hàm chuyển đổi số đo góc từ độ sang radian
Tên hàm (name) #include <stdio.h>
danh sách tham số (list of parameters) giao diện #include <math.h>
#define PI 3.14159
kiểu trả về (return type) (interface) của hàm
thân hàm (function body) /* nguyen mau ham*/
float Radian(float x);
lệnh trả về (return) void main() {
float ang, adj, opp;
printf("Nhap gia tri cua goc (theo do:"); scanf("%f", &ang);
<return_type> function_name (<list_of_parameters>) printf("Nhap canh ke:"); scanf("%f", &adj);
opp= adj*tan(Radian(ang));
printf("Canh doi la: %f\n", opp);
Các hàm phải được khai báo trước khi được gọi thi hành. }
/* Ham tinh Radian */
float Radian(float deg) {
float result;
result = PI * deg/180.0;
return result;
}
Lập trình C - Việt Nhật 2007 141 Lập trình C - Việt Nhật 2007 142
Khi gọi hàm, các đối số đưa vào hàm phải đầy đủ và đúng
kiểu như đã khai báo.
Lập trình C - Việt Nhật 2007 143 Lập trình C - Việt Nhật 2007 144
36
Ví dụ Giá trị trả về (return value)
/* ... */ Một hàm được phép trả về cho phần chương trình gọi nó một
1. float max(float x, float y) giá trị: giá trị trả về.
2. {
3. return (x > y ? x : y); Chương trình gọi hàm có thể sử dụng giá trị trả về.
4. }
Một số hàm không cần trả về các giá trị. Từ khóa void được
/*…*/ dùng trong khai báo giá trị trả về của các hàm này.
6. int main() Kiểu int sẽ là kiểu của trị trả về nếu không chỉ rõ kiểu giá trị
7. { trả về trong khai báo hàm.
8. float z = 4.7;
9. float x = max(4.5, z); ví dụ:
afunction() { /*…*/ }
10. }
Lập trình C - Việt Nhật 2007 145 Lập trình C - Việt Nhật 2007 146
Lập trình C - Việt Nhật 2007 147 Lập trình C - Việt Nhật 2007 148
37
Phạm vi truy cập của biến Ví dụ
Phạm vi truy cập (scope) của biến xác định vùng chương 1. int i=1; /* i là biến toàn cục vì nằm ở ngoài các khối lệnh */
trình có thể truy cập đến biến.
2. { /* block A */
3. int i=2;
Biến được khai báo trong khối lệnh (nằm giữa { }) có thể 4. printf (“%d\n”, i); /* outputs 2 */ }
được truy cập bởi các lệnh nằm trong cùng khối và các lệnh 5. { /* Block B */
thuộc các khối con. 6. int i=3;
7. printf (“%d\n”, i); /* outputs 3 */
Biến được khai báo “ngoài cùng” có phạm vi truy cập trong 8. { /* Block C */
toàn chương trình Æ biến toàn cục (global variables). 9. int i=4;
10. printf (“%d\n”, i); /* outputs 4 */ }
Biến cục bộ (local variable) được khai báo và sử dụng trong 11. { /* Block D */
phạm vi một khối lệnh và các khối lệnh con. 12. printf (“%d\n”, i); /* outputs 3 */ }
13. }
14. { /* Block E */
Biến thuộc phạm vi trong cùng được tham chiếu đến đầu tiên. 15. printf (“%d\n”, i); /* outputs 1 */ }
Lập trình C - Việt Nhật 2007 149 Lập trình C - Việt Nhật 2007 150
Chú ý:
Từ khoá auto được dùng để khai báo các biến cục
bộ. Tuy nhiên rất ít khi người ta sử dụng nó vì các
biến cục bộ đã mặc nhiên được xem là automatic.
Tham số hình thức trong một hàm cũng là biến
automatic.
Lập trình C - Việt Nhật 2007 151 Lập trình C - Việt Nhật 2007 152
38
Cấp lưu trữ và phạm vi các đối tượng Biến tự động
/* Hoán vị giá trị hai số nguyên */
Cấp lưu trữ ( storage class): thời gian sống + phạm vi #include <stdio.h>
Có tất cả bốn cấp lưu trữ trong C: tự động, toàn cục (ngoài), void swap(auto int* , auto int* ); /* Hàm nguyên mẫu */
tĩnh và register được khai bao thông qua các từ khóa auto, main()
{
extern, static và register.
auto int x, y;
Mặt khác cũng lưu ý rằng cấp lưu trữ của một biến đôi khi x = 10; y = 20;
được xác định bởi chính vị trí khai báo của biến đó trong printf("Ban đầu x = %d, y = %d", x, y);
swap( &x, &y);
chương trình. Trong các trường hợp khác người ta dùng
printf("Sau đó x = %d, y = %d", x, y);
bốn từ khóa nói trên trong khai báo }
Ví dụ: /* Định nghĩa hàm swap */
auto int a,b,c; void swap( auto int* a, auto int* b)
{
extern float r1,r2; auto int temp;
static int count = 0; temp = *a;
extern char star; *a = *b;
*b = temp;
}
Lập trình C - Việt Nhật 2007 153 Lập trình C - Việt Nhật 2007 154
Lập trình C - Việt Nhật 2007 155 Lập trình C - Việt Nhật 2007 156
39
Biến tĩnh (static) Biến tĩnh
Biến tồn tại liên tục từ lúc bắt đầu cho tới lúc kết thúc chương #include<stdio.h>
long fibo(int count);
trình.
main()
Cấp phát bộ nhớ một lần và tồn tại cho đến khi kết thúc {
static tênkiểu tênbiến = giatri; int count,n;
printf("\nn =");scanf("%d",&n);
for(count=1;count<=n; ++count)
#include <stdio.h> printf("\ni=%2d F=%ld", count,fibo(count));
int inc(){ }
Ket qua inc lan 1: 1
static int n=0; long int fibo(int count)
Ket qua inc lan 2: 2
n++; {
return n; static long int f1=1,f2=1;
} long int f;
void main(){ f=(count<3) ? 1: f1+f2;
f2=f1; f1=f;
printf("\n Ket qua inc lan 1: %d", inc());
return f;
printf("\n Ket qua inc lan 2: %d", inc()); }
} Nếu bỏ từ khóa static ở trong thân hàm main ????
Lập trình C - Việt Nhật 2007 157 Lập trình C - Việt Nhật 2007 158
Ví dụ sau đề nghị trình biên dịch đặt biến counter vào hàm tự kết thúc khi thực hiện hết lệnh cuối cùng.
một trong các thanh ghi của máy và khởi tạo nó giá trị
ban đầu bằng 1:
register int counter = 1;
Lập trình C - Việt Nhật 2007 159 Lập trình C - Việt Nhật 2007 160
40
Một số ví dụ Một số ví dụ minh họa
Tính tổng các số nguyên tố nhỏ hơn n Chương trình tính tổng các số nguyên tố
Lập trình C - Việt Nhật 2007 161 Lập trình C - Việt Nhật 2007 162
Lập trình C - Việt Nhật 2007 163 Lập trình C - Việt Nhật 2007 164
41
Truyền giá trị Tại sao truyền giá trị không làm
1. /* Swapping routine that doesn’t work */
thay đổi giá trị đối số
2. #include <stdio.h>
Lập trình C - Việt Nhật 2007 165 Lập trình C - Việt Nhật 2007 166
Truyền bằng tham chiếu Tại sao truyền bằng tham chiếu làm
/* Swapping routine that does work */
thay đổi giá trị đối số
#include <stdio.h>
void Swap(int &x, int &y)
{
M1 main#1::Left = 5 M1 main#1::Left = 7
int Temp;
M2 main#1::Right = 7 M2 main#1::Right = 5
Temp = x;
x = y;
y = Temp;
} M3 Swap#1::Temp = ? M3 Swap#1::Temp = 5
M4 Swap#1::x M4 Swap#1::x
main(void)
M5 Swap#1::y M5 Swap#1::y
{
int Left, Right;
Left = 5; Right = 7;
Swap(Left, Right);
printf(“Left = %d, Right = %d\n”, Left, Right);
}
Lập trình C - Việt Nhật 2007 167 Lập trình C - Việt Nhật 2007 168
42
Dừng chương trình và mã lỗi Truyền mảng cho hàm
Thông thường main trả về giá trị kiểu int
Nên xây dựng một đoạn chương trình con làm nhiệm vụ bắt
lỗi trong quá trình chạy.
Lập trình C - Việt Nhật 2007 169 Lập trình C - Việt Nhật 2007 170
43
Hàm truy cập, in Mảng 2 chiều Chương trình
1. void print_Score(int Score[MAX_STUDENT][MAX_SUBJECT], 1. main(void)
int nStudents, int nSubjects) 2. {
2. {
3. int nStudents, nScores;
3. int i,j;
Ví dụ:
Ngôn ngữ lập trình C
bài tập viết chương trình giải phương trình bậc hai.
…
x1 = (-b + sqrt(delta))/(2*a);
…
Đệ Quy
44
Nguyên lý Tính giai thừa
Trong C cho phép trong thân một hàm có thể gọi ngay Ví dụ: Viết chương trình nhập số tự nhiên n và tính giai thừa : n!.
đến chính nó, cơ chế này gọi là đệ qui. Giải quyết bài toán bằng vòng lặp
Có thể định nghĩa hàm đệ qui là hàm sẽ gọi đến chính 1. #include <stdio.h>
nó trực tiếp hay gián tiếp thông qua các hàm khác.
Cách tiến hành giải một bài toán đệ qui nhìn chung có 2. unsigned long int factorial(int n)
những điểm chung sau. 3. { unsigned long f = 1;
Hàm đệ qui thực ra chỉ biết cách giải bài toán trong trường hợp đơn giản 4. for (int i = 1; i<=n; i++)
nhất (hay còn gọi là trường hợp cơ sở). 5. f *= i;
Nếu hàm được gọi trong các trường hợp phức tạp hơn, hàm đệ qui sẽ 6. return f;
chia công việc cần giải quyết thành hai phần. Một phần hàm biết cách 7. }
giải quyết như thế nào, còn phần kia vẫn không biết cách giải quyết như
thế nào tuy nhiên để được gọi là có khả năng đệ qui, phần sau phải
giống với bài toán ban đầu nhưng đơn giản hơn hay nhỏ hơn bài toán 8. int main(void)
ban đầu. 9. { int n;
Để đảm bảo việc đệ qui có kết thúc, mỗi một lần gọi đệ qui thì bài toán
phải đảm bảo đơn giản hơn và các bước đệ qui này còn thực hiên tiếp 10. printf(“Nhap n:”); scanf(“%d”, &n);
cho dến khi nào bài toán đơn giản dần, đơn giản tới mức trở thành printf(“n! = %d! = %l\n”, n, factorial(n));
trường hợp cơ sở. 11. return 0;
12. }
Lập trình C - Việt Nhật 2007 177 Lập trình C - Việt Nhật 2007 178
Lập trình C - Việt Nhật 2007 179 Lập trình C - Việt Nhật 2007 180
45
Dãy Fibonaci (tiếp) Lời gọi hàm đệ quy và Điều kiện
/* Tính dẫy số Fibonaci phương pháp đệ qui */ dừng của thuật giải đệ quy
#include <stdio.h>
long fibo( long ); /* Hàm nguyên mẫu */ Bài toán giải bằng thuật giải đệ quy phải có điều kiện dừng.
main()
{
long result, n;
printf("Hãy nhập vào một số nguyên : "); Thuật toán đệ quy trên máy tính có thể bị giới hạn bởi dung
scanf("%ld", &n); lượng bộ nhớ do lời gọi hàm liên tiếp.
result = fibo(n);
printf("Fibonaci thứ %ld là : %ld\n", number, result);
return 0;
}
/* Định nghĩa đệ qui hàm fibonaci */
long fibo( long n)
{ main
if ( n = 0 || n = 1 )
return n;
else factorial (4) factorial (3) factorial (2) factorial (1)
return fibo(n-1) + fibo(n-2);
}
Hãy vẽ sơ đồ tiến trình gọi hàm khi thực hiện tính dãy fibonacci bằng
đệ quy.
Lập trình C - Việt Nhật 2007 181 Lập trình C - Việt Nhật 2007 182
Truyền thuyết: lúc thế giới hình thành, trong ngôi đền thờ
Brahma có một chồng 64 cái đĩa. Mỗi ngày, có một thầy tu di
chuyển một đĩa. Đến khi hết đĩa thì đó là ngày tận thế.
Lập trình C - Việt Nhật 2007 183 Lập trình C - Việt Nhật 2007 184
46
Cài đặt bằng đệ quy
1. MoveDisk(disk_number, starting_post, target_post,
intermediate_post)
2.
3.
{
if(disk)number > 1)
Ngôn ngữ lập trình C
4. {
5. MoveDisk(disk_number-1, starting_post,
intermediate_post, target_post);
6. printf(“Move disk number %d, from post %d to post %d.\n”,
disk_number, starting_post, target_post);
7. MoveDisk(disk_number-1,intermediate_post,
target_post, starting_post); CON TRỎ
8. }
9. else
10. printf(“Move disk number 1 from post %d to post %d.\n”, Cao Tuấn Dũng
starting_post, target_post); 2007
11. }
47
Bộ nhớ Giá trị biến và địa chỉ trong bộ nhớ
Biến là tên các vùng nhớ được dùng để giữ các giá trị.
M1 main#1::a Khi chúng ta khai báo một biến, máy sẽ cấp phát cho biến đó
M2 main#1::b một số ô nhớ liên tiếp đủ để chứa nội dung của biến, ví dụ
một biến ký tự được cấp phát một byte, một biến nguyên
được cấp phát hai bytes, một biến thực được cấp phát bốn
M3 move_one#1::x bytes .v.v
M4 move_one#1::y Hàm move_one(a, b) cần truy cập vào các vị trí nhớ của a và
b cũng như các giá trị của a và b.
Lập trình C - Việt Nhật 2007 189 Lập trình C - Việt Nhật 2007 190
Lập trình C - Việt Nhật 2007 191 Lập trình C - Việt Nhật 2007 192
48
Con trỏ và địa chỉ biến Sử dụng Con trỏ
Địa chỉ của hai biến ký tự liên tiếp sẽ cách nhau một byte Truy cập vùng nhớ được chỉ bởi một con trỏ
trong khi địa chỉ của hai biến nguyên liên tiếp cách nhau hai int x=1,y=2,z[10];
int *pi; /*pi là một biến con trỏ có kiểu nguyên*/
byte còn địa chỉ của hai biến thực liên tiếp cách nhau tới bốn
pi = &x; /*Địa chỉ của x được gán cho pi, và pi trỏ tới biến x*/
bytes. y=*pi; /*y có giá trị bằng 1*/
Người ta phân biệt các con trỏ theo các kiểu địa chỉ chứa *pi=15; /*Từ bây giờ x có giá trị bằng 15*/
trong các con trỏ: con trỏ kiểu nguyên dùng để chứa địa chỉ pi=&z[0]; /*Từ đây pi chứa địa chỉ của z[0], tức là địa chỉ của mảng z*/
các biến nguyên, con trỏ ký tự chứa địa chỉ các biến ký tự,
con trỏ thực chứa địa chỉ các biến thực.
1024: 15
x
1024:
... p a
pi
Lập trình C - Việt Nhật 2007 193 Lập trình C - Việt Nhật 2007 194
Các phép toán trên con trỏ Các phép toán trên con trỏ
Một biến con trỏ có thể được cộng, trừ với một số nguyên (int Có thể áp dụng các phép so sánh, phép gán cho các con trỏ.
,long) để cho kết quả là một con trỏ cùng kiểu. Xét ví dụ sau: Trong các phép toán này đòi hỏi các toán hạng con trỏ phải
int *x=2,*px,*py; có cùng kiểu. Mọi sự chuyển đổi kiểu tự động từ các kiểu
px = &x; khác thành các kiểu con trỏ phải luôn luôn được cân nhắc và
py = px+1; /*py trỏ đến số nguyên nằm sau x hỏi ý kiến bởi chương trình biên dịch.
trong bộ nhớ*/ int *addr1;
Phép trừ hai con trỏ cùng kiểu được coi là hợp lệ và cho kết char *addr2;
quả là một số nguyên biểu thị "khoảng cách" (ở đây bằng số addr1=0; /*Chương trình dịch sẽ đưa ra
phần tử ) giữa hai biến con trỏ. một warning */
Ví dụ 3.?. Quay về ví dụ trên, câu lệnh addr2 = (char *)0; /*Phép gán này hợp lệ*/
x=py-px; if(addr1==addr2) /*Gây ra một warning*/
gán 1 cho x.
{
Phép cộng hai con trỏ không hợp lệ. Cũng vậy đối với các addr1 = (int *)addr2; /*Hợp lệ*/
phép nhân, chia hai con trỏ.
}
Lập trình C - Việt Nhật 2007 195 Lập trình C - Việt Nhật 2007 196
49
Sử dụng con trỏ như tham số Bộ nhớ
#include <stdio.h>
void move_one(int* xPtr, int* yPtr) M1 main#1::a
{ M2 main#1::b
*xPtr = *xPtr-1;
*yPtr = *yPtr+1;
} M3 move_one#1::xPtr
int main(void) M4 move_one#1::yPtr
{
int a, b;
a=4; b=7;
move_one(&a, &b);
print(“%d, %d\n”, a, b);
return 0;
}
Lập trình C - Việt Nhật 2007 197 Lập trình C - Việt Nhật 2007 198
50
Con trỏ void Con trỏ và mảng một chiều
Kiểu dữ liệu void Xét câu lệnh: int a[10];
Giá trị trả về của hàm: không cần giá trị trả về a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
Tham số trong hàm: không có tham số
Con trỏ kiểu void được khai báo như sau: Tên mảng là một hằng địa chỉ, nó chính là địa chỉ của phần
tử đầu tiên của mảng; điều này có nghĩa là:
void *tên_con_trỏ;
a tương đương với &a[0]
Đây là con trỏ đặc biệt, con trỏ không kiểu, nó có thể nhận
bất kỳ địa chỉ kiểu nào. Chẳng hạn các câu lệnh sau là hợp Nếu pa là một con trỏ kiểu nguyên,
lệ: int *pa;
void *px,*py; Khi đó phép gán pa
int x=1; pa = &a[0];
float y=0.1; Đem pa trỏ đến phần tử a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
Lập trình C - Việt Nhật 2007 201 Lập trình C - Việt Nhật 2007 202
Con trỏ và mảng một chiều Con trỏ và mảng một chiều
pa +i là địa chỉ của a[i] và *(pa+i) là nội dung của a[i] Có sự khác nhau giữa tên của mảng và con trỏ. Một con trỏ là
một biến và vì thế, các câu lệnh pa=a và pa++ là hợp lệ.
pa+9 a[9] Nhưng một tên mảng không phải là một con trỏ, do đó các
câu lệnh kiểu như a=pa;a++ là không hợp lệ. (Về thực chất,
... ...
tên mảng là một hằng con trỏ do đó chúng ta không thể thay
đổi giá trị của nó được).
pa+1 a[1]
Ví dụ : viết chương trình thực hiện các công việc sau:
Đọc từ bàn phím các phần tử của một mảng.
pa a[0] Tính tổng của chúng
Một biểu thức chứa một mảng và một chỉ số tương đương với
một cách viết khác sử dụng một con trỏ cùng một độ lệch.
Lập trình C - Việt Nhật 2007 203 Lập trình C - Việt Nhật 2007 204
51
#include <stdio.h> #include <stdio.h>
#include <stdio.h> #include <stdio.h> main() main()
main() main() { {
{ { float a{5],s, *p;
float a{5],s,*p;
int i; p=a;
float a{5],s; float a{5],s; for(i=0;i<5;i++) int i; p=a;
int i int i [ for(i=0;i<5;i++)
for(i=0;i<5;i++) for(i=0;i<5;i++) printf("\na[%d] = [
[ [ ",i) ;
printf("\na[%d] =
scanf("%f",&p[i]);
printf("\na[%d] = printf("\na[%d] = ",i) ;
}
",i) ; ",i) ; s=0; scanf("%f",p+i);
scanf("%f",&a[i]); scanf("%f",a+i); for(i=0;i<5;i++) }
} } s+=p[i]; s=0;
s=0; s=0; printf("\n tong la: for(i=0;i<5;i++)
%10.2f",s);
for(i=0;i<5;i++) for(i=0;i<5;i++) } s+=*(p+i);
s+=a[i]; s+=a[i]; printf("\n tong la:
printf("\n tong la: printf("\n tong la: %10.2f",s);
%10.2f",s); %10.2f",s); }
} }
Lập trình C - Việt Nhật 2007 205 Lập trình C - Việt Nhật 2007 206
Truyền mảng qua con trỏ Con trỏ và mảng nhiều chiều
1. #include <stdio.h> Phép toán lấy địa chỉ nói chung không dùng được đối với các thành
2. #define SIZE 5 phần của mảng nhiều chiều (trừ trường hợp mảng hai chiều các số
nguyên).
3. void getArray(int *a, int size);
Phép cộng địa chỉ trong mảng hai chiều
4. main() Xét khai báo
5. {int an_array[SIZE]; float a[2][3];
6. getArray(an_array, SIZE); Với khai báo này hệ thống cấp sáu phần tử liên tiếp trong bộ nhớ theo
7. return 0; thứ tự sau:
8. }
a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]
tên của mảng hai chiều được hiểu như địa chỉ của phần tử đầu tiên của
9. void getArray(int *a, int size)
nó; phép cộng địa chỉ phải hiểu như sau: C cho rằng mảng hai chiều là
10. {for(int i=0; i<size; i++) {
mảng của mảng; như vậy với khai báo trên thi a là mảng mà mỗi phần tử
11. printf(“a[%d]=“); của nó là một dãy gồm 3 số thực. Vì vậy
12. scanf(“%d”, &a[i]);
a trỏ tới đầu hàng thứ nhất (phần tử a[0][0]
13. }
a+1 trỏ tới đầu hàng thứ hai (phần tử a[1][0]
14. }
Lập trình C - Việt Nhật 2007 207 Lập trình C - Việt Nhật 2007 208
52
Con trỏ và mảng hai chiều Tính địa chỉ từng phần tử
Để truy xuất vào các phần tử của mảng hai chiều ta Để ý rằng, a là một hằng con trỏ trỏ đến các dòng của một ma trân
hai chiều, vì vậy
vẫn có thể dùng con trỏ theo cách sau:
a trỏ đến dòng thứ nhất
float *p, a[2][3]; a+1 trỏ đến dòng thứ hai
p=(float*)a;
Để tính toán được địa chỉ của phần tử ở dòng i cột j chúng ta phải
Khi đó dùng phép chuyển đổi kiểu bắt buộc đối với a: (float * )a, đây là
p trỏ tới a[0][0] con trỏ trỏ đến thành phần a[0][0] của ma trận. Và vì vậy thành
phần a[i][j] sẽ có địa chỉ là (float *a) +i*n+j (n là số cột).
p+1 trỏ tới a[0][1]
p +2 trỏ tới a[0][2] Một cách tổng quát, nếu mảng có kiểu type và có kích thước các
chiều tương ứng là n1,n2,..,nk (Giả sử mảng a có k chiều). Địa chỉ
p +3 trỏ tới a[1][0] cuả thành phần a[0]..[0] (k chỉ số 0) là (type *)a, và địa chỉ của
p +4 trỏ tới a[1][1] a[i1][i2]...[ik] được tính như sau
k −1 k
p +5 trỏ tới a[1][2] (type*)a + ∑ ij ∏ nl + ik
j =1 l = j +1
Lập trình C - Việt Nhật 2007 209 Lập trình C - Việt Nhật 2007 210
Ví dụ: Đọc dữ liệu cho mảng hai chiều Mảng các con trỏ
#include <stdio.h> Chúng ta có thể khai báo một mảng các con trỏ bằng câu
main()
{ lệnh sau:
float a[2][3],*p; type *pointer_array[size];
int i,m,n;
p=(float*)a;
ví dụ câu lệnh,
for(i=0;i<2;i++) char *ma[10];
for(j=0;j<3;j++)
Sẽ khai báo một mảng 10 con trỏ char có thể được dùng để
{
printf("a[%d][%d] = ",i,j); khai báo một mảng để lưu trữ địa chỉ của mười chuỗi ký tự
scanf("%f",p+i*3+j); nào đó.
} Nếu các con trỏ được chuẩn bị để chỉ đến một biến nào đó đã
for(i=0;i<2;i++)
for(j=0;j<3;j++) có, thì như vậy, chúng ta có thể truy xuất được các biến này
{ thông qua một mảng mà không cần đến vị trí thực sự của các
printf("%6.2f",a[i][j]); biến đó có liên tiếp hay không.
if(j==2) printf("\n");
}
}
Lập trình C - Việt Nhật 2007 211 Lập trình C - Việt Nhật 2007 212
53
Ví dụ: sắp xếp thông qua con trỏ
Xem xét một mảng các con trỏ ptr_array được gán các địa chỉ /*Gán các thành phần của mảng*/
ptr_array[0]=&a;
của các biến int có giá trị và vị trí bất kỳ. Chúng ta sẽ dùng một
ptr_array[1]=&b;
hàm để sắp xếp lại các địa chỉ này trong mảng để sao cho các ptr_array[2]=&c;
địa chỉ của các số bé được xếp trước địa chỉ của các số lớn hơn. ptr_array[3]=&d;
Không làm thay đổi vị trí hoặc thay đổi các giá trị của các biến ptr_array[4]=&e;
nhưng mảng vẫn có vẻ như là một mảng chỉ đến các giá trị đã ptr_array[5]=&f;
sắp xếp có thứ tự. /*Sắp xếp lại dãy số*/
for(i=0;i<5;i++)
for(j=i+1;j<6;j++)
#include <stdio.h> if(*ptr_array[i]>*ptr_array[j])
main() { {
x=ptr_array[i];
int i,j,*x; ptr_array[i]=ptr_array[j];
int d=10,e=3,f=7; ptr_array[j]=x;
}
int a=12,b=2,c=6;
/*In kết quả sau khi sắp xếp*/
int *ptr_array[6]; for(i=0;i<6;i++)
printf(" %d \n",*ptr_array[i]);
}
Lập trình C - Việt Nhật 2007 213 Lập trình C - Việt Nhật 2007 214
Lập trình C - Việt Nhật 2007 215 Lập trình C - Việt Nhật 2007 216
54
Cấp phát động Ví dụ
#include <stdio.h>
Hàm malloc trong thư viện < stdlib.h>, <alloc.h> #include <string.h>
Cú pháp khai báo #include <alloc.h>
#include <process.h>
(type*)malloc (số ô nhớ cần cấp phát);
main()
Hàm malloc dùng để xin cấp phát một vùng bộ nhớ (với kích {
thước được xác định khi gọi hàm) từ vùng nhớ heap. Hàm này char *str;
/* cấp phat bộ nhớ cho string */
cho phép một chương trình xin cấp phát một vùng bộ nhớ đúng if ((str = (char *) malloc(10)) == NULL)
với kích thước mà chương trình cần. {
Giá trị trả về: printf("Not enough memory to allocate buffer\n");
exit(1); /* Kết thúc chương trình trong trường hợp tràn
Trong trường hợp cấp phát thành công, hàm malloc trả về một con bộ nhớ*/
trỏ tới khối nhớ mới được cung cấp. }
Trong trường hợp có lỗi (không đủ bộ nhớ để cấp phát, malloc trả về /* copy "Hello" into string */
strcpy(str, "Hello");
giá trị NULL.
/* display string */
Nếu tham số size==0, malloc trả về con trỏ NULL printf("String is %s\n", str);
/* free memory */
free(str);
}
Lập trình C - Việt Nhật 2007 217 Lập trình C - Việt Nhật 2007 218
Lập trình C - Việt Nhật 2007 219 Lập trình C - Việt Nhật 2007 220
55
Cấp phát động
Hàm farrealloc, realloc (thư viện <alloc.h>, <stdlib.h>) #include <stdio.h>
Cú pháp khai báo #include <alloc.h>
(datatype*)realloc(buf_ptr,newsize ); #include <string.h>
int main(void)
void far *farrealloc(void far *oldblock, unsigned long
nbytes); {
char *str;
Trong đó buf_ptr là con trỏ đang chỉ tới vùng ô nhớ được cấp phát từ
/* allocate memory for string */
trước, còn newsize là kích thước mới càn cấp phát; như vậy hàm này
điều chỉnh lại kích thước của khối nhớ block thành size, sao chép nội str = (char *) malloc(10);
dung vùng nhớ cũ vào vùng mới nếu thấy cần thiết /* copy "Hello" into string */
Hàm farrealloc chỉnh lại kích thước của khối nhớ block thành nbytes, strcpy(str, "Hello");
sao chép nội dung của vùng nhớ cũ vào vùng nhớ mới nếu vùng nhớ printf("String is %s\n Address is %p\n", str, str);
mới được cấp phát lại không cùng địa chỉ với vùng trước cấp phát. str = (char *) realloc(str, 20);
Giá trị trả về: printf("String is %s\n New address is %p\n", str,
str);
Trong trương hợp thành công, cả hai hàm trả về địa chỉ của khối mới /* free memory */
được cấp phát lại, địa chỉ mới có thể khác so với địa chỉ của khối ban free(str);
đầu.
return 0;
Trong trường hợp thất bại, cả hai hàm trả về NULL. }
Lập trình C - Việt Nhật 2007 221 Lập trình C - Việt Nhật 2007 222
56
Ký tự (character) Nhập xuất Ký tự
Kiểu char: scanf
ký tự “in được” gồm: 26 chữ thường (a..z), 26 chữ hoa (A..Z), 10 char ch;
chữ số (0..9), khoảng trắng, các ký tự: scanf(“%c”, &ch);
!“#$%&‘()*+,-./:;<=>?@[\]^_{|}~
Các ký tự “không in được”: tab, lert (bell), newline, formfeed,... sử dụng các đoạn macro có trong thư viện <stdio.h>
putchar: đưa ký tự ra thiết bị xuất chuẩn (stdout)
các ký tự “in được” đặc biệt: ‘\\’, ‘\’’, ‘\”’ putchar(‘\n’);
getchar: lấy ký tự từ thiết bị nhập chuẩn (stdin)
các ký tự “không in được” đặc biệt: ch = getchar();
\n new line
\a bell getch: lấy trực tiếp ký tự từ bàn phím không hiển thị ra màn hình
\0 null character ch = getch();
\b backspace getche(): lấy trực tiếp ký tự từ bàn phím và hiển thị ký tự ra màn
\t horizontal tab hình.
... ch = getche();
Lập trình C - Việt Nhật 2007 225 Lập trình C - Việt Nhật 2007 226
getchar putchar
1. #include <stdio.h> 1. /* putchar example */
2. #include <stdio.h>
57
Một số hàm khác
putchar(RIGHT_TOP);
17.
18. putchar('\n');
kbhit: kiểm tra có phím bấm
Lập trình C - Việt Nhật 2007 231 Lập trình C - Việt Nhật 2007 232
58
Xâu ký tự Khai báo và khởi tạo một xâu
Một xâu là một mảng một chiều các ký tự Dưới dạng một mảng các ký tự:
char cay[6]={‘m’,’i’,’t’,’\0’}; char ten[10]={‘h’,’o’,’a’,’h’,’o’,’n’,’g’,’\0’};
printf(“Trong vuon co cay %s\n”, cay);
Một xâu cũng chứa một con trỏ trỏ tới ký tự đầu tiên char ten[10];
ten[0]=‘h’; ten[1]=‘o’; ten[2]=‘a’;
ten[3]=‘h’; ten[4]=‘o’; ten[5]=‘n’; ten[6]=‘g’;
chính xác hơn xâu là mảng một chiều các ký tự kết thúc bằng ten[7]=\0’;
ký tự null (null-terminated array of char)
char ten[10]=“hoahong”;
cay
‘m’ ‘i’ ‘t’ ‘\0’ char ten[]=“hoahong”; // Tạo mảng 8 phần tử. Ký tự cuối cùng là ‘\0’
Dưới dạng con trỏ
char *colorptr = “blue”; // Tạo con trỏ colorptr trỏ đến xâu “blue”
cay[0] cay[4] nằm đâu đó trong bộ nhớ
Lập trình C - Việt Nhật 2007 233 Lập trình C - Việt Nhật 2007 234
Khai báo dưới dạng mảng ký tự Lỗi khi tạo một chuỗi
Ký tự Chỉ có thể chứa
kết thúc:
tối
• Đánh dấu đakết
4 ký tự,xâu
thúc Chú ý: không có phép gán trong kiểu dữ liệu chuỗi
Khai báo 1: do `\0’
• ký tự đặc biệt: ’\0’ như thế này là sai
char name[5] = “Ann”;
• aka NUL (single L) char ten[10];
ten = “hoahong”;
name A n n \0
is 0x2000
0x2000 0x2004
Lập trình C - Việt Nhật 2007 235 Lập trình C - Việt Nhật 2007 236
59
Chú ý Ký tự trong xâu
0x3995 0x399C
Không :
sử dụng toán tử gán = để chép nội dung của một chuỗi sang
chuỗi khác. name J o h n \0
char a[4]=“hi”; is 0x3995
char b[4];
b = a; //???
index 0 index 4
Không:
dùng toán tử == để so sánh nội dung hai chuỗi char name[8] = “John”;
char a[] = “hi”; int i = 2;
char b[] = “there”;
printf(“Ky tu tai chi so %d la %c.\n”, i, name[i]);
if(a==b) //???
{}
Ket qua: Ky tu tai chi so 2 la h.
Lập trình C - Việt Nhật 2007 237 Lập trình C - Việt Nhật 2007 238
index 2
name[2] = ‘X’;
printf(“Ten: %s\n”, name);
Lập trình C - Việt Nhật 2007 239 Lập trình C - Việt Nhật 2007 240
60
Danh sách các hàm Làm việc với chuỗi
Prototype Description
int isdigit( int c ) Trả về true nếu c là một chữ số và false nếu không phải #include <string.h>
int isalpha( int c ) Trả về true nếu c là một chữ cái và false nếu không phải
int isalnum( int c ) Trả về true nếu c là một chữ số hay chữ cái và false nếu không
int isxdigit( int c ) Trả về true nếu c là một chữ số hệ 16 và false nếu không Với hàm scanf và printf
int islower( int c ) Trả về true nếu c là một chữ thường và false nếu không phải
int isupper( int c ) Trả về true nếu c là một chữ hoa và false nếu không phải
int tolower( int c ) Trả về chữ thường nếu c là một chữ hoa và không thay đổi 1. #include <stdio.h>
trong trường hợp ngược lại
int toupper( int c ) Trả về chữ hoa nếu c là một chữ thường và không thay đổi
2. void main (void)
trong trường hợp ngược lại
3. {
int isspace( int c ) Trả về true nếu c là các ký tự trắng, xuống dòng ‘\n’, ‘\t’,
‘\r’ ‘\v’ 4. char name[20];
int iscntrl( int c ) Trả về true nếu c là một ký tự điều khiển false nếu không phải 5. printf (“Enter a name: “);
int ispunct( int c ) Trả về true nếu c là một ký tự in trừ chữ số, chữ cái và ký tự
trắng và false nếu không phải
6. scanf (“%s”, name); // there is no & before name
int isprint( int c ) Trả về true nếu c là một ký tự in tính cả ký tự trắng và false 7. printf (“Hello %s\n”, name);
nếu không phải
int isgraph( int c ) Trả về true nếu c là một ký tự in không tính ký tự trắng và 8. }
false nếu không phải
Lập trình C - Việt Nhật 2007 241 Lập trình C - Việt Nhật 2007 242
61
Ví dụ Lỗi thường gặp 1
#include <stdio.h>
#include <string.h>
Ghép chuỗi src vào cuối chuỗi dest. Chiều dài chuỗi kết quả là
char name[] = “Ann”; strlen(dest) + strlen(src)
strcpy(name, “David”);
1. char destination[25];
2. char *blank = " ", *c = “C++“;
3. char *turbo = "Turbo";
62
So sánh chuỗi: strcmp()
Không đủ không
int strcmp(const char *s1, const char*s2);
gian lưu trữ ký tự
char name[5]; kết quả: số âm nếu s1 < s2
0 nếu s1 == s2
strcpy(name, “Ann”); số dương nếu s1 > s2
strcat(name, “ Smith”);
Hàm strncmp()
0x2000 0x2004
Lập trình C - Việt Nhật 2007 249 Lập trình C - Việt Nhật 2007 250
f = atof(str);
printf("string = %s float = %f\n", str, f);
...
Lập trình C - Việt Nhật 2007 251 Lập trình C - Việt Nhật 2007 252
63
Nhập/xuất chuỗi Chuỗi ký tự với tư cách tham số hàm
gets: lấy chuỗi ký tự từ thiết bị nhập chuẩn stdin Khi làm tham số hình thức, xâu được khai báo
puts: đưa chuỗi ký tự ra thiết bị xuất chuẩn stdout
dưới dạng
char *gets(char *s); char* hoặc char[]
int puts(const char *s); Ví dụ:
void Greet ( char* name )
void Greet ( char name[] )
vd:
1. char string[80];
Những thay đổi xâu trong hàm sẽ có tác động
2. printf("Input a string:"); toàn cục.
3. gets(string);
4. printf("The string input was: %s\n", string);
5. puts(string);
Lập trình C - Việt Nhật 2007 253 Lập trình C - Việt Nhật 2007 254
Ví dụ
#include <stdio.h>
#include <stdio.h>
#include <string.h> int main()
#include <string.h> int main()
#define NAMELEN 50 {
#define NAMELEN 50 {
char user[NAMELEN];
char user[NAMELEN];
/* Hien thi loi chao don gian */
/* Hien thi loi chao den nguoi dung */
printf("Who are you? ");
printf("Who are you? ");
void Greet ( char * name ) scanf("%s", user);
void Greet ( char * name ) scanf("%s", user);
{ Greet(user);
{ Greet(user);
strcat(name, "! How are ya?"); printf("%s\n", user);
strcat(name, "! How are ya?"); printf("%s\n", user);
}
}
return 0;
return 0;
}
}
name user
user
Jake\0 Jake\0
Lập trình C - Việt Nhật 2007 255 Lập trình C - Việt Nhật 2007 256
64
#include <stdio.h> #include <stdio.h>
#include <string.h> int main() #include <string.h> int main()
#define NAMELEN 50 { #define NAMELEN 50 {
char user[NAMELEN]; char user[NAMELEN];
/* Print a simple greeting to /* Print a simple greeting to
the user */ printf("Who are you? "); the user */ printf("Who are you? ");
scanf("%s", user); scanf("%s", user);
void Greet ( char * name ) Greet(user); void Greet ( char * name ) Greet(user);
{ printf("%s\n", user); { printf("%s\n", user);
strcat(name, "! How are ya?"); strcat(name, "! How are ya?");
} return 0; } return 0;
} }
Lập trình C - Việt Nhật 2007 259 Lập trình C - Việt Nhật 2007 260
65
Khái niệm và định nghĩa
¾ Trong thực tế lập trình, chúng ta có thể sẽ cần đến những
Ngôn ngữ lập trình C kiểu dữ liệu phức tạp hơn được tạo thành từ những kiểu dữ
liệu đơn giản mà chúng ta đã biết.
¾ Mảng lưu giữ các phần tử cùng kiểu
¾ Có những lúc ta muốn lưu giữ đồng thời các dữ liệu khác
kiểu.
¾ Ví dụ:
KIỂU CẤU TRÚC (STRUCTURE) ¾ Tiêu đề, tên nghệ sỹ, giá bán của các CD trong cửa hàng
¾ Tên người và số điện thoại
¾ Tên học sinh, ID, và điểm số….
Cao Tuấn Dũng
¾ Cấu trúc là một kiểu dữ liệu bao gồm nhiều thành phần có thể
2007
thuộc nhiều kiểu dữ liệu khác nhau. Các thành phần được
truy nhập thông qua tên.
Lập trình C - Việt Nhật 2007 263 Lập trình C - Việt Nhật 2007 264
66
Định nghĩa kiểu bằng typedef Định nghĩa cấu trúc bằng typedef
typedef kiểu_đã_có tên_kiểu_mới; typedef struct
{
trong đó : tên kiểu tên trường_1 ;
tên kiểu tên trường_2 ;
kiểu_đã_có là kiểu dữ liệu mà ta muốn đổi tên. tên kiểu tên trường_3 ;
tên_kiểu_mới là tên mới mà ta muốn đặt. << ... >>
} tên kiểu cấu trúc ;
Xét câu lệnh sau
typedef unsigned char byte; typedef struct {
char ho_ten[20];
sau câu lệnh này, byte được xem như là kiểu dữ liệu float diem;
tương đương với unsigned char và có thể được sử dụng } hoc_sinh;
trong các khai báo biến như các kiểu dữ liệu khác.
typedef struct
{
char ten[DODAI];
long dienthoai;
char diachi[KICHTHUOC];
} Ban;
Lập trình C - Việt Nhật 2007 265 Lập trình C - Việt Nhật 2007 266
Định nghĩa Cấu trúc Kích thước của một cấu trúc
1. struct SinhVien Kích thước kiểu dữ liệu cấu trúc:
2. {
3. char *hoten;// toi da 30 ky tu sizeof(<<typename>>)
4. int namsinh; // >= 1960
vd:
5. char *noisinh; // 3 chu cai viet tat cua noi sinh
6. char *maso; // maso dai toi da 10 ky tu SinhVien_t_size = sizeof(SinhVien); // 48
7. };
Lập trình C - Việt Nhật 2007 267 Lập trình C - Việt Nhật 2007 268
67
Khai báo biến cấu trúc Khai báo biến cấu trúc (tt)
Khai báo biến cùng định nghĩa cấu trúc 1. struct SinhVien
struct hoc_sinh { 2. {
char ho_ten[20]; 3. char hoten[31]; // toi da 30 ky tu
float diem;
4. int namsinh; // >= 1960
} hs,dshs[100];
5. char noisinh[4];// 3 chu cai viet tat cua noi sinh
Khai báo sau khi định nghĩa kiểu bằng từ khóa struct
struct hoc_sinh VietNhat101, dssv[50]; 6. char maso[11]; // maso dai toi da 10 ky tu
7. } X, Y, Z;
Khai báo biến cùng định nghĩa typedef
typedef struct { 8. Khởi tạo
char ho_ten[20]; 9. struct SinhVien NguyenLe = (“Nguyen Le”, 1983, “HU”,
float diem; “301160123”);
} hoc_sinh;
Khai báo biến sau định nghĩa kiểu bằng từ khóa typedef 10. Z = NguyenLe;
hoc_sinh hs,*ptr_hocsinh;
Lập trình C - Việt Nhật 2007 269 Lập trình C - Việt Nhật 2007 270
Khi khai báo biến cấu trúc, nếu Toán tử thành viên: .
X.hoten // “Nguyen Van X”
X.namsinh // 1983
khai báo nhiều giá trị khởi tạo hơn số trường của kiểu
dữ liệu Æ sai. gets(X.hoten);
scanf(“%d %s %s”, &X.namsinh, X.noisinh,
X.maso);
khai báo giá trị khởi tạo cho một số trường trong cấu Có thể thực hiện phép gán biến cấu trúc này sang biến cấu
trúc, các trường còn lại sẽ tự động được gán giá trị 0. trúc khác.
X = NguyenLe;
Thực chất đây là phép gán từng bit.
Tuy nhiên với các cấu trúc có mảng ở trong, nên dùng cách
chép từng thành phần.
Lập trình C - Việt Nhật 2007 271 Lập trình C - Việt Nhật 2007 272
68
Truy cập qua con trỏ Truy cập thành phần cấu trúc
Khai báo con trỏ đến cấu trúc tương tự như các kiểu dữ liệu struct friendStr
khác. {
char name[MAXNAME];
long int phoneNumber;
SinhVien *SV_ptr;
char street[MAXSTREET];
};
Với một con trỏ, các thành phần của cấu trúc có thể được struct friendStr sarah;
truy nhập thông qua toán tử -> Hoặc
SV_ptr->namsinh = 1988; sarah.phoneNumber = 55559999;
strcpy(sarah.name,"Sarah Finch");
strcpy(SV_ptr->noisinh, "Ha noi");
strcpy(sarah.street,"Firthsmith St");
Hoặc
scanf("%s",sarah.name);
scanf("%ld",&sarah.phoneNumber);
scanf("%s",sarah.street);
Lập trình C - Việt Nhật 2007 273 Lập trình C - Việt Nhật 2007 274
Ví dụ
#include <stdio.h>
Chú ý
#define MAXLEN 50
struct Sinhvien
{ Không dùng phép so sánh == với hai cấu trúc
char ten[MAXLEN];
float diem; Chúng ta chỉ có thể so sánh các thành phần của cấu trúc
};
69
Truyền cấu trúc như là tham số Truyền bằng tham trị
Như một biến bình thường biến cấu trúc có thể
được truyền bằng:
void hienthiCautruc ( Sinhvien sv )
- tham trị: Không làm thay đổi cấu trúc {
printf("Ten\t: %s\n", sv.ten);
printf("Diem\t: %.1f\n\n", sv.diem);
}
- tham biến thông qua con trỏ: thay đổi nội
dung cấu trúc main()
{
Sinhvien svA = {“Dao Xuan Vu”, 9.0};
Truy nhập nội dung: (*sptr).item
hienthiCautruc(svA);
Đọc qua địa chỉ: &((*sptr).item)
}
Lập trình C - Việt Nhật 2007 277 Lập trình C - Việt Nhật 2007 278
Lập trình C - Việt Nhật 2007 279 Lập trình C - Việt Nhật 2007 280
70
Mảng cấu trúc
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 50
dssv #define MAXN 20
id:VN006 dssv[0]chỉ đến typedef struct {
0 char ten[MAXLEN];
toàn thể struct float diem;
name: "Mai"
} Sinhvien;
Union
int main()
{
int count = 0;
Sinhvien dssv[MAXN];
int i; Khai báo union được dùng để khai báo các biến dùng chung
printf("Co bao nhieu sinh vien? ");
scanf("%d", &count); bộ nhớ.
if (count > MAXN)
{ union int_or_long {
printf("Khong du bo nho luu tru.\n");
exit(1); int i;
} long l;
for (i=0; i < count; i++)
} a_number;
{
dssv[i] = readRecord(); Các thành phần của union có thể không có kích thước bằng
}
printf("\nDanh sach lop:\n\n"); nhau.
for (i=0; i < count; i++)
{
printRecord(dssv[i]); Kích thước bộ nhớ trong khai báo union là kích thước của
} kiểu dữ liệu lớn nhất có trong khai báo union.
return 0;
}
Lập trình C - Việt Nhật 2007 283 Lập trình C - Việt Nhật 2007 284
71
Ví dụ
1. union int_or_long {
2.
3.
int
long
i;
l; Ngôn ngữ lập trình C
4. } a_number;
5. a_number.i = 5;
6. a_number.l = 100L;
7. // anonymous union
8. union { TẬP TIN (FILE)
9. int i;
10. float f;
11. }; Cao Tuấn Dũng
12. i = 10; 2007
13. f = 2.2;
72