SlideShare a Scribd company logo
1 of 72
Download to read offline
1
Ngôn ngữ lập trình C
Chương 1: Giới thiệu chung
Cao Tuấn Dũng
2007
Lập trình C - Việt Nhật 2007 2
Tài liệu tham khảo
The C Programming Language (2nd Edition) Kernighan & Ritchie
Prentice Hall 1988.
Ngôn Ngữ Lập Trình C. Quách Tuấn Ngọc. Nhà Xuất Bản Giáo Dục,
1998.
Efficient C programming. Mark Allen Weiss. Prentice Hall, 1998.
Kỹ thuật lập trình C, cơ sở và nâng cao. Phạm Văn Ất. Nhà Xuất
Bản Khoa Học & Kỹ Thuật.
Lập trình C - Việt Nhật 2007 3
Một số khái niệm
Computer program –chương trình máy tính là một tập các câu
lệnh (instruction) hướng dẫn máy tính làm một số việc nhất
định.
Programming language - Ngôn ngữ lập trình là ngôn ngữ để
viết chương trình. Có nhiều loại ngôn ngữ lập trình.
Compiler – trình biên dịch, là phần mềm chịu trách nhiệm dịch
chương trình viết bằng một ngôn ngữ lập trình sang dạng mã
máy.
Lập trình C - Việt Nhật 2007 4
MACHINE CODE
ASSEMBLER LANGUAGES
HIGH-LEVEL
LANGUAGES
ForTran, COBOL, C, C++,
LISP, Pascal, Java, ...
4GLs ORACLE, SEQUEL, INGRES, ...
5GLs artificial intelligence
Các lớp Ngôn ngữ lập trình
2
Lập trình C - Việt Nhật 2007 5
Thuật toán - Algorithm
Tập các lệnh được tổ chức có thứ tự nhằm giải quyết một bài
toán hoặc đạt đến một mục tiêu nào đó.
Ví dụ:
hướng dẫn chế biến một món ăn,
hướng dẫn sửa chữa xe máy,
cách giải một bài toán.
…
Algorithm –Thuật toán - Thuật giải
Lập trình C - Việt Nhật 2007 6
Thuật giải tốt
Một thuật giải tốt là thuật giải:
chính xác
rõ ràng
đúng
hiệu quả
và có thể bảo trì được.
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 7
Tính điểm trung bình môn học
Nhập: điểm thực hành Vật Lý, điểm bài tập, điểm bài kiểm tra
giữa học kỳ, điểm bài kiểm tra cuối học kỳ.
Điểm hệ số
Thực hành : 8 2
bài tập: 9 2
KT giữa kỳ: 8 4
KT cuối kỳ: 8 6
Tổng cộng: TONG = 8*2 + 9*2 + 8*4 + 8*6
Điểm trung bình: TB = TONG/(2+2+4+6)
Lập trình C - Việt Nhật 2007 8
Sơ đồ xử lý
Sử dụng sơ đồ xử lý để minh họa quá trình xử lý một chương
trình.
start,stop
condition expression
process
data
flow
Bài tập: dùng sơ đồ để biểu diễn bài toán nhập và tính điểm
trung bình.
3
Lập trình C - Việt Nhật 2007 9
Ngôn ngữ C - Lịch sử phát triển
C được ra đời tại Bell Lab vào 1972, cha đẻ là Dennis Ritchie.
C và mối liên hệ với hệ điều hành Unix
Kernighan và Ritchie đưa ra phiên bản C chuẩn đầu tiên năm
1978.
ANSI C - C được chuẩn hóa vào năm 1989 bởi American
National Standards Institute.
Lập trình C - Việt Nhật 2007 10
Ngôn ngữ C – Đặc điểm chính
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.
Linh hoạt và mạnh
Bản thân ngôn ngữ không đặt ra giới hạn đối với người lập trình
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
Tính khả chuyển cao
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 11
Ngôn ngữ lập trình C
có thể đọc và viết mã chương trình trên hầu hết các hệ thống.
chuyển lên C++ và có thể viết các kịch bản CGI (CGI script)
cho các Website.
C là ngôn ngữ biên dịch (complied language).
Viết chương trình bằng ngôn ngữ C bằng
các chương trình soạn thảo (emacs, vi,
các công cụ viết chương trình)
Không dùng các chương trình soạn thảo
văn bản (vd:Word, WordPad)
Hello
C
Compiler
Lập trình C - Việt Nhật 2007 12
Cấu trúc một chương trình C
Khai báo tệp tiêu đề
#include <stdio.h>
#include <math.h>
Khai báo các cấu trúc, biến,
hàm có sử dụng đến
Hàm chính main()
- Điểm bắt đầu của chương
trình
- Chỉ có duy nhất
- Từ hàm main thường gọi
đến các chương trình con
(hàm) khác.
1 /* Fig. 2.1: fig02_01.c
2 A first program in C */
3 #include <stdio.h>
4
5 int main()
6 {
7 printf( "Welcome to C!n" );
8
9 return 0;
10 }
- Các chú thích nằm trong /*….*/
- Câu lệnh kết thúc bằng dấu ;
- { } mở và đóng một khối lệnh
4
Lập trình C - Việt Nhật 2007 13
Giới thiệu chung về Linux
Linux là phiên bản phát triển có mã nguồn mở của hệ điều
hành Unix
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
của hệ điều hành (kernel), cung cấp các dịch vụ truy nhập
quản lý tài nguyên phần cứng cho người sử dụng và các
chương trình ứng dụng
Các bản phân phối Linux: RedHat, SuSE, Debian, Mandrake.
Linux kế thừa các ưu điểm của dòng hệ điều hành Unix: môi
trường đa người dùng, đa nhiệm, chạy trên nhiều platform
khác nhau.
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 14
Hệ thống file trên Linux
Trong Linux, các tập tin được
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 phân biệt chữ hoa, chữ
thường đối với tên file
Mỗi người sử dụng khi được
tạo tài khoản sẽ có một thư mục,
gọi là thư mục home. Ký hiệu ~.
Thông thường người dùng
userid sẽ có thư mục home là
/home/userid
Lập trình C - Việt Nhật 2007 15
Một số lệnh cần thiết
Đổi mật khẩu tài khoản: passwd
Hiện đường dẫn tới thư mục hiện hành: pwd
Liệt kê danh sách các file trong thư mục: ls tên_thư_mục
ls –l examples xem nội dung thư mục con examples của thư mục hiện
hành theo từng trang.
Tạo thư mục : mkdir tên_thư_mục
Chuyển tới thư mục mới : cd đường_dẫn
Xem nội dung file: cat tên_file
Xóa một thư mục hay file: rm
Sao chép file: cp nguồn đích
Xem hướng dẫn sử dụng một lệnh:
man tên_lệnh. Ví dụ man mv
lệnh --help
Tìm thư mục chứa lệnh: which tên_lệnh which gcc
Thoát khỏi hệ thống: logout
Lập trình C - Việt Nhật 2007 16
Trình soạn thảo emacs
Linux tồn tại sẵn một số lệnh soạn thảo tập tin văn bản như
lệnh vi.
emacs (editor macros): trình soạn thảo văn bản đa chức
năng. Hai phiên bản phổ biến: GNU emacs và xemacs
Dùng ngôn ngữ emacs lisp với khả năng mở rộng: soạn thảo,
căn lề, nhận biết tag, gọi chương trình dịch, thậm chí duyệt
Web.
emacs cho phép làm việc trên nhiều cửa sổ và bộ đệm. Nội
dung soạn thảo không ghi trực tiếp vào file mà ghi lên bộ
đệm.
emacs làm việc theo lệnh. Người sử dụng gõ lệnh trên cửa sổ
lệnh để tác động lên nội dung trong bộ đệm
5
Lập trình C - Việt Nhật 2007 17
Các lệnh cơ bản trong emacs
Quy ước:
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
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.
Trở về trạng thái chờ lệnh: C-g
Thao tác với file:
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.
Lưu nội dung bộ đệm vào file: C-x C-s
Lưu với tên khác: C-x C-w tên
Di chuyển con trỏ
Sử dụng các phím mũi tên
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
Về đầu dòng C-a, về cuối dòng C-e
Lập trình C - Việt Nhật 2007 18
Các lệnh trong emacs (tiếp)
Di chuyển con trỏ theo từ:
Về sau: M-f. Về trước M-b
Theo câu: Chú ý emacs phân biệt câu với dòng.
Về đầu : M-a, về cuối câu: M-e.
Theo bộ đệm: Về đầu: M-v. Về cuối: C-v
Thao tác soạn thảo, cắt dán
Xóa ký tự: Del hoặc C-d
Xóa từ con trỏ đến hết từ: M-d. Xóa từ con trỏ đến hết dòng: C-k
Xóa một dòng: C-a C-k
Chọn tất cả: C-x h. Undo : C-x u
M-w: copy vùng được đánh dấu.
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 19
Các lệnh trong Emacs
Tìm kiếm trong văn bản:
Tìm xuôi chiều C-s từ_cần_tìm
Tìm ngược chiều C-r từ_cần_tìm
Soạn thảo trên nhiều cửa sổ
Cho phép theo dõi nội dung ở những phần khác nhau của file
C-x-2 Chia hai cửa sổ theo chiều ngang
C-x-3 Chia hai cửa sổ theo chiều dọc
C-x-0 di chuyển giữa hai cửa sổ
C-x-1 Xóa tất cả các cửa sổ và về lại cửa sổ đầu tiên
Thoát khỏi Emacs
C-x-c
Lập trình C - Việt Nhật 2007 20
Biên dịch chương trình C trên Linux
Hầu hết các bản phân phối của Linux đều tích hợp với trình
biên dịch ngôn ngữ C: gcc.
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,
sau đó liên kết mã đối tượng để tạo ra tập tin thực thi
Nếu không chỉ định tên tập tin thực thi, mặc định kết quả sẽ là
a.out
Các tham số biên dịch
-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
-o: chỉ định tên file thực thi mong muốn
-g: thêm thông tin gỡ rối (debug)
-l: chỉ định thư viện liên kết.
gcc –Wall hello.c –o runhello
./runhello
6
Lập trình C - Việt Nhật 2007 21
Biên dịch chương trình C trên Linux
Các tập tin makefile cho phép:
Tạo các script biên dịch, tiết kiệm thời gian gõ lệnh
Tiết kiệm thời gian biên dịch vì chỉ dịch lại những tập tin có thay
đổi
Rất phức tạp và dễ gây lỗi.
Có thể sử dụng lại
EXEC_NAME = helloWorld
all: main.o
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 22
Một số lỗi thường gặp khi biên dịch
Segfaults
Khi có lỗi truy nhập bộ nhớ. Ví dụ: cố gắng truy nhập thông tin từ
một con trỏ NULL.
Linker errors
Lỗi trong dòng lệnh gcc hay makefile
Multiple definitions of functions
Định nghĩa sai trong cái chỉ thị tiền xử lý trong tập tin header như:
#ifndef, #define
Ngôn ngữ lập trình C
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
Cao Tuấn Dũng
2007
Lập trình C - Việt Nhật 2007 24
Chương trình C đầu tiên
1. #include <stdio.h>
2.
3. int main()
4. {
5. printf(“Hellon");
6. return 0;
7. }
7
Lập trình C - Việt Nhật 2007 25
Chương trình C
#include <stdio.h>
khai báo sử dụng thư viện xuất/nhập chuẩn (standard I/O library).
Các thư viện khác: string, time, math…
int main()
khai báo hàm main(). Chương trình C phải khai báo (duy nhất)
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().
{ … }
mở và đóng một khối mã.
printf
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)
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.
Lập trình C - Việt Nhật 2007 26
Mở rộng 1
1. #include <stdio.h>
2.
3. int main()
4. {
5. int a, b, c;
6. a = 5;
7. b = 7;
8. c = a + b;
9. printf(“%d + %d = %dn“, a, b, c);
10. return 0;
11. }
Lập trình C - Việt Nhật 2007 27
Tên (Định danh)
Tên dùng để xác định các đối tượng khác nhau trong chương
trình
Tên biến, tên hàm, tên hằng, tên nhãn.
Quy tắc đặt tên:
Bao gồm các chữ cái, chữ số, dấu gạch dưới
Không được bắt đầu bằng chữ số
Phân biệt chữ hoa chữ thường.
Không trùng với các từ khóa.
Tên đúng: sothu_3, tigiack
Tên sai:x^2, y-2, 23ant
Lập trình C - Việt Nhật 2007 28
Biến (variable)
Biến tương ứng với một vùng trống nào đó trong bộ nhớ máy tính
dùng để giữ các giá trị. Là đại lượng mà giá trị của nó có thể thay đổi trong
quá trình tính toán.
Một biến luôn có: tên, kiểu, kích thước, giá trị.
Việc đọc giá trị một biến không làm thay đổi giá trị của nó
Khai báo: <type> <var-name>;
ví dụ:
int b;
float m_1, m_2;
Gán giá trị vào biến:
<var-name> = <value>;
vd: b = 5;
Sử dụng biến:
printf(“%d + %d = %dn“, a, b, c);
Biến
nguyên
45
8
Lập trình C - Việt Nhật 2007 29
Mở rộng 2
1. #include <stdio.h>
2.
3. int main()
4. {
5. int a, b, c;
6. printf(“Nhap so thu nhat: “);
7. scanf(“%d”, &a);
8. printf(“Nhap so thu hai: “);
9. scanf(“%d”, &b);
10. c = a + b;
11. printf(“%d + %d = %dn“, a, b, c);
12. return 0;
13. }
Nhap so thu nhat:
Nhap so thu hai:
5 + 7 = 12
5
7
5
7
12
a
b
c
/ngonnguC/bin/tong
/ngonnguC/bin/
Lập trình C - Việt Nhật 2007 30
Chú ý
C phân biệt chữ hoa/chữ thường do đó phải viết đúng tên
lệnh.
vd: printf chứ không phải là Printf, pRintf, PRINTF.
Trong câu lệnh scanf() để lấy giá trị vào biến, phải luôn dùng
dấu & trước tên biến.
Khi gọi các hàm phải khai báo các tham số đúng vị trí và đầy
đủ.
Phải khai báo biến trước khi sử dụng trong chương trình.
Lập trình C - Việt Nhật 2007 31
Các kiểu dữ liệu cơ bản
Character: char (ký tự 1-byte)
Integer: int (các giá trị nguyên 2-byte)
-32768 đến 32767
Số nguyên không dấu: unsigned int
0 đến 65535
Số phẩy động độ chính xác đơn: float (các giá trị dấu chấm
động 4-byte)
(+,-)3.4E-38 , (+,-)3.4E+38
Số phẩy động độ chính xác kép: double (dấu chấm động 8-
byte)
(+,-)3.4E-308 , (+,-)3.4E+308
Lập trình C - Việt Nhật 2007 32
Biến và hằng số
Biến số (variable) được dùng để giữ các giá trị và có thể thay
đổi các giá trị mà biến đang giữ
Khai báo: <typename> varname;
Vd:
int i;
float x, y, z;
char c;
Gán giá trị cho biến: <varname> = <value>;
vd:
i = 4;
x = 5.4;
y = z = 1.2;
9
Lập trình C - Việt Nhật 2007 33
Hằng số
Hằng số (constant) giá trị không thay đổi trong quá trình sử
dụng.
Khai báo hằng:
#define <constantname> <value>
vd:
#define TRUE 1
#define FALSE 0
Giá trị hằng
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’
‘9’ – ‘0’ = 57 – 48 = 9
Lập trình C - Việt Nhật 2007 34
Kiểu và chuyển kiểu (typecasting)
C cho phép chuyển đổi kiểu dữ liệu cơ bản trong khi đang
tính toán (chuyển kiểu tường minh).
ví dụ:
void main()
{
float a;
int b;
b = 10/3;
a = (float)10/3;
printf(“a = %f n b = %dn”, a, b);
}
Chuyển kiểu tự động: khi biểu thức gồm hai toán hạng khác
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 35
Định nghĩa kiểu (typedef)
Có thể định nghĩa các kiểu riêng bằng lệnh typedef.
vd:
#define TRUE 1
#define FALSE 0
typedef int boolean;
void main() {
boolean b;
b = FALSE;
/*...*/
}
Lập trình C - Việt Nhật 2007 36
Các Toán tử
right to left= *= /= %= += -=
&= |= ^= <<= >>=
Assignment14
13
12
11
10
9
8
7
6
5
4
3
2
2
1
0
Priority
Conditional operator
Logical OR
Logical AND
Boolean OR
Boolean XOR
Boolean AND
Equality
Relational
Shift
Additive
Multiplicative
Type cast
Prefix and unary
Postfix
Primary expression
Category
Right to left?
left to right||
left to right&&
left to right|
left to right^
left to right&
left to right== !=
left to right< <= > >=
left to right<< >>
left to right+ -
left to right* / %
right to left( typeName )
right to left! ~ + - ++ -- & sizeof
left to rightFunction() () [] ->
Noneidentifiers constants
AssociativityExample
10
Lập trình C - Việt Nhật 2007 37
Các toán tử so sánh và toán tử logic
15
7
5
7
0
X | Y
1
1
1
0
0
!Y
0
0
0
1
1
!X
1
1
1
1
0
X || Y
7
7
0
7
0
Y
0
0
0
X>>Y
32
48
24
X<<Y
0
0
1
X == Y
1
1
0
X != Y
1
0
1
X >= Y
1
0
0
X > Y
0
1
1
X <= Y
3
4
3
Y
018
515
005
000
000
X & YX && YX
Possible MistakesLogical Operators
304
413
303
X=YX < YX
Possible MistakesRelational and Quality Operators
Lập trình C - Việt Nhật 2007 38
Số nhị phân
Hệ đếm thập phân:
5426=5*103 + 4*102 + 2*101 + 6*100
254,68 = 2*102 + 5*101 + 4*100 + 6*10-1 + 8*10-2
Hệ đếm nhị phân (cơ số hai): Hệ chỉ có hai chữ số 0 và 1.
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
Một chữ số nhị phân – BIT – là đơn vị lưu trữ thông tin nhỏ nhất
của máy tính.
Lập trình C - Việt Nhật 2007 39
Các phép toán số học
+ - / *
%: phép chia lấy phần dư trong số nguyên. (modulo).
i = i + 1; i++; ++i;
i = i – 1; i--; --i;
i = i + 3; i += 3;
i = i * j; i *= j;
Phép chia kiểu nguyên: 7/5 cho kết quả 1.
Phép lấy phần dư: 7 % 5 cho kết quả 2.
Thứ tự ưu tiên:
Toán tử Tên gọi Thứ tự tính toán (precedence)
() Ngoặc tròn Được ưu tiên nhất. Tính giá trị đầu tiên trong một biểu thức
*, /, or % Nhân, chia, lấy
phần dư
Độ ưu tiên thứ hai, nếu cùng tồn tại thì được thực hiện từ
trái qua phải
+ or - Cộng, trừ Độ ưu tiên thấp nhất
Lập trình C - Việt Nhật 2007 40
Toán tử tăng giảm
Có thể viết i=i+1; (i=i-1;) dưới dạng
i++; ( i--; )
hay ++i ( --i; )
Sự khác nhau giữa tiền tố và hậu tố
i++, giá trị của i tham gia vào việc lượng giá biểu thức chứa nó
trước khi tăng.
++I, giá trị của i tăng, sau đó giá trị mới có hiệu lực khi lượng giá
biểu thức chứa nó.
Ví dụ: Với i= 4
j = ++i +10; cho j bằng 15 và j= i++ +10; cho j=14.
int i= 6;
printf ("%dn",i++); /* Prints 6 sets i to 7 */
int i= 6;
printf ("%dn",++i); /* prints 7 and sets i to 7 */
11
Lập trình C - Việt Nhật 2007 41
Các phép gán tắt
Ngôn ngữ lập trình C
Chương 3: Cách thức vào ra
Cao Tuấn Dũng
2007
Lập trình C - Việt Nhật 2007 43
Dòng vào, ra chuẩn
Việc nhập dữ liệu được thực hiện thông qua
dòng vào chuẩn stdin (bàn phím)
Khi có dữ liệu trên dòng vào chuẩn, các hàm
nhận dữ liệu như scanf, getchar sẽ lấy phần dữ
liệu mà nó yêu cầu
Khi dòng vào không đủ dữ liệu: máy chờ người
dùng
Dòng ra chuẩn: màn hình
Các hàm xuất dữ liệu
Printf, putchar
Thư viện vào ra chuẩn: stdio.h
a 12 1.4
Lập trình C - Việt Nhật 2007 44
Hàm printf
int printf (char* địnhdạng, [danh sách đối,….]
Vd: printf(“Gia tri ham tai toa do %d,%d la %f”, 2,3, 1.09);
Dạng tổng quát của đặc tả chuyển dạng
%[-] [width][.prec] ký tự chuyển dạng
width: dãy số nguyên xác định độ rộng tối thiểu của giá trị hiển thị
.prec: dùng cho kiểu float, double
Đối số nguyên không dấuunsigned intu
Đối số là xâu ký tựchar *s
Đối số là số dấu phẩy tĩnhfloat, doublef
Đối số là số nguyên lớnlongld
Đối số là số nguyên có dấuintd
Đối số là một ký tựcharc
Ý nghĩaKiểu giá trị của đối sốKý tự chuyển dạng
12
Lập trình C - Việt Nhật 2007 45
Hàm printf
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
quả dồn về bên trái
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;
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
"-0124"printf("%05d", i);
" -124"printf("%5d", i);
"-124"printf("%-05d", i);
"-124 "
Kết quả
printf("%-5d", i);
Lệnh
Lập trình C - Việt Nhật 2007 46
Ký tự Escape
Đánh dấu các ký tự đặc biệt: ’, ”, n, t, r, , 0
Dùng trong hàm printf để định dạng dòng ra
Lập trình C - Việt Nhật 2007 47
Hàm scanf
Có một số đặc điểm tương tự printf nhưng theo chiều ngược lại:
đọc thông tin từ dòng vào.
Cú pháp: int scanf(char *định_dạng, [danhsachdiachi]);
Trả về giá trị EOF nếu có lỗi.
Xâu định dạng có cú pháp: %[*][width]kýtựchuyểndạng
int a;
float x, y;
char ch[6], ct[6];
scanf("%f%5f%3d%3s%s", &x, &y,&a, ch, ct);
54.32e-1 25 12357287a
x = 5.432
y=25.0
a=123
ch="572"
ct="87a
Lập trình C - Việt Nhật 2007 48
Một số hàm nhập xuất khác
getchar: Nhập một ký tự từ dòng vào:
int getchar();
int c;
c = getchar();
putchar
putchar(int c);
fflush(stdin): Làm sạch dòng vào.
Nhập xuất xâu:
char *gets(char *s) nhận dữ liệu cho đến khi gặp 'n'
char ten[30];
int t;
printf( "nTuoi:");
scanf("%d",&t);
fflush(stdin); /* xoá mọi ký tự ở dòng vào */
printf( "nTen:");
scanf("%s",ten);
13
Ngôn ngữ lập trình C
Các cấu trúc điều khiển
Cao Tuấn Dũng
2007
Lập trình C - Việt Nhật 2007 50
Câu lệnh điều kiện if
if (<dieu kien>)
{
/* cac lenh thuc hien neu dieu kien dung */
}
…
expression
statement(s)
Next statement
True False
Một quyết định có thể
được đưa ra dựa trên bất
cứ một biểu thức nào.
0 - false
khác 0 - true
Ví dụ:
3 - 4 - true
Lập trình C - Việt Nhật 2007 51
Ví dụ
1. #include <stdio.h>
2. int main() {
3. int b;
4. printf("Enter a value:");
5. scanf("%d", &b);
6. if (b < 0)
7. printf("The value 
is negativen");
8. return 0;
9. }
Lập trình C - Việt Nhật 2007 52
Ví dụ
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 )
printf( "Vuot qua ky thin" );
true
false
diemthi >= 5 print “Qua”
14
Lập trình C - Việt Nhật 2007 53
if … else …
if (<dieu kien>)
{
/* cac lenh thuc hien neu dieu kien dung */
}
else
{
/* cac lenh thuc hien neu dieu kien sai */
}
… expression
statement1
Next statement
True False
statement2
Lập trình C - Việt Nhật 2007 54
Ví dụ
…
printf(“1/X is: “);
if(X)
printf(“ %f n”, 1/X);
else
printf(“ undefined n”);
…
Lập trình C - Việt Nhật 2007 55
Ví dụ
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 nếu không thì sinh viên thi trượt.
Mã chương trình C: if ( diemthi >= 5 )
printf( "Vuot qua ky thin" );
else printf( "Truotn" );
truefalse
print “Truot” print “Qua”
diemthi >= 5
Lập trình C - Việt Nhật 2007 56
Lỗi đơn giản nhưng dễ phạm
1. #include <stdio.h>
2. int main() {
3. int b;
4. printf("Enter a value:");
5. scanf("%d", &b);
6. if (b == 5)
7. printf(“b is "); printf( “5 n”);
8. return 0;
9. }
15
Lập trình C - Việt Nhật 2007 57
Lỗi đơn giản nhưng dễ phạm
1. printf(“1/X is: “);
2. if(X < 0) ;
3. printf(“ X is negative n”);
4. …
Lập trình C - Việt Nhật 2007 58
Lệnh if else if
Khi muốn thực hiện 1 trong n quyết định
if (biểu thức1)
lệnh 1;
else if (biểu thức2)
lệnh 2;
…
else if (biểu thức_n_1)
lệnh n -1;
else lệnh n;
Biểu thức 1
Biểu thức 2
Lệnh 1
Lệnh 1
+
+
-
-
Lệnh ứng với else cuối cùng
Lập trình C - Việt Nhật 2007 59
Lệnh if else if
Lập chương trình nhập vào mã trình độ và đưa ra tên trình độ tương
ứng
#include <stdio.h>
int main()
{
int ma;
printf("n Go vao ma trinh do:");
scanf("%d", &ma);
if (ma==1)
printf("n Trinh do trung cap");
else if (ma==2)
printf("n Trinh do cao dang");
else if (ma==3)
printf("n Trinh do dai hoc");
else
printf("n Nhap sai ma");
return 0;
}
Lập trình C - Việt Nhật 2007 60
Ví dụ: Kiểm tra nhiều điều kiện
1. #include <stdio.h>
2. int main() {
int b;
3. printf("Enter a value:");
4. scanf("%d", &b);
5. if (b < 0)
6. printf("The value is negativen");
7. else if (b == 0)
8. printf("The value is zeron");
9. else
10. printf("The value is positiven");
11. return 0;
12. }
Bài tập: Viết chương trình giải phương trình bậc nhất:
ax + b = 0. Biện luận các điều kiện có nghiệm của phương trình.
16
Lập trình C - Việt Nhật 2007 61
Điều kiện lồng nhau
Câu lệnh if có thể được lồng vào nhau.
1. if ( X >= 0 ) {
2. if ( Y < 0 )
3. Y = Y + sqrt(X);
4. }
5. else
6. Y = Y + sqrt(-X);
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.
1. if ( X >= 0 )
2. if ( Y < 0 )
3. Y = Y + sqrt(X);
4. else
5. Y = Y + sqrt(-X);
Bài tập: Viết chương trình giải phương trình bậc 2:
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 62
Lặp - lệnh while
while (bieu thuc dieu kien)
{cac lenh}
Khi biểu thức điều kiện (expression) còn khác 0 (TRUE), lệnh
(statement) tiếp tục được thực hiện. Nếu expression bằng 0
(FALSE), lệnh while dừng và chương trình sẽ gọi lệnh kế tiếp
sau while.
Nếu lúc đầu expression bằng 0 thì (statement) trong while không
bao giờ được gọi thực hiện.
expression
statement(s)
Next statement
True
False
Lập trình C - Việt Nhật 2007 63
Ví dụ
In bảng đổi nhiệt độ từ độ Fahrenheit (oF) sang độ Celcius
(oC).
1. #include <stdio.h>
2. int main() {
3. int a = 0;
4. while (a <= 100) {
5. printf("%4d degrees F = %4d degrees Cn",a, (a - 32)*5/9);
6. a = a + 10;
7. }
8. return 0;
9. }
Lập trình C - Việt Nhật 2007 64
Ví dụ: Biến đếm điều khiển
Biến đếm điều khiển vòng lặp
Lặp cho đến khi bộ đếm đạt tới một giá trị nhất định
Vòng lặp xác định: số vòng lặp được biết trước.
Ví dụ: Một lớp học 120 sinh viên phải làm bài tập. Điểm số (số
nguyên trong khoảng 0 đến 10). Tính điểm bài tập trung bình cho
cả lớp.
Mã giả:
Gán tong bang 0
Gán biến đếm sinh viên bằng 1
While biến đếm nhỏ hơn hay bằng 120
Nhập điểm số
Cộng điểm số vào tổng
Tăng 1 vào biến đếm
Điểm trung bình = tổng / 120
Hiển thị ra màn hình
17
Lập trình C - Việt Nhật 2007 65
1. Khởi tạo biến
2. Thực hiện vòng
lặp
3. Xuất kết quả
1 /*
2 Tinh diem trung binh kiem tra
3 Dieu khien vong lap bang bien dem */
4 #include <stdio.h>
5
6 int main()
7 {
8 int counter, grade, total, average;
9
10 /* Khoi tao gia tri bien */
11 total = 0;
12 counter = 1;
13
14 /* Xu ly */
15 while ( counter <= 120 ) {
16 printf( "Nhap diem: " );
17 scanf( "%d", &grade );
18 total = total + grade;
19 counter = counter + 1;
20 }
21
22 /* Kêt thuc */
23 average = total / 120;
24 printf( "Diem trung binh cua ca lop la %dn", average );
25
26 return 0; /* Chuong trinh ket thuc thanh cong */
27 } Lập trình C - Việt Nhật 2007 66
Thiết kế giải thuật Top-down
Mở rộng bài toán:
Viết chương trình tính điểm trung bình mà số sinh viên không
được biết trước.
Làm cách nào để kết thúc chương trình?
Sử dụng giá trị có vai trò "lính canh"
Còn có tên gọi khác là cờ
Chỉ ra dấu hiệu “kết thúc nhập dữ liệu.”
Vòng lặp kết thúc khi người sử dụng nhập vào giá trị lính canh.
Giá trị này được chọn sao cho không thể nhầm lẫn nó với các giá
trị nhập vào thông thường khác.
Lập trình C - Việt Nhật 2007 67
Thiết kế giải thuật Top-down
Tiếp cận top-down, giải quyết từng bước vấn đề
Bắt đầu với vấn đề cần giải quyết từ đỉnh (top):
Xác định điểm trung bình kiểm tra của cả lớp
Phân chia bài toán thành các công việc nhỏ hơn và liệt kê chúng
theo trình :
Khởi tạo biến
Nhập dữ liệu, tính tổng và đếm số sinh viên
Tính điểm trung bình
Nhiều chương trình trải qua ba giai đoạn:
Khởi tạo: khởi tạo giá trị các biến
Xử lý: nhập giá trị dữ liệu và thao tác trên chúng
Kết thúc: Tính toán và hiển thị kết quả cuối cùng
Lập trình C - Việt Nhật 2007 68
1. Khởi tạo biến
2. Nhập liệu
2.1 Thi hành vòng
lặp
1 /*
2 Chương trình tính điểm trung bình
3 sự dụng biến canh để điều khiển vòng lặp */
4 #include <stdio.h>
5
6 int main()
7 {
8 float average;
9 int counter, grade, total;
10
11 /* khởi tạo */
12 total = 0;
13 counter = 0;
14
15 /* xử lý */
16 printf( "Nhập điểm, -1 để thoát ra: " );
17 scanf( "%d", &grade );
18
19 while ( grade != -1 ) {
20 total = total + grade;
21 counter = counter + 1;
22 printf( "Enter grade, -1 to end: " );
23 scanf( "%d", &grade );
24 }
18
Lập trình C - Việt Nhật 2007 69
Các cấu trúc điều khiển lồng nhau
Bài toán
Một học viện có một danh sách kết quả kiểm tra của 120 sinh
viên (1 = qua, 2 = trượt)
Viết chương trình phân tích kết quả
Nếu có hơn 90 sinh viên thi đỗ, hiển thị "Chất lượng đạt"
Chú ý
Chương trình phải xử lý 120 kết quả
Biến đếmđiều khiển vòng lặp có thể được sử dụng
Có thể dùng hai biến đếm
Một cho số lượng thi đỗ, một cho số thi trượt
Kiểm tra kết quả thi—hoặc 1 hoặc 2
Nếu kết quả không phải là 1, coi như là 2
Lập trình C - Việt Nhật 2007 70
1 /* Fig. 3.10: fig03_10.c
2 Phân tích kết quả thi */
3 #include <stdio.h>
4
5 int main()
6 {
7 /* Khai báo khởi tạo biến */
8 int passes = 0, failures = 0, student = 1, result;
9
10 /* Làm việc với 120 sinh viên; vòng lặp điều khiển bởi biến đếm */
11 while ( student <= 120 ) {
12 printf( "Enter result ( 1=pass,2=fail ): " );
13 scanf( "%d", &result );
14
15 if ( result == 1 ) /* if/else lồng trong while */
16 passes = passes + 1;
17 else
18 failures = failures + 1;
19
20 student = student + 1;
21 }
22
23 printf( "Passed %dn", passes );
24 printf( "Failed %dn", failures );
25
26 if ( passes > 90 )
27 printf( "Chất lượng đạtn" );
28
29 return 0; /* kết thúc thành công */
30 }
Lập trình C - Việt Nhật 2007 71
Lặp - lệnh for
for (biểu thức khởi tạo; biểu thức kiểm tra; biểu thức tăng
giảm)
{Các lệnh}
Khởi động. Sau đó, nếu điều kiện (test) khác 0: lệnh (statement)
được thi hành, lệnh điều chỉnh lại “biến đếm” được gọi thi hành.
test
statement(s)
Next statement
True
False
adjustment
initialization
Lập trình C - Việt Nhật 2007 72
Ví dụ
In ra các số từ 1 đến 10
Biểu thức khởi tạo được thực hiện một lần, còn các biểu thức
còn lại được tính nhiều lần.
Vòng lặp for có thể lồng nhau nhiều lần
Câu lệnh break làm máy thoát khỏi vòng lặp for sâu nhất
chứa lệnh đó.
In ra các chữ cái hoa
for (i = 1; i <= 10; i++)
printf("%d ", i);
char ch;
for (ch = 'A'; ch <= 'Z'; ch++)
printf("%c ", ch);
19
Lập trình C - Việt Nhật 2007 73
Lệnh for (tiếp theo)
Giữa hai dấu ; có thể có nhiều hơn một biểu thức
Phân cách nhau bởi dấu phảy.
Example:
for (i = 0, j = 0; j + i <= 10; j++, i++)
printf( "%dn", j + i );
Lập trình C - Việt Nhật 2007 74
Ví dụ
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.
1. #include <stdio.h>
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 Cn",
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 75
Ví dụ
Sum is 2550
1 /* Tính tổng các số chẵn
2 từ 1 đến 100 */
3 #include <stdio.h>
4
5 int main()
6 {
7 int sum = 0, number;
8
9 for ( number = 2; number <= 100; number += 2 )
10 sum += number;
11
12 printf( "Sum is %dn", sum );
13
14 return 0;
15 }
Lập trình C - Việt Nhật 2007 76
Lặp - lệnh do while
do
{statement(s)}
while (expression) ;
Thực hiện lệnh (statement). Kiểm tra biểu thức điều kiện
(expression). Nếu (expression) bằng 0, dừng. Nếu không, thực
hiện (statement).
Lệnh do while thực hiện (statement) ít nhất một lần.
expression
statement(s)
Next statementTrue
False
20
Lập trình C - Việt Nhật 2007 77
Ví dụ - giao diện chương trình
1. #include <stdio.h>
2. #define PTB1 1
3. #define PTB2 2
4. #define STOP 3
5. int main()
6. {
7. int i;
8. do {
9. printf(“ Chuong trinh giai phuong trinh bac thap n”);
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”);
Lập trình C - Việt Nhật 2007 78
14. printf(“ 3. Thoat chuong trinh nn”);
15. printf(“ Chon muc so (1/2/3) ? “);
16. scanf(“%d”, &i);
17. if(i == PTB1)
18. printf(“Giai phuong trinh bac 1: hien chua con”);
19. else if(i == PTB2)
20. printf(“Giai phuong trinh bac 2: chua cai datnn”);
21. } while (i != STOP);
22. return 0;
23. }
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 79
break
dùng để thoát khỏi vòng lặp giữa
chừng.
cú pháp:
break;
Thường sử dụng cùng với lệnh if để
kiểm tra điều kiện dừng trước khi dùng
lệnh break.
Bài tập: Viết chương trình nhập vào
một số rồi tìm số nguyên tố đầu tiên
lớn hơn số vừa nhập
for…
{
for…
{
for…
{
….
break;
…
}
…
}
}
Lập trình C - Việt Nhật 2007 80
Tìm số nguyên tố lớn
Cho trước một số tự nhiên N, tìm số nguyên tố lớn hơn gần
nhất.
Giải pháp:
Số nguyên tố là số nguyên tố không chia hết cho các số nhỏ hơn
căn bậc hai của nó.
Không tính trường hợp 2, 3 thì số nguyên tố phải lẻ.
Thuật toán sơ khởi:
Kiểm tra N và so sánh với 2, 3
Nếu N chẵn thì tăng lên 1. Bắt đầu thử với một số lẻ.
Nếu N là hợp số thì tăng N lên 2
Thử thế nào?
Thử tất cả các số nhỏ hơn căn bậc hai của N, nếu N chia hết cho
một số trong chúng thì N là hợp số.
21
Lập trình C - Việt Nhật 2007 81
Tìm số nguyên tố lớn
1. #include <stdio.h>
2. #define TRUE 1
3. main(void)
4. {
5. unsigned long int sochia, ucv_nguyento;
6. int la_nguyento;
7. printf(“Nhap vao so khoi dau: “);
8. scanf(“%lu”, & ucv_nguyento);
9. if(ucv_nguyento <= 2)
10. ucv_nguyento = 2;
11. else if(ucv_nguyento !=3 )
12. {
13. if(ucv_nguyento %2 == 0)
14. ucv_nguyento ++; /* Phai la so le */
Lập trình C - Việt Nhật 2007 82
16. for( ; ; ucv_nguyento += 2)
17. {
18. la_nguyento = !TRUE;
19. for(sochia = 3; ucv_nguyento % sochia; sochia += 2)
20. if(sochia * sochia > ucv_nguyento)
21. { la_nguyento = TRUE;
22. break;
23. }
24. if (la_nguyento)
25. break;
26. }
27. }
28. printf(“So nguyen to lon hon gan nhat la %lun”, ucv_nguyento);
29. }
Lập trình C - Việt Nhật 2007 83
continue
bỏ qua các lệnh kế tiếp
trong một vòng lặp và bắt
đầu vòng lặp tiếp theo.
cú pháp:
continue;
chỉ áp dụng với lệnh lặp.
Bài tập: Viết chương trình
nhập vào một số và tìm ra
tất cả các thừa số nguyên
tố của số đó.
while(i<10)
{
scanf(“%d”, &grade);
if ((grade<0) || (grade>100))
{
printf(“Illegal grade – try againn”);
continue;
}
…….
}
Lập trình C - Việt Nhật 2007 84
Ví dụ minh họa: continue
1 /* Ví dụ minh họa
2 Sử dụng continue trong vòng lặp for */
3 #include <stdio.h>
4
5 int main()
6 {
7 int x;
8
9 for ( x = 1; x <= 10; x++ ) {
10
11 if ( x == 5 )
12 continue;
13
14
15 printf( "%d ", x );
16 }
17
18 printf( "nDung lenh continue de bo qua gia tri
5n" );19 return 0;
20 }
1 2 3 4 6 7 8 9 10
Dung lenh continue de bo qua gia tri 5
22
Lập trình C - Việt Nhật 2007 85
Tìm thừa số nguyên tố
1. #include <stdio.h>
2. main(void)
3. {
4. unsigned long N, thuasonguyento, phanconlai;
5. printf(“Nhap vao mot so tu nhien: “);
6. scanf(“%lu”, &N);
7. thuasonguyento = 2;
phanconlai = N;
8. while(thuasonguyento * thuasonguyento <= phanconlai)
9. {
10. if(phanconlai % thuasonguyento == 0)
Lập trình C - Việt Nhật 2007 86
12. { /* Tim ra mot thua so */
13. printf(“%lu”, thuasonguyento);
14. phanconlai /= thuasonguyento;
15. continue;
16. }
17. /* Khong phai la thua so nguyen to */
18. if(thuasonguyento == 2) thuasonguyento = 3;
19. else
20. thuasonguyento += 2;
21. }
22. /* thua so nguyen to cuoi cung */
23. printf(“%lun”, phanconlai);
24. }
Lập trình C - Việt Nhật 2007 87
Thực hành lệnh lặp
Viết chương trình giải các bài tập sau sử dụng lần lượt các
cấu trúc lặp: while, for, do while
Nhập vào một số nguyên năm chữ số (giả sử người dùng
tuân thủ nghiêm ngặt quy tắc nhập). In các chữ số này trên
năm hàng
Viết chương trình in ra giai thừa của số tự nhiên N.
Nhập mười số nguyên từ bàn phím và đưa ra số lớn nhất.
Lập trình C - Việt Nhật 2007 88
Lệnh switch
Bài tập:
Viết chương trình lấy ngẫu nhiên 1000 số nguyên và đếm
số lần xuất hiện ở hàng đơn vị các số chẵn (2, 4, 6, 8), số lẻ
(1, 3, 5, 7, 9) và số 0.
Nếu chúng ta dùng cấu trúc lệnh if ... else ... if … thì phức tạp
và có thể đòi hỏi nhiều phép thử.
Lý do: if ... else ... : rẽ nhánh hai chiều.
Thử cài đặt bài toán bằng if...else...
23
Lập trình C - Việt Nhật 2007 89
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 case1:
case case2:
<statements>;
break;
/* … */
case casen:
<statements>;
break;
default:
<statements>;
break;
}
Lập trình C - Việt Nhật 2007 90
Sơ đồ xử lý lệnh switch
true
false
.
.
.
case a case a action(s) break
case b case b action(s) break
false
false
case z case z action(s) break
true
true
default action(s)
Lập trình C - Việt Nhật 2007 91
Giải bài bằng switch
1. #include <stdlib.h>
2. #include <stdio.h>
3. #include <time.h>
4. int main(void)
5. { int n,i;
6. int n_even = n_odd = n_zero = 0;
7. randomize();
8. for(i=0; i<1000; i++)
9. { n = random(1000);
10. switch (n%10) {
11. case 2:
12. case 4:
13. case 6:
14. case 8:
15. n_even++;
16. break;
Lập trình C - Việt Nhật 2007 92
17. case 1:
18. case 3:
19. case 5:
20. case 7:
case 9:
21. n_odd++;
22. break;
23. case 0:
24. n_zero++;
25. break;
26. }
27. }
28. // print out the summary
29. printf(“Number of even_eding number: %dn”
Number of odd_ending number: %dn”
Number of zero_ending number: %dn”,
n_even, n_odd, n_zero);
30. return 0;
31. }
24
Lập trình C - Việt Nhật 2007 93
Thống kê điểm số (American system)
Hệ thống điểm của học sinh, sinh viên Mỹ: A, B, C, D, E, F
Chương trình nhập điểm của sinh viên, kết thúc bằng EOF
In ra số lượng sinh viên đạt điểm số tương ứng
Lập trình C - Việt Nhật 2007 94
1. Khởi tạo các
biến
2. Nhập liệu
2.1 Dùng switch
để cập nhật các
biến đếm
1 /*
2 Thống kê điểm số */
3 #include <stdio.h>
4
5 int main()
6 {
7 int diem;
8 int aCount = 0, bCount = 0, cCount = 0,
9 dCount = 0, fCount = 0;
10
11 printf( "Nhập phân loại học tập theo chữ hoa.n" );
12 printf( "Nhấn ký tự EOF để kết thúc nhập liệu.n" );
13
14 while ( ( grade = getchar() ) != EOF ) {
15
16 switch ( grade ) { /* switch lồng trong while */
17
18 case 'A': /* điểm là hạng A */
19 ++aCount;
20 break;
21
22 case 'B': /* điểm là hạng B */
23 ++bCount;
24 break;
25
26 case 'C': /* điểm là hạng C */
27 ++cCount;
28 break;
29
30 case 'D': /* điểm là hạng D */
31 ++dCount;
32 break;
Lập trình C - Việt Nhật 2007 95
3. In kết quả
33
34 case 'F': /* điểm là hạng F */
35 ++fCount;
36 break;
37
38 case 'n': case' ': /* ignore these in input */
39 break;
40
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" );
44 break;
45 }
46 }
47
48 printf( "Kết quả phân loại sinh viên:n" );
49 printf( "A: %dn", aCount );
50 printf( "B: %dn", bCount );
51 printf( "C: %dn", cCount );
52 printf( "D: %dn", dCount );
53 printf( "F: %dn", fCount );
54
55 return 0;
56 }
Lập trình C - Việt Nhật 2007 96
Bài tập
1) Viết chương trình tính giá trị biểu thức "a op b" như "2 + 3"
a, b toán hạng – op : toán tử
2) Yêu cầu người sử dụng nhập vào một tháng. In ra số ngày
trong tháng đó.
Tháng có 31 ngày: 1, 3, 5, 7, 8, 10, 12
Tháng có 30 ngày: 4, 6, 9, 10
Tháng có 28 hoặc 29 ngày : 2
25
Lập trình C - Việt Nhật 2007 97
In số ngày trong một tháng
#include <stdio.h>
int main ()
{
int thang;
printf("n Nhap vao thangs trong
nam ");
scanf("%d",&thang);
switch(thang) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
printf("n Thang %d co 31
ngay ",thang);
break;
case 4:
case 6:
case 9:
case 11:
printf("n Thang %d co 30
ngay ",thang);
break;
case 2:
printf (" Thang 2 co 28 hoac
29 ngay");
break;
default :
printf("n Khong co thang
%d", thang);
break;
}
return 0;
}
Lập trình C - Việt Nhật 2007 98
Tính ex với độ chính xác 10-5
ex = 1+x/1!+x2/2!+……………..+ xn/n!
#include <stdio.h>
void main() {
float epsilon = 0.00001;
float x,add;
int i=1;
float result = 0.0;
add = 1;
printf("Chương trình tính giá trị e mũ x. n");
printf("x="); scanf("%f", &x);
while (add > epsilon) {
result = result + add;
add = add*x/i;
i = i+1;
}
printf ("Ket qua la:%f", result);
}
Lập trình C - Việt Nhật 2007 99
Một số toán tử và lệnh khác
Toán tử ‘,’ được dùng để khởi động nhiều biến trong vòng
lặp.
Ví dụ:
for(i = 0, j = 0; i < 5; i++, j += i++)
printf(“i = %d, j = %d, i+j = %dn”, i, j, i+j);
Kết quả là: ????
Toán tử ba ngôi
<TestExpr> ? <YesExpr> : <NoExpr>
Ví dụ:
Max = (Y > Z) ? Y : Z;
Lập trình C - Việt Nhật 2007 100
Một số toán tử và lệnh khác
Lệnh goto cho phép nhảy không điều kiện đến bất kỳ nơi nào
trong chương trình.
Cú pháp:
goto <label>
Ví dụ: xem chương trình ví dụ.
Lệnh goto làm mất cấu trúc chương trình.
26
Lập trình C - Việt Nhật 2007 101
Từ khóa của C
auto break case char
const continue default do
double else enum extern
float for goto if
int long register return
short signed sizeof static
struct switch typedef union
unsigned void volatile while
Lập trình C - Việt Nhật 2007 102
Từ khóa của C
#define để khai báo hằng số và …
typedef để khai báo kiểu dữ liệu riêng
Toán tử sizeof xác định số byte được dùng để chứa một đối
tượng
ví dụ:
typedef unsigned long int Int32;
/* ... */
int x;
x = sizeof(Int32); // x = 4
Ngôn ngữ lập trình C
Chương 4: Mảng
Cao Tuấn Dũng
2007
Lập trình C - Việt Nhật 2007 104
Mảng
Mảng là tập hợp các giá trị cùng kiểu.
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];
số phần tử trong mảng: array_size;
int a[array_size];
n = array_size;
Truy cập phần tử mảng qua chỉ số của phần tử: i
array[i]; // 0 <= i <= array_size-1, i ∈N0
a[n-1]a[n-1]…a[2]a[1]a[0]
a
27
Lập trình C - Việt Nhật 2007 105
Mảng
Các phần tử mảng hoạt động như các
biến thông thường
c[ 0 ] = 3;
printf( "%d", c[ 0 ] );
Các thao tác toán học trên chỉ số mảng
If x bằng 3
c[ 5 - 2 ] == c[ 3 ] == c[ x ]
Ví dụ khai báo mảng:
int c[ 10 ];
float myArray[ 3284 ];
Khai báo nhiều mảng cùng kiểu
Tương tự khi khai báo nhiều biến cùng
kiểu
int b[ 100 ], x[ 27 ];
Tên mảng
(Các phần tử mảng
có cùng chung một
tên, c)
Vị trí của một
phần tử trong mảng
c
c[6]
-45
6
0
72
1543
-89
0
62
-3
1
6453
78
c[0]
c[1]
c[2]
c[3]
c[11]
c[10]
c[9]
c[8]
c[7]
c[5]
c[4]
Lập trình C - Việt Nhật 2007 106
Chú ý
C không kiểm tra giới hạn của chỉ số truy cập phần tử mảng.
Truy cập đến phần tử i>=array_size không có cảnh báo,
nhưng giá trị không kiểm soát được.
Kích thước mảng phải là một hằng số.
Kích thước mảng có thể được khai báo tường minh hoặc
thông qua một giá trị định nghĩa trước (#define)
Lập trình C - Việt Nhật 2007 107
Ví dụ thực tế
Bài toán: Sử dụng bảng số liệu
về lượng mưa hàng tháng
trong năm.
Input: tháng
Output: Lượng mưa trung bình
cho tháng đó
tháng lượng mưa TB (in mm)
0 30
1 40
2 45
3 95
4 130
5 220
6 210
7 185
8 135
9 80
10 40
11 45
Bảng lượng mưa
Lập trình C - Việt Nhật 2007 108
Khởi tạo mảng
Mảng có thể được khởi tạo bởi một dãy các
giá trị thích hợp.
int n[ 5 ] = { 1, 2, 3, 4, 5 };
Nếu không đủ các giá trị khởi tạo cho tất cả, các phần
tử bên phải sẽ có giá trị 0
int n[ 5 ] = { 0 } => Tất cả phần tử = 0.
Nếu kích thước mảng bị bỏ qua, chương
trình dịch tự phát hiện kích thước cần
thiết.
int n[ ] = { 1, 2, 3, 4, 5 };
Các khai báo sau đây là hợp lệ
int Squares[5] = {0,1,4,9,16};
int Squares[5] = {0,1,4};
int Squares[] = {0,1,4,9,16};
int Squares[];
28
Lập trình C - Việt Nhật 2007 109
Chú ý
Không thể thực hiện các thao tác chép nội dung một mảng
sang mảng khác.
Chép từng phần tử mảng
char A[3]={‘a’,’b’,’c’};
char B[3];
B = A; // ???
for(int i=0; i<3; i++)
B[i] = A[i];
hoặc chép khối bộ nhớ (sẽ được đề cập sau)
Không dùng phép so sánh trực tiếp (==) nội dung trong hai
mảng.
Phép so sánh (A==B) so sánh địa chỉ hai vùng nhớ mà A và B
chỉ đến.
Lập trình C - Việt Nhật 2007 110
Bảng lượng mưa (tiếp)
#include <stdio.h>
int main()
{
int thang;
int bangmua[12] = { 30, 40, 45, 95, 130, 220,
210, 185, 135, 80, 40, 45 };
printf("Nhap thang: ");
scanf("%d", &thang);
printf("Luong mua trung binh cua thang: %d mm.n",
bangmua[thang-1]);
return 0;
}
Lập trình C - Việt Nhật 2007 111
Bảng lượng mưa (tiếp)
#include <stdio.h>
int main()
{
int thang;
int bangmua[12] = { 30, 40, 45, 95, 130, 220,
210, 185, 135, 80, 40, 45 };
printf("Nhap thang: ");
scanf("%d", &thang);
printf("Luong mua trung binh cua thang: %d mm.n",
bangmua[thang-1]);
return 0;
}
Lập trình C - Việt Nhật 2007 112
Chú ý
Các phần tử trong mảng được dùng như các biến đơn thông
thường.
Các hàm printf, scanf không làm việc với kiểu mảng, do đó việc
nhập xuất giá trị là do người lập trình tự tiến hành với từng phần tử
mảng.
1. // nhap gia tri cho cac phan tu mang
2. float a[4];
3. for(int i=0; i<4; i++)
4. {
5. printf(“a[%d]=“,i);
6. scanf(“%f”, &a[i]);
7. }
Chỉ số của phần tử mảng phải thuộc kiểu nguyên (int, short int, long
int, char)
29
Lập trình C - Việt Nhật 2007 113
Khởi tạo mảng gồm các số chẵn từ 2 đến
20
#include <stdio.h>
#define arraySize 10
int main()
{
int s[ arraySize ]; // mảng S có 10 phần tử
int i;
for ( i = 0; i < arraySize; i++ ) // khởi tạo giá trị
s[ i ] = 2 + 2 * i;
printf("Phan tu t Gia trin");
for ( i = 0; i < arraySize; i++ )
printf("%dt%dn", i, s[i]);
return 0;
}
Lập trình C - Việt Nhật 2007 114
Nhập xuất dữ liệu cho mảng
#include <stdio.h>
#define SOTHANG 12
/* Lưu và hiển thị lượng mưa */
int main()
{
int solieu[SOTHANG];
int thang;
for ( thang=0; thang < SOTHANG; thang++ )
{
scanf("%d", &solieu[thang] );
}
...
Lập trình C - Việt Nhật 2007 115
Nhập xuất dữ liệu cho mảng
#include <stdio.h>
#define SOTHANG 12
...
/* Print from January to December */
for ( thang=0; thang< SOTHANG; thang++ )
{
printf( "%5d ” , solieu[thang] );
}
printf("n");
/* Print from December to January */
for ( thang = SOTHANG - 1; thang >= 0; thang-- )
{
printf( "%5d ” , solieu[month] );
}
printf("n");
return 0;
}
Lập trình C - Việt Nhật 2007 116
1 /*
2 Histogram printing program */
3 #include <stdio.h>
4 #define SIZE 10
5
6 int main()
7 {
8 int n[ SIZE ] = { 19, 3, 15, 7, 11, 9, 13, 5, 17, 1 };
9 int i, j;
10
11 printf( "%s%13s%17sn", "Element", "Value", "Histogram"
);12
13 for ( i = 0; i <= SIZE - 1; i++ ) {
14 printf( "%7d%13d ", i, n[ i ]) ;
15
16 for ( j = 1; j <= n[ i ]; j++ )
17 printf( "%c", '*' );
18
19 printf( "n" );
20 }
21
22 return 0;
23 }
Element Value Histogram
0 19 *******************
1 3 ***
2 15 ***************
3 7 *******
4 11 ***********
5 9 *********
6 13 *************
7 5 *****
8 17 *****************
9 1 *
30
Lập trình C - Việt Nhật 2007 117
Đổ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
111 2
51 2
21 2
10 2
0110111
Lập trình C - Việt Nhật 2007 118
Đổi số nguyên dương sang nhị phân
Lập trình C - Việt Nhật 2007 119
Khởi tạo ngẫu nhiên
Tạo dãy số nguyên dương 500 số. Mỗi số có giá trị trong
khoảng 1 đến 1000. In ra màn hình giá trị trung bình của dãy,
số phần tử trong dãy bằng, nhỏ hơn, lớn hơn giá trị trung
bình.
Tạo số ngẫu nhiên
#include <stdlib.h>
#include <time.h>
srand(time(NULL)); /* Khởi tạo bộ sinh số ngẫu nhiên, gọi một lần
duy nhất */
rand() ; sinh số nguyên ngẫu nhiên
1+ rand() % N
Lập trình C - Việt Nhật 2007 120
Sắp xếp mảng
Sắp xếp dữ liệu
Là một ứng dụng quan trọng
Hầu hết mọi cơ quan tổ chức cần sắp xếp dữ liệu
Sắp xếp nổi bọt
Duyệt mảng vài lần
So sánh các cặp phần tử liên tiếp
Nếu thứ tự tăng (hay bằng nhau) không thay đổi gì.
Nếu thứ tự giảm: tráo đổi hai phần tử.
Lặp lại bước trên cho mọi phần tử
31
Lập trình C - Việt Nhật 2007 121
Sắp xếp mảng
Lập trình C - Việt Nhật 2007 122
Sắp xếp mảng
Lập trình C - Việt Nhật 2007 123
Sắp xếp mảng
for (pass = 0; pass < arraySize - 1; pass++ )
for (j = 0; j < arraySize - 1; j++ )
// So sánh hai phần tử kề nhau và đổi chỗ chúng khi
// phần từ trước lớn hơn phần tử sau
if ( a[ j ] > a[ j + 1 ] ) {
hold = a[ j ];
a[ j ] = a[ j + 1 ];
a[ j + 1 ] = hold;
}
Lập trình C - Việt Nhật 2007 124
Mảng hai chiều
Một phần tử mảng có thể là một mảng
khác
Mảng của các mảng được gọi là mảng
nhiều chiều
Hàng 0
Hàng 1
Hàng 2
Cột 0 Cột 1 Cột 2 Cột 3
a[ 0 ][ 0 ]
a[ 1 ][ 0 ]
a[ 2 ][ 0 ]
a[ 0 ][ 1 ]
a[ 1 ][ 1 ]
a[ 2 ][ 1 ]
a[ 0 ][ 2 ]
a[ 1 ][ 2 ]
a[ 2 ][ 2 ]
a[ 0 ][ 3 ]
a[ 1 ][ 3 ]
a[ 2 ][ 3 ]
Chỉ số hàng
Tên mảng
Chỉ số cột
32
Lập trình C - Việt Nhật 2007 125
Mảng nhiều chiều
Khai báo mảng 2 chiều:
type name[row_size][column_size];
Khởi tạo
int SumSquares[2][3] = { {0,1,4}, {1,2,5} };
int SumSquares[2][3] = { {0,1,4} };
int SumSquares[ ][3] = { {0,1,4}, {1,2,5} };
int SumSquares[ ][3] = { {0,1, }, {1} };
int SumSquares[ ][3];
0 1 4
1 2 5
SumSquares
0 1 4 1 2 5
Lập trình C - Việt Nhật 2007 126
Ví dụ: Lượng mưa hàng năm
Bài toán: thao tác trên bảng vũ biểu
• input: tháng và năm
• output: lượng mưa trung bình vào năm, tháng đó
0 1 2 3 4 5 6 7 8 9 10 11
0 30 40 75 95 130 220 210 185 135 80 40 45
1 25 25 80 75 115 270 200 165 85 5 10 0
2 35 45 90 80 100 205 135 140 170 75 60 95
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
nam
thang
Lượng mưa trung bình năm (mm)
Lập trình C - Việt Nhật 2007 127
#define NYEARS 5
#define NMONTHS 12
int main()
{
int table[NYEARS][NMONTHS] =
{
{30,40,75,95,130,220,210,185,135,80,40,45},
{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}
};
int year, month;
printf("Enter year and month: "); scanf("%d %d", &year, &month);
if ((0 <= year) && (year < NYEARS) &&
(0 <= month) && (month < NMONTHS))
printf("Rainfall for year %d month %d is %d", year, month,
table[year-1][month-1]);
else
{
printf("Data non available");
}
return 0;
}
Lập trình C - Việt Nhật 2007 128
Nhập Mảng 2 chiều
1. #define MAX_STUDENT 5
2. #define MAX_SUBJECT 6
3. int StudentScore[MAX_STUDENT][MAX_SUBJECT];
4. void read_Score(int Score[MAX_STUDENT][MAX_SUBJECT],
int nStudents, int nSubjects)
5. {
6. int i,j;
7. for(i=0; i<nStudents; i++)
8. for(j=0; j<nSubjects; j++)
9. scanf(“%d”, &Score[i][j]);
10. }
33
Lập trình C - Việt Nhật 2007 129
Biểu diễn mảng 2 chiều
0 1 4
1 2 5
StudentScores
0 1 4 1 2 5
? ? ?
? ? ?
? ? ? ? ? ?
? ? ? ? ? ?
? ? ? ? ? ?
? ? ? ? ? ?
Student1 Student2
Student1
Student2
Student3
Student4
Student5
Lập trình C - Việt Nhật 2007 130
Bài tập
Viết chương trình nhập vào một dãy số theo thứ tự tăng, nếu
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
cho không thay đổi tính chất của dãy. In lại dãy mới để kiểm
tra.
Viết chương trình nhập vào một ma trận nguyên m hàng n
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
Ngôn ngữ lập trình C
Hàm (function)
Cao Tuấn Dũng
2007
Lập trình C - Việt Nhật 2007 132
Lập trình cấu trúc
Với chương trình máy tính
Với chương trình đơn giản, tất
cả các xử lý nên được đặt
trong hàm main.
Chia để trị: Phân chia chương
trình thành các phần nhỏ - các
chương trình con (routine) hay
còn gọi là các hàm (function)
Chương trình con A
Các biến
Câu lệnh
Kết thúc chương trình con A
Chương trình con B
Các biến
Câu lệnh
Kết thúc chương trình con B
Chương trình
Trong thực tế: Sản xuất bằng cách lắp ghép các module: các
module được lắp ghép lại thành sản phẩm, các module có thể được
cải tiến nhưng không ảnh hưởng đến các module khác trong sản
phẩm.
34
Lập trình C - Việt Nhật 2007 133
Hàm – Mô đun hóa chương trình
Trong C mọi chương trình con là hàm, không có sự phân biệt giữa
hàm và thủ tục.
Cách tiếp cận phân tích bài toán theo hướng top-down: xác định
chức năng của các hàm.
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
gọi lẫn nhau và truyền các thông tin qua lại giữa các hàm.
Các hàm có thể được dùng lại nhiều lần thành lập các thư viện
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 134
Hàm
Là một đoạn mã lệnh độc lập, được đặt tên, thực hiện một
nhiệm vụ cụ thể và có thể trả về một giá trị cho chương trình
gọi 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ý
Thống nhất các đoạn mã tương tự nhau, sử dụng nhiều lần trong
một chương trình
Tái sử dụng mã lệnh trong nhiều hơn một chương trình
Lập trình C - Việt Nhật 2007 135
Các hàm toán học
ceil(9.2) bằng 10.0
ceil(-9.8) bằng -9.0
làm tròn số
nguyên
nhỏ nhất
không lớn
hơn x
ceil(x)
nếu x≥0 thì fabs(x)
bằng x
nếu x<0 thì fabs(x)
bằng -x
giá trị tuyệt
đối của x
fabs(x)
log10(1.0) bằng 0.0
log10(10.0) bằng 1.0
logarithm
thập phân
(cơ số 10)
của x
log10(x)
log(2.718282) bằng
1.0
logarithm
tự nhiên
(cơ số e)
của x
log(x)
exp(1.0) bằng
2.718282
hàm mũ exexp(x)
sqrt(9.00) bằng 3.0Căn bậc 2
của x
sqrt(x)
Ví dụMô tảHàm
tan(0.0) bằng 0.0tan của x (x theo
radian)
tan(x)
cos(0.0) bằng 1.0cos của x (x theo
radian)
cos(x)
sin(0.0) bằng 0.0sin của x (x theo
radian)
sin(x)
fmod(13.657,2.333) bằng
1.992
phần dư của phép
chia x cho y
fmod(x,y
)
pow(2,7) bằng 128x mũ y (xy)pow(x,y)
floor(9.2) bằng 9.0
floor(-9.8) bằng -10.0
làm tròn số nguyên
lớn nhất không nhỏ
hơn x
floor(x)
Lập trình C - Việt Nhật 2007 136
Ví dụ đầu tiên: Hàm lập phương
Cho vao mot so nguyen: 6
Gia tri lap phuong cua 6 la 216
#include <stdio.h>
/* nguyen mau ham*/
int cube(int x);
void main() {
int a, kq;
printf("Cho mot so nguyen:"); scanf("%d", &a);
kq=cube(a);
printf("n Gia tri lap phuong cua %d la %d", a, kq);
}
/* Ham tinh lap phuong */
int cube(int x) {
int x3;
x3 = x*x*x;
return x3;
}
35
Lập trình C - Việt Nhật 2007 137
Hoạt động của hàm
Các lệnh trong hàm chưa được
thực hiện cho tới khi hàm được
gọi. Khi hàm được gọi điều
khiển thực hiện các lệnh được
chuyển cho hàm.
Chương trình gửi thông tin đến
hàm: các đối số
Hàm gửi lại thông tin: giá trị trả
về
Bốn bước cơ bản khi gọi hàm:
Chương trình
Khai báo Biến;
Lệnh;
….
Lời gọi hàm
Lệnh
…….
Kết thúc
Hàm
danh sách đối số
Giá trị trả về
Cấp phát bộ nhớ cho các đối số và biến cục bộ
Gán giá trị của các tham số thực cho đối số tương ứng
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 138
Khai báo hàm
Nguyên mẫu hàm (prototype): nguyên mẫu hàm cho chương
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
số, kiểu giá trị trả về cụ thể
Cú pháp:
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);
int Min(int x, int y);
Nguyên mẫu hàm chỉ cần thiết khi phần định nghĩa hàm nằm
phía sau lời gọi hàm trong chương trình.
Nguyên mẫu hàm được chương trình dịch sử dụng để kiểm
tra tính hợp lệ của hàm
Nguyên mẫu hàm phải khớp với định nghĩa hàm
Lập trình C - Việt Nhật 2007 139
Tệp tiêu đề
Chứa các nguyên mẫu hàm của các hàm trong thư viện
stdio.h, stdlib.h, math.h, time.h
Chứa nguyên mẫu các hàm do người dùng định nghĩa
Sử dụng tệp tiêu đề do người dùng định nghĩa
Khai báo các hàm vào lưu trong file riêng
Đặt tên filename.h
Khai báo sử dụng các hàm này ở chương trình khác
#include "filename.h"
Sử dụng lại các hàm đã khai báo
Lập trình C - Việt Nhật 2007 140
Định nghĩa hàm
Nơi cài đặt thực sự mã cho một hàm
Dòng đầu tiên tương tự như nguyên mẫu hàm, tuy nhiên
chỉ định rõ danh sách đối số
không kết thúc bằng dấu ;
Phần tiếp theo là thân hàm
36
Lập trình C - Việt Nhật 2007 141
Các thành phần của hàm
Tên hàm (name)
danh sách tham số (list of parameters)
kiểu trả về (return type)
thân hàm (function body)
lệnh trả về (return)
<return_type> function_name (<list_of_parameters>)
Các hàm phải được khai báo trước khi được gọi thi hành.
giao diện
(interface) của hàm
Lập trình C - Việt Nhật 2007 142
Hàm radian
Hàm chuyển đổi số đo góc từ độ sang radian
#include <stdio.h>
#include <math.h>
#define PI 3.14159
/* nguyen mau ham*/
float Radian(float x);
void main() {
float ang, adj, opp;
printf("Nhap gia tri cua goc (theo do:"); scanf("%f", &ang);
printf("Nhap canh ke:"); scanf("%f", &adj);
opp= adj*tan(Radian(ang));
printf("Canh doi la: %fn", opp);
}
/* 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 143
Thành phần của hàm – Tên hàm
Tên hàm là một định danh (identifier), do đó nó tuân theo các
quy định của ngôn ngữ C cho định danh. (xem bảng các toán
tử)
Nên đặt tên có ý nghĩa.
Không đặt tên trùng với tên các hàm hệ thống trong C hoặc
các từ khóa của C.
Lập trình C - Việt Nhật 2007 144
Danh sách tham số
Danh sách tham số xác định các đối số được đưa vào hàm.
Các đối số được khai báo trong phần mô tả cài đặt của hàm
thì được gọi là các tham số hình thức (formal parameters).
Mỗi tham số hình thức là một cặp: <type> <identifier>. Từ
khoá void có thể được dùng nếu không có tham số hình thức
nào cần khai báo. Các tham số trong các hàm khác nhau có
thể trùng tên.
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.
37
Lập trình C - Việt Nhật 2007 145
Ví dụ
/* ... */
1. float max(float x, float y)
2. {
3. return (x > y ? x : y);
4. }
/*…*/
6. int main()
7. {
8. float z = 4.7;
9. float x = max(4.5, z);
10. }
Lập trình C - Việt Nhật 2007 146
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
giá trị: giá trị trả về.
Chương trình gọi hàm có thể sử dụng giá trị trả về.
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.
Kiểu int sẽ là kiểu của trị trả về nếu không chỉ rõ kiểu giá trị
trả về trong khai báo hàm.
ví dụ:
afunction() { /*…*/ }
Lập trình C - Việt Nhật 2007 147
Thân hàm (function body)
{ /* các đoạn mã trong thân hàm */ }
Các biến có thể được khai báo bên trong hàm biến cục bộ
(local variable)
Biến cục bộ không được trùng tên với tham số hình thức
trong khai báo hàm.
Các biến cục bộ chỉ có giá trị trong phạm vi của hàm.
Lập trình C - Việt Nhật 2007 148
Hàm tìm USCLN giữa hai số
int USCLN(int a, int b)
{
a=abs(a);
b=abs(b);
while(a!=b)
{
if(a>b)
a=a-b;
else
b=b-a;
}
return a;
}
38
Lập trình C - Việt Nhật 2007 149
Phạm vi truy cập của biến
Phạm vi truy cập (scope) của biến xác định vùng chương
trình có thể truy cập đến biến.
Biến được khai báo trong khối lệnh (nằm giữa { }) có thể
được truy cập bởi các lệnh nằm trong cùng khối và các lệnh
thuộc các khối con.
Biến được khai báo “ngoài cùng” có phạm vi truy cập trong
toàn chương trình biến toàn cục (global variables).
Biến cục bộ (local variable) được khai báo và sử dụng trong
phạm vi một khối lệnh và các khối lệnh con.
Biến thuộc phạm vi trong cùng được tham chiếu đến đầu tiên.
Lập trình C - Việt Nhật 2007 150
Ví dụ
1. int i=1; /* i là biến toàn cục vì nằm ở ngoài các khối lệnh */
2. { /* block A */
3. int i=2;
4. printf (“%dn”, i); /* outputs 2 */ }
5. { /* Block B */
6. int i=3;
7. printf (“%dn”, i); /* outputs 3 */
8. { /* Block C */
9. int i=4;
10. printf (“%dn”, i); /* outputs 4 */ }
11. { /* Block D */
12. printf (“%dn”, i); /* outputs 3 */ }
13. }
14. { /* Block E */
15. printf (“%dn”, i); /* outputs 1 */ }
Lập trình C - Việt Nhật 2007 151
Phạm vi của biến
Lập trình C - Việt Nhật 2007 152
Biến tự động ( automatic)
Biến automatic được khai báo trong một hàm và nó là
biến cục bộ trong hàm này, tức là phạm vi của nó chính
là hàm mà trong đó biến này được khai báo.
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.
39
Lập trình C - Việt Nhật 2007 153
Cấp lưu trữ và phạm vi các đối tượng
Cấp lưu trữ ( storage class): thời gian sống + phạm vi
Có tất cả bốn cấp lưu trữ trong C: tự động, toàn cục (ngoài),
tĩnh và register được khai bao thông qua các từ khóa auto,
extern, static và register.
Mặt khác cũng lưu ý rằng cấp lưu trữ của một biến đôi khi
được xác định bởi chính vị trí khai báo của biến đó trong
chương trình. Trong các trường hợp khác người ta dùng
bốn từ khóa nói trên trong khai báo
Ví dụ:
auto int a,b,c;
extern float r1,r2;
static int count = 0;
extern char star;
Lập trình C - Việt Nhật 2007 154
Biến tự động
/* Hoán vị giá trị hai số nguyên */
#include <stdio.h>
void swap(auto int* , auto int* ); /* Hàm nguyên mẫu */
main()
{
auto int x, y;
x = 10; y = 20;
printf("Ban đầu x = %d, y = %d", x, y);
swap( &x, &y);
printf("Sau đó x = %d, y = %d", x, y);
}
/* Định nghĩa hàm swap */
void swap( auto int* a, auto int* b)
{
auto int temp;
temp = *a;
*a = *b;
*b = temp;
}
Lập trình C - Việt Nhật 2007 155
Biến toàn cục (global, external).
Phạm vi (scope) của biến toàn cục là toàn bộ chương
trình
Các biến toàn cục được định nghĩa bên ngoài mọi hàm
do vậy chúng có tiềm năng sử dụng cho nhiều hàm.
Biến toàn cục còn đóng một vai trò quan trọng khi phát
triển các ứng dụng lớn, vì khi đó hiển nhiên là các
chương trình nguồn để trên nhiều tệp khác nhau.
Từ khoá extern thông báo với trình biên dịch rằng tên
và kiểu của biến đặt sau đã được khai báo ở đâu đó rồi.
Lập trình C - Việt Nhật 2007 156
Biến toàn cục
#include <stdio.h>
long fibo(int count);
long f1=1,f2=1;/* khai báo biến toàn cục */
main()
{
int count,n;
printf("nn =");scanf("%d",&n);
for(count=1;count<=n; ++count)
printf("ni=%2d F=%ld", count,fibo(count));
}
long int fibo(int count)
{
long int f;
f=(count<3) ? 1: f1+f2;
f2=f1; f1=f;
return f;
}
40
Lập trình C - Việt Nhật 2007 157
Biến tĩnh (static)
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
trình.
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;
#include <stdio.h>
int inc(){
static int n=0;
n++;
return n;
}
void main(){
printf("n Ket qua inc lan 1: %d", inc());
printf("n Ket qua inc lan 2: %d", inc());
}
Ket qua inc lan 1: 1
Ket qua inc lan 2: 2
Lập trình C - Việt Nhật 2007 158
Biến tĩnh
#include<stdio.h>
long fibo(int count);
main()
{
int count,n;
printf("nn =");scanf("%d",&n);
for(count=1;count<=n; ++count)
printf("ni=%2d F=%ld", count,fibo(count));
}
long int fibo(int count)
{
static long int f1=1,f2=1;
long int f;
f=(count<3) ? 1: f1+f2;
f2=f1; f1=f;
return f;
}
Nếu bỏ từ khóa static ở trong thân hàm main ????
Lập trình C - Việt Nhật 2007 159
Biến register
Từ khoá register được đặt trước khai báo của các biến
tự động để đề nghị trình biên dịch duy trì giá trị biến đó
trong thanh ghi của máy tính, giúp nâng cao tốc độ thực
hiện.
Trình biên dịch có thể bỏ qua từ khoá register khi không
còn đủ thanh ghi nữa và nói chung chúng chỉ áp dụng
cho các biến kiểu int và char.
Ví dụ sau đề nghị trình biên dịch đặt biến counter vào
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 160
Lệnh return
kết thúc hàm và trả quyền điều khiển về cho phần chương
trình có lời gọi hàm.
cú pháp:
return Expr;
hoặc
return;
hàm tự kết thúc khi thực hiện hết lệnh cuối cùng.
41
Lập trình C - Việt Nhật 2007 161
Một số ví dụ
Tính tổng các số nguyên tố nhỏ hơn n
Lập trình C - Việt Nhật 2007 162
Một số ví dụ minh họa
Chương trình tính tổng các số nguyên tố
Lập trình C - Việt Nhật 2007 163
Một số bài tập
Viết chương trình tính tiền thuê máy dịch vụ Internet và
in ra màn hình kết quả. Dữ liệu nhập vào là giờ bắt đầu
thuê (GBD), giờ kết thúc thuê (GKT) và số máy thuê
Điều kiện dữ liệu: 6 <=GBD < GKT <= 21, Giờ nguyên.
Đơn giá 2500 đ cho mỗi giờ trước 5h30 và 3500 cho mỗi giờ sau 5h30
Nhập vào ba số thực a, b, c kiểm tra xem chúng có phải
là ba cạnh một tam giác không. Nếu có tính diện tích, độ
dài 3 đường cao.
Lập trình C - Việt Nhật 2007 164
Truyền tham số khi gọi hàm
Truyền tham chiếu (call by reference): các tham chiếu đến
các tham số hình thức là tham chiếu đến các đối số. Giá trị
của các đối số có thể được thay đổi từ xử lý bên trong hàm.
Truyền giá trị (call by value): các đối số đưa vào hàm được
chép vào các tham số hình thức. Các giá trị của các đối số
được sử dụng trong hàm nhưng những thay đổi của tham số
hình thức trong hàm không làm thay đổi giá trị của các đối số
truyền vào.
42
Lập trình C - Việt Nhật 2007 165
Truyền giá trị
1. /* Swapping routine that doesn’t work */
2. #include <stdio.h>
3. void Swap(int x, int y)
{ int Temp;
4. Temp = x;
5. x = y;
6. y = Temp;
7. }
8. main(void)
9. { int Left, Right;
10. Left = 5; Right = 7;
11. Swap(Left, Right);
12. printf(“Left = %d, Right = %dn”, Left, Right);
13. }
Lập trình C - Việt Nhật 2007 166
Tại sao truyền giá trị không làm
thay đổi giá trị đối số
main#1::Left = 5
main#1::Right = 7
Swap#1::Temp = ?
Swap#1::x = 5
Swap#1::y = 7
M1
M2
M3
M4
M5
main#1::Left = 5
main#1::Right = 7
Swap#1::Temp = 5
Swap#1::x = 7
Swap#1::y = 5
M1
M2
M3
M4
M5
Lập trình C - Việt Nhật 2007 167
Truyền bằng tham chiếu
/* Swapping routine that does work */
#include <stdio.h>
void Swap(int &x, int &y)
{
int Temp;
Temp = x;
x = y;
y = Temp;
}
main(void)
{
int Left, Right;
Left = 5; Right = 7;
Swap(Left, Right);
printf(“Left = %d, Right = %dn”, Left, Right);
}
Lập trình C - Việt Nhật 2007 168
Tại sao truyền bằng tham chiếu làm
thay đổi giá trị đối số
main#1::Left = 5
main#1::Right = 7
Swap#1::Temp = ?
Swap#1::x
Swap#1::y
M1
M2
M3
M4
M5
main#1::Left = 7
main#1::Right = 5
Swap#1::Temp = 5
Swap#1::x
Swap#1::y
M1
M2
M3
M4
M5
43
Lập trình C - Việt Nhật 2007 169
Dừng chương trình và mã lỗi
Thông thường main trả về giá trị kiểu int
có thể sử dụng khai báo: void main()
Nên sử dụng giá trị trả về để kiểm soát xử lý của chương
trình.
Sử dụng hàm exit(<exitcode>); để dừng chương trình và trả
về mã lỗi.
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 170
Truyền mảng cho hàm
Lập trình C - Việt Nhật 2007 171
Truyền mảng cho hàm
Lập trình C - Việt Nhật 2007 172
1 /* Fig. 6.13: fig06_13.c
2 Passing arrays and individual array elements to functions */
3 #include <stdio.h>
4 #define SIZE 5
5
6 void modifyArray( int [], int ); /* appears strange */
7 void modifyElement( int );
8
9 int main()
10 {
11 int a[ SIZE ] = { 0, 1, 2, 3, 4 }, i;
12
13 printf( "Effects of passing entire array call "
14 "by reference:nnThe values of the "
15 "original array are:n" );
16
17 for ( i = 0; i <= SIZE - 1; i++ )
18 printf( "%3d", a[ i ] );
19
20 printf( "n" );
21 modifyArray( a, SIZE ); /* passed call by reference */
22 printf( "The values of the modified array are:n" );
23
24 for ( i = 0; i <= SIZE - 1; i++ )
25 printf( "%3d", a[ i ] );
26
27 printf( "nnnEffects of passing array element call "
28 "by value:nnThe value of a[3] is %dn", a[ 3 ] );
29 modifyElement( a[ 3 ] );
30 printf( "The value of a[ 3 ] is %dn", a[ 3 ] );
31 return 0;
32 }
44
Lập trình C - Việt Nhật 2007 173
Hàm truy cập, in Mảng 2 chiều
1. void print_Score(int Score[MAX_STUDENT][MAX_SUBJECT],
int nStudents, int nSubjects)
2. {
3. int i,j;
4. for(i=0; i<nStudents; i++)
5. {
6. for(j=0; j<nSubjects; j++)
7. printf(“%2dt”, &Score[i][j]);
8. printf(“n”);
9. }
10. }
Lập trình C - Việt Nhật 2007 174
Chương trình
1. main(void)
2. {
3. int nStudents, nScores;
4. scanf(“%d %d”, &nStudents, &nScores);
5. if(nStudents <= MAX_STUDENT &&
nScores <= MAX_SCORES)
6. read_Score(StudentScore, nStudents, nScores);
7. print_Score (StudentScore, nStudents, nScores);
8. return 0;
9. }
Lập trình C - Việt Nhật 2007 175
Sử dụng hàm như tham số.
Ví dụ:
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);
…
Ngôn ngữ lập trình C
Đệ Quy
Cao Tuấn Dũng
2007
45
Lập trình C - Việt Nhật 2007 177
Nguyên lý
Trong C cho phép trong thân một hàm có thể gọi ngay
đến chính nó, cơ chế này gọi là đệ qui.
Có thể định nghĩa hàm đệ qui là hàm sẽ gọi đến chính
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ó
những điểm chung sau.
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
nhất (hay còn gọi là trường hợp cơ sở).
Nếu hàm được gọi trong các trường hợp phức tạp hơn, hàm đệ qui sẽ
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
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
ban đầu.
Để đả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
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
trường hợp cơ sở.
Lập trình C - Việt Nhật 2007 178
Tính giai thừa
Ví dụ: Viết chương trình nhập số tự nhiên n và tính giai thừa : n!.
Giải quyết bài toán bằng vòng lặp
1. #include <stdio.h>
2. unsigned long int factorial(int n)
3. { unsigned long f = 1;
4. for (int i = 1; i<=n; i++)
5. f *= i;
6. return f;
7. }
8. int main(void)
9. { int n;
10. printf(“Nhap n:”); scanf(“%d”, &n);
printf(“n! = %d! = %ln”, n, factorial(n));
11. return 0;
12. }
Lập trình C - Việt Nhật 2007 179
Tính giai thừa
Tuy nhiên cũng có thể định nghĩa đệ qui hàm giai thừa như sau :
nếu n>0 n! = n * (n-1)!
nếu n=0 n! = 0! = 1
1. #include <stdio.h>
2. unsigned long int factorial(int n)
3. { if(n==0)
4. return 1;
5. return (n* factorial(n-1));
6. }
7. int main(void)
8. { int n;
9. printf(“Nhap n:”); scanf(“%d”, &n);
10. printf(“n! = %d! = %ln”, n, factorial(n));
11. return 0;
12. }
Lập trình C - Việt Nhật 2007 180
Dãy Fibonaci
Một ví dụ thứ hai dùng đệ qui là tính dẫy số
Fibonaci
Dãy số Fibonaci gồm những số
0, 1, 1, 2, 3, 5, 8, 13, 21 ...
Bắt đầu từ hai số 0 và 1, tiếp sau đó các số Fibonaci
sau bằng tổng của 2 số Fibonaci trước nó.
Dẫy Fibonaci có thể định nghĩa đệ qui như sau:
fibonaci(0) = 0; fibonaci(1) = 1;
fibonaci(n) = fibonaci(n-1) + fibonaci(n-2) ∀n>1
46
Lập trình C - Việt Nhật 2007 181
Dãy Fibonaci (tiếp)
/* Tính dẫy số Fibonaci phương pháp đệ qui */
#include <stdio.h>
long fibo( long ); /* Hàm nguyên mẫu */
main()
{
long result, n;
printf("Hãy nhập vào một số nguyên : ");
scanf("%ld", &n);
result = fibo(n);
printf("Fibonaci thứ %ld là : %ldn", number, result);
return 0;
}
/* Định nghĩa đệ qui hàm fibonaci */
long fibo( long n)
{
if ( n = 0 || n = 1 )
return n;
else
return fibo(n-1) + fibo(n-2);
}
Lập trình C - Việt Nhật 2007 182
Lời gọi hàm đệ quy và Điều kiện
dừng của thuật giải đệ quy
Bài toán giải bằng thuật giải đệ quy phải có điều kiện dừng.
Thuật toán đệ quy trên máy tính có thể bị giới hạn bởi dung
lượng bộ nhớ do lời gọi hàm liên tiếp.
factorial (4) factorial (3) factorial (2) factorial (1)
main
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 183
Bài toán Tháp Hà Nội
Có 3 cái cột và một chồng đĩa ở cột thứ nhất. Hãy chuyển
chồng đĩa sang cột thứ ba với điều kiện mỗi lần di chuyển chỉ
một đĩa và các đĩa bé luôn nằm trên đĩa lớn.
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 184
Thuật giải
Chuyển (n-1) đĩa sang cột trung gian.
Chuyển đĩa lớn nhất sang cột đích.
Chuyển (n-1) đĩa từ cột trung gian sang cột đích.
47
Lập trình C - Việt Nhật 2007 185
Cài đặt bằng đệ quy
1. MoveDisk(disk_number, starting_post, target_post,
intermediate_post)
2. {
3. if(disk)number > 1)
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);
8. }
9. else
10. printf(“Move disk number 1 from post %d to post %d.n”,
starting_post, target_post);
11. }
Ngôn ngữ lập trình C
CON TRỎ
Cao Tuấn Dũng
2007
Lập trình C - Việt Nhật 2007 187
Con trỏ
Biến con trỏ
Khai báo biến con trỏ
Địa chỉ và giá trị
Truyền tham chiếu trong lời gọi hàm
Lập trình C - Việt Nhật 2007 188
Truyền tham số qua trị
1. #include <stdio.h>
2. void move_one(int x, int y)
3. {
4. x = x-1;
5. y = y+1;
6. }
7. int main(void)
8. {
9. int a = 4, b = 7;
10. move_one(a, b);
11. print(“%d, %dn”, a, b);
12. return 0;
13. }
48
Lập trình C - Việt Nhật 2007 189
Bộ nhớ
main#1::a
main#1::b
move_one#1::x
move_one#1::y
M1
M2
M3
M4
Lập trình C - Việt Nhật 2007 190
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ị.
Khi chúng ta khai báo một biến, máy sẽ cấp phát cho biến đó
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
bytes .v.v
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.
Bằng cách nào?
x
địa chỉtên biến
giá trị41024:
Lập trình C - Việt Nhật 2007 191
Kiểu dữ liệu Con trỏ
Địa chỉ của biến được tính là số thứ tự của byte đầu tiên trong
dãy các bytes được cấp cho biến. Tuỳ theo kích thước bộ
nhớ người ta biểu diễn địa chỉ bằng hai hoặc bốn bytes.
Một biến kiểu con trỏ (pointer) chứa một tham chiếu
(reference) đến một biến loại khác. Nói khác đi, biến con trỏ
chứa địa chỉ ô nhớ của một biến.
int x;
int* xp;
/* con tro tro toi mot so nguyen */
x = 4;
xp = &x;
41024:
x
1024:
xp
Lập trình C - Việt Nhật 2007 192
Khai báo, toán tử và sử dụng trong
hàm
Khai báo kiểu dữ liệu con trỏ:
int * “con trỏ đến kiểu int”
float * “con trỏ đến kiểu float”
char * “con trỏ đến kiểu character”
Toán tử
& địa chỉ của một đối tượng
* giá trị của vùng nhớ biến con trỏ chỉ đến
Con trỏ được dùng như tham số hình thức trong khai báo
hàm để truyền và lấy các đối số có giá trị thay đổi.
49
Lập trình C - Việt Nhật 2007 193
Con trỏ và địa chỉ biến
Địa chỉ của hai biến ký tự liên tiếp sẽ cách nhau một byte
trong khi địa chỉ của hai biến nguyên liên tiếp cách nhau hai
byte còn địa chỉ của hai biến thực liên tiếp cách nhau tới bốn
bytes.
Người ta phân biệt các con trỏ theo các kiểu địa chỉ chứa
trong các con trỏ: con trỏ kiểu nguyên dùng để chứa địa chỉ
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.
... p a
Lập trình C - Việt Nhật 2007 194
Sử dụng Con trỏ
Truy cập vùng nhớ được chỉ bởi một con trỏ
int x=1,y=2,z[10];
int *pi; /*pi là một biến con trỏ có kiểu nguyên*/
pi = &x; /*Địa chỉ của x được gán cho pi, và pi trỏ tới biến x*/
y=*pi; /*y có giá trị bằng 1*/
*pi=15; /*Từ bây giờ x có giá trị bằng 15*/
pi=&z[0]; /*Từ đây pi chứa địa chỉ của z[0], tức là địa chỉ của mảng z*/
151024:
x
1024:
pi
Lập trình C - Việt Nhật 2007 195
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
,long) để cho kết quả là một con trỏ cùng kiểu. Xét ví dụ sau:
int *x=2,*px,*py;
px = &x;
py = px+1; /*py trỏ đến số nguyên nằm sau x
trong bộ nhớ*/
Phép trừ hai con trỏ cùng kiểu được coi là hợp lệ và cho kết
quả là một số nguyên biểu thị "khoảng cách" (ở đây bằng số
phần tử ) giữa hai biến con trỏ.
Ví dụ 3.?. Quay về ví dụ trên, câu lệnh
x=py-px;
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
phép nhân, chia hai con trỏ.
Lập trình C - Việt Nhật 2007 196
Các phép toán trên con trỏ
Có thể áp dụng các phép so sánh, phép gán cho các con trỏ.
Trong các phép toán này đòi hỏi các toán hạng con trỏ phải
có cùng kiểu. Mọi sự chuyển đổi kiểu tự động từ các kiểu
khác thành các kiểu con trỏ phải luôn luôn được cân nhắc và
hỏi ý kiến bởi chương trình biên dịch.
int *addr1;
char *addr2;
addr1=0; /*Chương trình dịch sẽ đưa ra
một warning */
addr2 = (char *)0; /*Phép gán này hợp lệ*/
if(addr1==addr2) /*Gây ra một warning*/
{
addr1 = (int *)addr2; /*Hợp lệ*/
}
50
Lập trình C - Việt Nhật 2007 197
Sử dụng con trỏ như tham số
#include <stdio.h>
void move_one(int* xPtr, int* yPtr)
{
*xPtr = *xPtr-1;
*yPtr = *yPtr+1;
}
int main(void)
{
int a, b;
a=4; b=7;
move_one(&a, &b);
print(“%d, %dn”, a, b);
return 0;
}
Lập trình C - Việt Nhật 2007 198
Bộ nhớ
main#1::a
main#1::b
move_one#1::xPtr
move_one#1::yPtr
M1
M2
M3
M4
Lập trình C - Việt Nhật 2007 199
Hàm swap
void swap(int *px, int *py)
{
int temp;
temp = *px;
*px = *py;
*py = temp;
}
main(void)
{
int a, b;
a=2; b=9;
swap(&a, &b);
}
Lập trình C - Việt Nhật 2007 200
Bài tập
Lập chương trình:
- Nhập từ bàn phím một số nguyên.
- Đưa ra màn hình dưới dạng hexadecimal nội dung byte cao và
byte thấp của số nguyên.
#include <stdio.h>
main() {
int n;
char *pc;
printf("Nhap vao so nguyen : ");
scanf("%d",&n);
pc=(char *)&n; /*pc trỏ đến byte cao cua n, pc+1
trỏ đến byte thấp*/
printf("Byte cao %x ,byte thap %x",*pc ,*pc++);
}
51
Lập trình C - Việt Nhật 2007 201
Con trỏ void
Kiểu dữ liệu void
Giá trị trả về của hàm: không cần giá trị trả về
Tham số trong hàm: không có tham số
Con trỏ kiểu void được khai báo như sau:
void *tên_con_trỏ;
Đâ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
lệ:
void *px,*py;
int x=1;
float y=0.1;
px=&x;
py=&y;
Lập trình C - Việt Nhật 2007 202
Con trỏ và mảng một chiều
Xét câu lệnh: int a[10];
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à:
a tương đương với &a[0]
Nếu pa là một con trỏ kiểu nguyên,
int *pa;
Khi đó phép gán
pa = &a[0];
Đem pa trỏ đến phần tử
thứ nhất (có chỉ số là 0) của a;
nghĩa là pa chứa địa chỉ của a[0].
a[9]a[8]a[7]a[6]a[5]a[4]a[3]a[2]a[1]a[0]
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
pa
Lập trình C - Việt Nhật 2007 203
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]
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.
... ...
a[9]pa+9
pa+1 a[1]
a[0]pa
Lập trình C - Việt Nhật 2007 204
Con trỏ và mảng một chiều
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ệ.
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).
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.
Tính tổng của chúng
52
Lập trình C - Việt Nhật 2007 205
#include <stdio.h>
main()
{
float a{5],s;
int i
for(i=0;i<5;i++)
[
printf("na[%d] =
",i) ;
scanf("%f",a+i);
}
s=0;
for(i=0;i<5;i++)
s+=a[i];
printf("n tong la:
%10.2f",s);
}
#include <stdio.h>
main()
{
float a{5],s;
int i
for(i=0;i<5;i++)
[
printf("na[%d] =
",i) ;
scanf("%f",&a[i]);
}
s=0;
for(i=0;i<5;i++)
s+=a[i];
printf("n tong la:
%10.2f",s);
}
Lập trình C - Việt Nhật 2007 206
#include <stdio.h>
main()
{
float a{5],s,*p;
int i; p=a;
for(i=0;i<5;i++)
[
printf("na[%d] =
",i) ;
scanf("%f",p+i);
}
s=0;
for(i=0;i<5;i++)
s+=*(p+i);
printf("n tong la:
%10.2f",s);
}
#include <stdio.h>
main()
{
float a{5],s, *p;
int i; p=a;
for(i=0;i<5;i++)
[
printf("na[%d] =
",i) ;
scanf("%f",&p[i]);
}
s=0;
for(i=0;i<5;i++)
s+=p[i];
printf("n tong la:
%10.2f",s);
}
Lập trình C - Việt Nhật 2007 207
Truyền mảng qua con trỏ
1. #include <stdio.h>
2. #define SIZE 5
3. void getArray(int *a, int size);
4. main()
5. {int an_array[SIZE];
6. getArray(an_array, SIZE);
7. return 0;
8. }
9. void getArray(int *a, int size)
10. {for(int i=0; i<size; i++) {
11. printf(“a[%d]=“);
12. scanf(“%d”, &a[i]);
13. }
14. }
Lập trình C - Việt Nhật 2007 208
Con trỏ và mảng nhiều chiều
Phép toán lấy địa chỉ nói chung không dùng được đối với các thành
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).
Phép cộng địa chỉ trong mảng hai chiều
Xét khai báo
float a[2][3];
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
thứ tự sau:
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
nó; phép cộng địa chỉ phải hiểu như sau: C cho rằng mảng hai chiều là
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ử
của nó là một dãy gồm 3 số thực. Vì vậy
a trỏ tới đầu hàng thứ nhất (phần tử a[0][0]
a+1 trỏ tới đầu hàng thứ hai (phần tử a[1][0]
53
Lập trình C - Việt Nhật 2007 209
Con trỏ và mảng hai chiều
Để truy xuất vào các phần tử của mảng hai chiều ta
vẫn có thể dùng con trỏ theo cách sau:
float *p, a[2][3];
p=(float*)a;
Khi đó
p trỏ tới a[0][0]
p+1 trỏ tới a[0][1]
p +2 trỏ tới a[0][2]
p +3 trỏ tới a[1][0]
p +4 trỏ tới a[1][1]
p +5 trỏ tới a[1][2]
Lập trình C - Việt Nhật 2007 210
Tính địa chỉ từng phần tử
Để ý 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
a trỏ đến dòng thứ nhất
a+1 trỏ đến dòng thứ hai
Để tính toán được địa chỉ của phần tử ở dòng i cột j chúng ta phải
dùng phép chuyển đổi kiểu bắt buộc đối với a: (float * )a, đây là
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).
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ỉ
cuả thành phần a[0]..[0] (k chỉ số 0) là (type *)a, và địa chỉ của
a[i1][i2]...[ik] được tính như sau
( *)type a i n ij l k
j
k
l j
k
+ +
=
−
= +
∑ ∏1
1
1
Lập trình C - Việt Nhật 2007 211
Ví dụ: Đọc dữ liệu cho mảng hai chiều
#include <stdio.h>
main()
{
float a[2][3],*p;
int i,m,n;
p=(float*)a;
for(i=0;i<2;i++)
for(j=0;j<3;j++)
{
printf("a[%d][%d] = ",i,j);
scanf("%f",p+i*3+j);
}
for(i=0;i<2;i++)
for(j=0;j<3;j++)
{
printf("%6.2f",a[i][j]);
if(j==2) printf("n");
}
}
Lập trình C - Việt Nhật 2007 212
Mảng các con trỏ
Chúng ta có thể khai báo một mảng các con trỏ bằng câu
lệnh sau:
type *pointer_array[size];
ví dụ câu lệnh,
char *ma[10];
Sẽ khai báo một mảng 10 con trỏ char có thể được dùng để
khai báo một mảng để lưu trữ địa chỉ của mười chuỗi ký tự
nào đó.
Nếu các con trỏ được chuẩn bị để chỉ đến một biến nào đó đã
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
biến đó có liên tiếp hay không.
54
Lập trình C - Việt Nhật 2007 213
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ỉ
của các biến int có giá trị và vị trí bất kỳ. Chúng ta sẽ dùng một
hàm để sắp xếp lại các địa chỉ này trong mảng để sao cho cá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.
Không làm thay đổi vị trí hoặc thay đổi các giá trị của các biến
nhưng mảng vẫn có vẻ như là một mảng chỉ đến các giá trị đã
sắp xếp có thứ tự.
#include <stdio.h>
main() {
int i,j,*x;
int d=10,e=3,f=7;
int a=12,b=2,c=6;
int *ptr_array[6];
Lập trình C - Việt Nhật 2007 214
/*Gán các thành phần của mảng*/
ptr_array[0]=&a;
ptr_array[1]=&b;
ptr_array[2]=&c;
ptr_array[3]=&d;
ptr_array[4]=&e;
ptr_array[5]=&f;
/*Sắp xếp lại dãy số*/
for(i=0;i<5;i++)
for(j=i+1;j<6;j++)
if(*ptr_array[i]>*ptr_array[j])
{
x=ptr_array[i];
ptr_array[i]=ptr_array[j];
ptr_array[j]=x;
}
/*In kết quả sau khi sắp xếp*/
for(i=0;i<6;i++)
printf(" %d n",*ptr_array[i]);
}
Lập trình C - Việt Nhật 2007 215
Mảng nhiều chiều và mảng con trỏ
Giữa mảng nhiều chiều và mảng các con trỏ tồn tại nhiều
điểm khác nhau:
Mảng nhiều chiều thực sự là mảng có khai báo, do đó có chỗ đầy đủ cho
tất cả các phần tử của nó. Còn mảng các con trỏ chỉ mới có chỗ cho các
biến con trỏ mà thôi.
Việc sử dụng mảng các con trỏ có hai ưu điểm:
Việc truy xuất đến các phần tử là truy xuất gián tiếp thông qua các con
trỏ và như vậy, vị trí của các mảng con này có thể là bất kỳ, và chúng có
thể là những mảng đã có bằng cách xin cấp phát chỗ động hay bằng
khai báo biến mảng bình thường, tùy ý.
Các mảng con của nó được chỉ đến bởi các con trỏ, có thể có độ dài tùy
ý, hoặc có thể không có (nếu con trỏ đó không được chuẩn bị, hoặc
được gán bằng NULL).
Lập trình C - Việt Nhật 2007 216
Cấp phát động
Ý nghĩa của việc cấp phát bộ nhớ động là cho phép
chương trình sử dụng vừa đúng khối lượng bộ nhớ mà
chương trình cần, và khi không cần dùng tới nữa thì có
thể giải phóng để cho các công việc tiếp theo có thể sử
dụng được. Như vậy, cùng một vùng bộ nhớ có thể
được sử dụng cho các mục đích khác nhau trong thời
gian thực hiện của chương trình.
Vùng nhớ heap được sử dụng cho mục đích cấp phát
động các khối nhớ có kích thước thay đổi. Có nhiều cấu
trúc dữ liệu sử dụng cách cấp phát động, ta có thể liệt kê
một vài loại như: cây (tree), các loại danh sách liên kết.
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat
[C] giao trinh c   dhbk - viet nhat

More Related Content

What's hot

Bài giảng ngôn ngữ lập trình c c++ phạm hồng thái[bookbooming.com]
Bài giảng ngôn ngữ lập trình c c++   phạm hồng thái[bookbooming.com]Bài giảng ngôn ngữ lập trình c c++   phạm hồng thái[bookbooming.com]
Bài giảng ngôn ngữ lập trình c c++ phạm hồng thái[bookbooming.com]bookbooming1
 
Lesson 7 - Linux Shell Programming
Lesson 7 - Linux Shell ProgrammingLesson 7 - Linux Shell Programming
Lesson 7 - Linux Shell ProgrammingThang Man
 
Bài giảng Lập trình cơ bản - truongkinhtethucpham.com
Bài giảng Lập trình cơ bản - truongkinhtethucpham.comBài giảng Lập trình cơ bản - truongkinhtethucpham.com
Bài giảng Lập trình cơ bản - truongkinhtethucpham.commai_non
 
Bài 1: Khái niệm lập trình và ngôn ngữ lập trình
Bài 1: Khái niệm lập trình và ngôn ngữ lập trìnhBài 1: Khái niệm lập trình và ngôn ngữ lập trình
Bài 1: Khái niệm lập trình và ngôn ngữ lập trìnhChâu Trần
 
Lập trình C# 2008 cơ bản_Nhất Nghệ
Lập trình C# 2008 cơ bản_Nhất NghệLập trình C# 2008 cơ bản_Nhất Nghệ
Lập trình C# 2008 cơ bản_Nhất NghệTrần Thiên Đại
 
Bài giảng C - 01 - Giới thiệu
Bài giảng C - 01 - Giới thiệuBài giảng C - 01 - Giới thiệu
Bài giảng C - 01 - Giới thiệuDoan Trung Tung
 
Bai tap lap trinh c
Bai tap lap trinh  cBai tap lap trinh  c
Bai tap lap trinh ctiểu minh
 

What's hot (10)

Phạm văn ất
Phạm văn ấtPhạm văn ất
Phạm văn ất
 
Bài giảng ngôn ngữ lập trình c c++ phạm hồng thái[bookbooming.com]
Bài giảng ngôn ngữ lập trình c c++   phạm hồng thái[bookbooming.com]Bài giảng ngôn ngữ lập trình c c++   phạm hồng thái[bookbooming.com]
Bài giảng ngôn ngữ lập trình c c++ phạm hồng thái[bookbooming.com]
 
Lesson 7 - Linux Shell Programming
Lesson 7 - Linux Shell ProgrammingLesson 7 - Linux Shell Programming
Lesson 7 - Linux Shell Programming
 
Bài giảng Lập trình cơ bản - truongkinhtethucpham.com
Bài giảng Lập trình cơ bản - truongkinhtethucpham.comBài giảng Lập trình cơ bản - truongkinhtethucpham.com
Bài giảng Lập trình cơ bản - truongkinhtethucpham.com
 
Bài 1: Khái niệm lập trình và ngôn ngữ lập trình
Bài 1: Khái niệm lập trình và ngôn ngữ lập trìnhBài 1: Khái niệm lập trình và ngôn ngữ lập trình
Bài 1: Khái niệm lập trình và ngôn ngữ lập trình
 
C# cơ bản hay
C# cơ bản hayC# cơ bản hay
C# cơ bản hay
 
Lập trình C# 2008 cơ bản_Nhất Nghệ
Lập trình C# 2008 cơ bản_Nhất NghệLập trình C# 2008 cơ bản_Nhất Nghệ
Lập trình C# 2008 cơ bản_Nhất Nghệ
 
Baigiang01 mo dau
Baigiang01 mo dauBaigiang01 mo dau
Baigiang01 mo dau
 
Bài giảng C - 01 - Giới thiệu
Bài giảng C - 01 - Giới thiệuBài giảng C - 01 - Giới thiệu
Bài giảng C - 01 - Giới thiệu
 
Bai tap lap trinh c
Bai tap lap trinh  cBai tap lap trinh  c
Bai tap lap trinh c
 

Viewers also liked

Cafe revenue disbursement reports-2 years
Cafe revenue disbursement reports-2 yearsCafe revenue disbursement reports-2 years
Cafe revenue disbursement reports-2 yearsWMfood
 
Contract fsmc-2 years
Contract fsmc-2 yearsContract fsmc-2 years
Contract fsmc-2 yearsWMfood
 
RFP 2010-11 for Food Services
RFP 2010-11 for Food ServicesRFP 2010-11 for Food Services
RFP 2010-11 for Food ServicesWMfood
 
Pomptonian response to RFP 2010-11
Pomptonian response to RFP 2010-11Pomptonian response to RFP 2010-11
Pomptonian response to RFP 2010-11WMfood
 
Cafeteria outsourcing recommendation for BOE 30607
Cafeteria outsourcing recommendation for BOE 30607Cafeteria outsourcing recommendation for BOE 30607
Cafeteria outsourcing recommendation for BOE 30607WMfood
 
Cafeteria outsourcing analysis for BOE 30521
Cafeteria outsourcing analysis for BOE 30521Cafeteria outsourcing analysis for BOE 30521
Cafeteria outsourcing analysis for BOE 30521WMfood
 

Viewers also liked (7)

Cafe revenue disbursement reports-2 years
Cafe revenue disbursement reports-2 yearsCafe revenue disbursement reports-2 years
Cafe revenue disbursement reports-2 years
 
Contract fsmc-2 years
Contract fsmc-2 yearsContract fsmc-2 years
Contract fsmc-2 years
 
RFP 2010-11 for Food Services
RFP 2010-11 for Food ServicesRFP 2010-11 for Food Services
RFP 2010-11 for Food Services
 
Diseño web exposición
Diseño web exposiciónDiseño web exposición
Diseño web exposición
 
Pomptonian response to RFP 2010-11
Pomptonian response to RFP 2010-11Pomptonian response to RFP 2010-11
Pomptonian response to RFP 2010-11
 
Cafeteria outsourcing recommendation for BOE 30607
Cafeteria outsourcing recommendation for BOE 30607Cafeteria outsourcing recommendation for BOE 30607
Cafeteria outsourcing recommendation for BOE 30607
 
Cafeteria outsourcing analysis for BOE 30521
Cafeteria outsourcing analysis for BOE 30521Cafeteria outsourcing analysis for BOE 30521
Cafeteria outsourcing analysis for BOE 30521
 

Similar to [C] giao trinh c dhbk - viet nhat

Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#Hihi Hung
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#LanLT2011
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#LanLT2011
 
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]leduyk11
 
Windows Programming Tác giả: Bộ môn Công nghệ phần mềm; Người hướng dẫn: -; ...
Windows Programming Tác giả: Bộ môn Công nghệ phần mềm;  Người hướng dẫn: -; ...Windows Programming Tác giả: Bộ môn Công nghệ phần mềm;  Người hướng dẫn: -; ...
Windows Programming Tác giả: Bộ môn Công nghệ phần mềm; Người hướng dẫn: -; ...VTrung46
 
Nhập môn lập trình - Vương Bá Thịnh
Nhập môn lập trình - Vương Bá ThịnhNhập môn lập trình - Vương Bá Thịnh
Nhập môn lập trình - Vương Bá Thịnhsilverclaw
 
Lap trinh c_tu_co_ban_den_nang_cao
Lap trinh c_tu_co_ban_den_nang_caoLap trinh c_tu_co_ban_den_nang_cao
Lap trinh c_tu_co_ban_den_nang_caoHuy Nguyễn
 
DoThanhNghi2016_Python.pdf
DoThanhNghi2016_Python.pdfDoThanhNghi2016_Python.pdf
DoThanhNghi2016_Python.pdfTamDo58
 
Nhập môn lập trình - Vương Bá Thịnh
Nhập môn lập trình - Vương Bá ThịnhNhập môn lập trình - Vương Bá Thịnh
Nhập môn lập trình - Vương Bá Thịnhsilverclaw
 
Python moi
Python moiPython moi
Python moiDÉp LÊ
 
Nmlt c02 gioi_thieunnltc
Nmlt c02 gioi_thieunnltcNmlt c02 gioi_thieunnltc
Nmlt c02 gioi_thieunnltcMinh Ngoc Tran
 
1. giới thiệu về ng￴n ngữ lập trình c#
1. giới thiệu về ng￴n ngữ lập trình c#1. giới thiệu về ng￴n ngữ lập trình c#
1. giới thiệu về ng￴n ngữ lập trình c#Duy Lê Văn
 
Giáo trình vb.net
Giáo trình vb.netGiáo trình vb.net
Giáo trình vb.netHung Pham
 
Phan 2 chuong 1-2
Phan 2   chuong 1-2Phan 2   chuong 1-2
Phan 2 chuong 1-2ngoclanh12
 

Similar to [C] giao trinh c dhbk - viet nhat (20)

Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#
 
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
 
005. LAP TRINH C#.pdf
005. LAP TRINH C#.pdf005. LAP TRINH C#.pdf
005. LAP TRINH C#.pdf
 
Windows Programming Tác giả: Bộ môn Công nghệ phần mềm; Người hướng dẫn: -; ...
Windows Programming Tác giả: Bộ môn Công nghệ phần mềm;  Người hướng dẫn: -; ...Windows Programming Tác giả: Bộ môn Công nghệ phần mềm;  Người hướng dẫn: -; ...
Windows Programming Tác giả: Bộ môn Công nghệ phần mềm; Người hướng dẫn: -; ...
 
Nhập môn lập trình - Vương Bá Thịnh
Nhập môn lập trình - Vương Bá ThịnhNhập môn lập trình - Vương Bá Thịnh
Nhập môn lập trình - Vương Bá Thịnh
 
Lap trinh c_tu_co_ban_den_nang_cao
Lap trinh c_tu_co_ban_den_nang_caoLap trinh c_tu_co_ban_den_nang_cao
Lap trinh c_tu_co_ban_den_nang_cao
 
DoThanhNghi2016_Python.pdf
DoThanhNghi2016_Python.pdfDoThanhNghi2016_Python.pdf
DoThanhNghi2016_Python.pdf
 
Chuong 1@ngon ngu c
Chuong 1@ngon ngu cChuong 1@ngon ngu c
Chuong 1@ngon ngu c
 
Chuong 01 mo dau
Chuong 01 mo dauChuong 01 mo dau
Chuong 01 mo dau
 
Ltc 01
Ltc 01Ltc 01
Ltc 01
 
Nhập môn lập trình - Vương Bá Thịnh
Nhập môn lập trình - Vương Bá ThịnhNhập môn lập trình - Vương Bá Thịnh
Nhập môn lập trình - Vương Bá Thịnh
 
Python moi
Python moiPython moi
Python moi
 
Nmlt c02 gioi_thieunnltc
Nmlt c02 gioi_thieunnltcNmlt c02 gioi_thieunnltc
Nmlt c02 gioi_thieunnltc
 
1. giới thiệu về ng￴n ngữ lập trình c#
1. giới thiệu về ng￴n ngữ lập trình c#1. giới thiệu về ng￴n ngữ lập trình c#
1. giới thiệu về ng￴n ngữ lập trình c#
 
Linux+03
Linux+03Linux+03
Linux+03
 
Giáo trình vb.net
Giáo trình vb.netGiáo trình vb.net
Giáo trình vb.net
 
Phan 2 chuong 1-2
Phan 2   chuong 1-2Phan 2   chuong 1-2
Phan 2 chuong 1-2
 

[C] giao trinh c dhbk - viet nhat

  • 1. 1 Ngôn ngữ lập trình C Chương 1: Giới thiệu chung Cao Tuấn Dũng 2007 Lập trình C - Việt Nhật 2007 2 Tài liệu tham khảo The C Programming Language (2nd Edition) Kernighan & Ritchie Prentice Hall 1988. Ngôn Ngữ Lập Trình C. Quách Tuấn Ngọc. Nhà Xuất Bản Giáo Dục, 1998. Efficient C programming. Mark Allen Weiss. Prentice Hall, 1998. Kỹ thuật lập trình C, cơ sở và nâng cao. Phạm Văn Ất. Nhà Xuất Bản Khoa Học & Kỹ Thuật. Lập trình C - Việt Nhật 2007 3 Một số khái niệm Computer program –chương trình máy tính là một tập các câu lệnh (instruction) hướng dẫn máy tính làm một số việc nhất định. Programming language - Ngôn ngữ lập trình là ngôn ngữ để viết chương trình. Có nhiều loại ngôn ngữ lập trình. Compiler – trình biên dịch, là phần mềm chịu trách nhiệm dịch chương trình viết bằng một ngôn ngữ lập trình sang dạng mã máy. Lập trình C - Việt Nhật 2007 4 MACHINE CODE ASSEMBLER LANGUAGES HIGH-LEVEL LANGUAGES ForTran, COBOL, C, C++, LISP, Pascal, Java, ... 4GLs ORACLE, SEQUEL, INGRES, ... 5GLs artificial intelligence Các lớp Ngôn ngữ lập trình
  • 2. 2 Lập trình C - Việt Nhật 2007 5 Thuật toán - Algorithm Tập các lệnh được tổ chức có thứ tự nhằm giải quyết một bài toán hoặc đạt đến một mục tiêu nào đó. Ví dụ: hướng dẫn chế biến một món ăn, hướng dẫn sửa chữa xe máy, cách giải một bài toán. … Algorithm –Thuật toán - Thuật giải Lập trình C - Việt Nhật 2007 6 Thuật giải tốt Một thuật giải tốt là thuật giải: chính xác rõ ràng đúng hiệu quả và có thể bảo trì được. 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 7 Tính điểm trung bình môn học Nhập: điểm thực hành Vật Lý, điểm bài tập, điểm bài kiểm tra giữa học kỳ, điểm bài kiểm tra cuối học kỳ. Điểm hệ số Thực hành : 8 2 bài tập: 9 2 KT giữa kỳ: 8 4 KT cuối kỳ: 8 6 Tổng cộng: TONG = 8*2 + 9*2 + 8*4 + 8*6 Điểm trung bình: TB = TONG/(2+2+4+6) Lập trình C - Việt Nhật 2007 8 Sơ đồ xử lý Sử dụng sơ đồ xử lý để minh họa quá trình xử lý một chương trình. start,stop condition expression process data flow Bài tập: dùng sơ đồ để biểu diễn bài toán nhập và tính điểm trung bình.
  • 3. 3 Lập trình C - Việt Nhật 2007 9 Ngôn ngữ C - Lịch sử phát triển C được ra đời tại Bell Lab vào 1972, cha đẻ là Dennis Ritchie. C và mối liên hệ với hệ điều hành Unix Kernighan và Ritchie đưa ra phiên bản C chuẩn đầu tiên năm 1978. ANSI C - C được chuẩn hóa vào năm 1989 bởi American National Standards Institute. Lập trình C - Việt Nhật 2007 10 Ngôn ngữ C – Đặc điểm chính 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. Linh hoạt và mạnh Bản thân ngôn ngữ không đặt ra giới hạn đối với người lập trình 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 Tính khả chuyển cao 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 11 Ngôn ngữ lập trình C có thể đọc và viết mã chương trình trên hầu hết các hệ thống. chuyển lên C++ và có thể viết các kịch bản CGI (CGI script) cho các Website. C là ngôn ngữ biên dịch (complied language). Viết chương trình bằng ngôn ngữ C bằng các chương trình soạn thảo (emacs, vi, các công cụ viết chương trình) Không dùng các chương trình soạn thảo văn bản (vd:Word, WordPad) Hello C Compiler Lập trình C - Việt Nhật 2007 12 Cấu trúc một chương trình C Khai báo tệp tiêu đề #include <stdio.h> #include <math.h> Khai báo các cấu trúc, biến, hàm có sử dụng đến Hàm chính main() - Điểm bắt đầu của chương trình - Chỉ có duy nhất - Từ hàm main thường gọi đến các chương trình con (hàm) khác. 1 /* Fig. 2.1: fig02_01.c 2 A first program in C */ 3 #include <stdio.h> 4 5 int main() 6 { 7 printf( "Welcome to C!n" ); 8 9 return 0; 10 } - Các chú thích nằm trong /*….*/ - Câu lệnh kết thúc bằng dấu ; - { } mở và đóng một khối lệnh
  • 4. 4 Lập trình C - Việt Nhật 2007 13 Giới thiệu chung về Linux Linux là phiên bản phát triển có mã nguồn mở của hệ điều hành Unix 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 của hệ điều hành (kernel), cung cấp các dịch vụ truy nhập quản lý tài nguyên phần cứng cho người sử dụng và các chương trình ứng dụng Các bản phân phối Linux: RedHat, SuSE, Debian, Mandrake. Linux kế thừa các ưu điểm của dòng hệ điều hành Unix: môi trường đa người dùng, đa nhiệm, chạy trên nhiều platform khác nhau. 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 14 Hệ thống file trên Linux Trong Linux, các tập tin được 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 phân biệt chữ hoa, chữ thường đối với tên file Mỗi người sử dụng khi được tạo tài khoản sẽ có một thư mục, gọi là thư mục home. Ký hiệu ~. Thông thường người dùng userid sẽ có thư mục home là /home/userid Lập trình C - Việt Nhật 2007 15 Một số lệnh cần thiết Đổi mật khẩu tài khoản: passwd Hiện đường dẫn tới thư mục hiện hành: pwd Liệt kê danh sách các file trong thư mục: ls tên_thư_mục ls –l examples xem nội dung thư mục con examples của thư mục hiện hành theo từng trang. Tạo thư mục : mkdir tên_thư_mục Chuyển tới thư mục mới : cd đường_dẫn Xem nội dung file: cat tên_file Xóa một thư mục hay file: rm Sao chép file: cp nguồn đích Xem hướng dẫn sử dụng một lệnh: man tên_lệnh. Ví dụ man mv lệnh --help Tìm thư mục chứa lệnh: which tên_lệnh which gcc Thoát khỏi hệ thống: logout Lập trình C - Việt Nhật 2007 16 Trình soạn thảo emacs Linux tồn tại sẵn một số lệnh soạn thảo tập tin văn bản như lệnh vi. emacs (editor macros): trình soạn thảo văn bản đa chức năng. Hai phiên bản phổ biến: GNU emacs và xemacs Dùng ngôn ngữ emacs lisp với khả năng mở rộng: soạn thảo, căn lề, nhận biết tag, gọi chương trình dịch, thậm chí duyệt Web. emacs cho phép làm việc trên nhiều cửa sổ và bộ đệm. Nội dung soạn thảo không ghi trực tiếp vào file mà ghi lên bộ đệm. emacs làm việc theo lệnh. Người sử dụng gõ lệnh trên cửa sổ lệnh để tác động lên nội dung trong bộ đệm
  • 5. 5 Lập trình C - Việt Nhật 2007 17 Các lệnh cơ bản trong emacs Quy ước: 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 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. Trở về trạng thái chờ lệnh: C-g Thao tác với file: 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. Lưu nội dung bộ đệm vào file: C-x C-s Lưu với tên khác: C-x C-w tên Di chuyển con trỏ Sử dụng các phím mũi tên 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 Về đầu dòng C-a, về cuối dòng C-e Lập trình C - Việt Nhật 2007 18 Các lệnh trong emacs (tiếp) Di chuyển con trỏ theo từ: Về sau: M-f. Về trước M-b Theo câu: Chú ý emacs phân biệt câu với dòng. Về đầu : M-a, về cuối câu: M-e. Theo bộ đệm: Về đầu: M-v. Về cuối: C-v Thao tác soạn thảo, cắt dán Xóa ký tự: Del hoặc C-d Xóa từ con trỏ đến hết từ: M-d. Xóa từ con trỏ đến hết dòng: C-k Xóa một dòng: C-a C-k Chọn tất cả: C-x h. Undo : C-x u M-w: copy vùng được đánh dấu. 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 19 Các lệnh trong Emacs Tìm kiếm trong văn bản: Tìm xuôi chiều C-s từ_cần_tìm Tìm ngược chiều C-r từ_cần_tìm Soạn thảo trên nhiều cửa sổ Cho phép theo dõi nội dung ở những phần khác nhau của file C-x-2 Chia hai cửa sổ theo chiều ngang C-x-3 Chia hai cửa sổ theo chiều dọc C-x-0 di chuyển giữa hai cửa sổ C-x-1 Xóa tất cả các cửa sổ và về lại cửa sổ đầu tiên Thoát khỏi Emacs C-x-c Lập trình C - Việt Nhật 2007 20 Biên dịch chương trình C trên Linux Hầu hết các bản phân phối của Linux đều tích hợp với trình biên dịch ngôn ngữ C: gcc. 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, sau đó liên kết mã đối tượng để tạo ra tập tin thực thi Nếu không chỉ định tên tập tin thực thi, mặc định kết quả sẽ là a.out Các tham số biên dịch -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 -o: chỉ định tên file thực thi mong muốn -g: thêm thông tin gỡ rối (debug) -l: chỉ định thư viện liên kết. gcc –Wall hello.c –o runhello ./runhello
  • 6. 6 Lập trình C - Việt Nhật 2007 21 Biên dịch chương trình C trên Linux Các tập tin makefile cho phép: Tạo các script biên dịch, tiết kiệm thời gian gõ lệnh Tiết kiệm thời gian biên dịch vì chỉ dịch lại những tập tin có thay đổi Rất phức tạp và dễ gây lỗi. Có thể sử dụng lại EXEC_NAME = helloWorld all: main.o 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 22 Một số lỗi thường gặp khi biên dịch Segfaults Khi có lỗi truy nhập bộ nhớ. Ví dụ: cố gắng truy nhập thông tin từ một con trỏ NULL. Linker errors Lỗi trong dòng lệnh gcc hay makefile Multiple definitions of functions Định nghĩa sai trong cái chỉ thị tiền xử lý trong tập tin header như: #ifndef, #define Ngôn ngữ lập trình C 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 Cao Tuấn Dũng 2007 Lập trình C - Việt Nhật 2007 24 Chương trình C đầu tiên 1. #include <stdio.h> 2. 3. int main() 4. { 5. printf(“Hellon"); 6. return 0; 7. }
  • 7. 7 Lập trình C - Việt Nhật 2007 25 Chương trình C #include <stdio.h> khai báo sử dụng thư viện xuất/nhập chuẩn (standard I/O library). Các thư viện khác: string, time, math… int main() khai báo hàm main(). Chương trình C phải khai báo (duy nhất) 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(). { … } mở và đóng một khối mã. printf 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) 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. Lập trình C - Việt Nhật 2007 26 Mở rộng 1 1. #include <stdio.h> 2. 3. int main() 4. { 5. int a, b, c; 6. a = 5; 7. b = 7; 8. c = a + b; 9. printf(“%d + %d = %dn“, a, b, c); 10. return 0; 11. } Lập trình C - Việt Nhật 2007 27 Tên (Định danh) Tên dùng để xác định các đối tượng khác nhau trong chương trình Tên biến, tên hàm, tên hằng, tên nhãn. Quy tắc đặt tên: Bao gồm các chữ cái, chữ số, dấu gạch dưới Không được bắt đầu bằng chữ số Phân biệt chữ hoa chữ thường. Không trùng với các từ khóa. Tên đúng: sothu_3, tigiack Tên sai:x^2, y-2, 23ant Lập trình C - Việt Nhật 2007 28 Biến (variable) Biến tương ứng với một vùng trống nào đó trong bộ nhớ máy tính dùng để giữ các giá trị. Là đại lượng mà giá trị của nó có thể thay đổi trong quá trình tính toán. Một biến luôn có: tên, kiểu, kích thước, giá trị. Việc đọc giá trị một biến không làm thay đổi giá trị của nó Khai báo: <type> <var-name>; ví dụ: int b; float m_1, m_2; Gán giá trị vào biến: <var-name> = <value>; vd: b = 5; Sử dụng biến: printf(“%d + %d = %dn“, a, b, c); Biến nguyên 45
  • 8. 8 Lập trình C - Việt Nhật 2007 29 Mở rộng 2 1. #include <stdio.h> 2. 3. int main() 4. { 5. int a, b, c; 6. printf(“Nhap so thu nhat: “); 7. scanf(“%d”, &a); 8. printf(“Nhap so thu hai: “); 9. scanf(“%d”, &b); 10. c = a + b; 11. printf(“%d + %d = %dn“, a, b, c); 12. return 0; 13. } Nhap so thu nhat: Nhap so thu hai: 5 + 7 = 12 5 7 5 7 12 a b c /ngonnguC/bin/tong /ngonnguC/bin/ Lập trình C - Việt Nhật 2007 30 Chú ý C phân biệt chữ hoa/chữ thường do đó phải viết đúng tên lệnh. vd: printf chứ không phải là Printf, pRintf, PRINTF. Trong câu lệnh scanf() để lấy giá trị vào biến, phải luôn dùng dấu & trước tên biến. Khi gọi các hàm phải khai báo các tham số đúng vị trí và đầy đủ. Phải khai báo biến trước khi sử dụng trong chương trình. Lập trình C - Việt Nhật 2007 31 Các kiểu dữ liệu cơ bản Character: char (ký tự 1-byte) Integer: int (các giá trị nguyên 2-byte) -32768 đến 32767 Số nguyên không dấu: unsigned int 0 đến 65535 Số phẩy động độ chính xác đơn: float (các giá trị dấu chấm động 4-byte) (+,-)3.4E-38 , (+,-)3.4E+38 Số phẩy động độ chính xác kép: double (dấu chấm động 8- byte) (+,-)3.4E-308 , (+,-)3.4E+308 Lập trình C - Việt Nhật 2007 32 Biến và hằng số Biến số (variable) được dùng để giữ các giá trị và có thể thay đổi các giá trị mà biến đang giữ Khai báo: <typename> varname; Vd: int i; float x, y, z; char c; Gán giá trị cho biến: <varname> = <value>; vd: i = 4; x = 5.4; y = z = 1.2;
  • 9. 9 Lập trình C - Việt Nhật 2007 33 Hằng số Hằng số (constant) giá trị không thay đổi trong quá trình sử dụng. Khai báo hằng: #define <constantname> <value> vd: #define TRUE 1 #define FALSE 0 Giá trị hằng 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’ ‘9’ – ‘0’ = 57 – 48 = 9 Lập trình C - Việt Nhật 2007 34 Kiểu và chuyển kiểu (typecasting) C cho phép chuyển đổi kiểu dữ liệu cơ bản trong khi đang tính toán (chuyển kiểu tường minh). ví dụ: void main() { float a; int b; b = 10/3; a = (float)10/3; printf(“a = %f n b = %dn”, a, b); } Chuyển kiểu tự động: khi biểu thức gồm hai toán hạng khác 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 35 Định nghĩa kiểu (typedef) Có thể định nghĩa các kiểu riêng bằng lệnh typedef. vd: #define TRUE 1 #define FALSE 0 typedef int boolean; void main() { boolean b; b = FALSE; /*...*/ } Lập trình C - Việt Nhật 2007 36 Các Toán tử right to left= *= /= %= += -= &= |= ^= <<= >>= Assignment14 13 12 11 10 9 8 7 6 5 4 3 2 2 1 0 Priority Conditional operator Logical OR Logical AND Boolean OR Boolean XOR Boolean AND Equality Relational Shift Additive Multiplicative Type cast Prefix and unary Postfix Primary expression Category Right to left? left to right|| left to right&& left to right| left to right^ left to right& left to right== != left to right< <= > >= left to right<< >> left to right+ - left to right* / % right to left( typeName ) right to left! ~ + - ++ -- & sizeof left to rightFunction() () [] -> Noneidentifiers constants AssociativityExample
  • 10. 10 Lập trình C - Việt Nhật 2007 37 Các toán tử so sánh và toán tử logic 15 7 5 7 0 X | Y 1 1 1 0 0 !Y 0 0 0 1 1 !X 1 1 1 1 0 X || Y 7 7 0 7 0 Y 0 0 0 X>>Y 32 48 24 X<<Y 0 0 1 X == Y 1 1 0 X != Y 1 0 1 X >= Y 1 0 0 X > Y 0 1 1 X <= Y 3 4 3 Y 018 515 005 000 000 X & YX && YX Possible MistakesLogical Operators 304 413 303 X=YX < YX Possible MistakesRelational and Quality Operators Lập trình C - Việt Nhật 2007 38 Số nhị phân Hệ đếm thập phân: 5426=5*103 + 4*102 + 2*101 + 6*100 254,68 = 2*102 + 5*101 + 4*100 + 6*10-1 + 8*10-2 Hệ đếm nhị phân (cơ số hai): Hệ chỉ có hai chữ số 0 và 1. 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 Một chữ số nhị phân – BIT – là đơn vị lưu trữ thông tin nhỏ nhất của máy tính. Lập trình C - Việt Nhật 2007 39 Các phép toán số học + - / * %: phép chia lấy phần dư trong số nguyên. (modulo). i = i + 1; i++; ++i; i = i – 1; i--; --i; i = i + 3; i += 3; i = i * j; i *= j; Phép chia kiểu nguyên: 7/5 cho kết quả 1. Phép lấy phần dư: 7 % 5 cho kết quả 2. Thứ tự ưu tiên: Toán tử Tên gọi Thứ tự tính toán (precedence) () Ngoặc tròn Được ưu tiên nhất. Tính giá trị đầu tiên trong một biểu thức *, /, or % Nhân, chia, lấy phần dư Độ ưu tiên thứ hai, nếu cùng tồn tại thì được thực hiện từ trái qua phải + or - Cộng, trừ Độ ưu tiên thấp nhất Lập trình C - Việt Nhật 2007 40 Toán tử tăng giảm Có thể viết i=i+1; (i=i-1;) dưới dạng i++; ( i--; ) hay ++i ( --i; ) Sự khác nhau giữa tiền tố và hậu tố i++, giá trị của i tham gia vào việc lượng giá biểu thức chứa nó trước khi tăng. ++I, giá trị của i tăng, sau đó giá trị mới có hiệu lực khi lượng giá biểu thức chứa nó. Ví dụ: Với i= 4 j = ++i +10; cho j bằng 15 và j= i++ +10; cho j=14. int i= 6; printf ("%dn",i++); /* Prints 6 sets i to 7 */ int i= 6; printf ("%dn",++i); /* prints 7 and sets i to 7 */
  • 11. 11 Lập trình C - Việt Nhật 2007 41 Các phép gán tắt Ngôn ngữ lập trình C Chương 3: Cách thức vào ra Cao Tuấn Dũng 2007 Lập trình C - Việt Nhật 2007 43 Dòng vào, ra chuẩn Việc nhập dữ liệu được thực hiện thông qua dòng vào chuẩn stdin (bàn phím) Khi có dữ liệu trên dòng vào chuẩn, các hàm nhận dữ liệu như scanf, getchar sẽ lấy phần dữ liệu mà nó yêu cầu Khi dòng vào không đủ dữ liệu: máy chờ người dùng Dòng ra chuẩn: màn hình Các hàm xuất dữ liệu Printf, putchar Thư viện vào ra chuẩn: stdio.h a 12 1.4 Lập trình C - Việt Nhật 2007 44 Hàm printf int printf (char* địnhdạng, [danh sách đối,….] Vd: printf(“Gia tri ham tai toa do %d,%d la %f”, 2,3, 1.09); Dạng tổng quát của đặc tả chuyển dạng %[-] [width][.prec] ký tự chuyển dạng width: dãy số nguyên xác định độ rộng tối thiểu của giá trị hiển thị .prec: dùng cho kiểu float, double Đối số nguyên không dấuunsigned intu Đối số là xâu ký tựchar *s Đối số là số dấu phẩy tĩnhfloat, doublef Đối số là số nguyên lớnlongld Đối số là số nguyên có dấuintd Đối số là một ký tựcharc Ý nghĩaKiểu giá trị của đối sốKý tự chuyển dạng
  • 12. 12 Lập trình C - Việt Nhật 2007 45 Hàm printf 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 quả dồn về bên trái 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; 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 "-0124"printf("%05d", i); " -124"printf("%5d", i); "-124"printf("%-05d", i); "-124 " Kết quả printf("%-5d", i); Lệnh Lập trình C - Việt Nhật 2007 46 Ký tự Escape Đánh dấu các ký tự đặc biệt: ’, ”, n, t, r, , 0 Dùng trong hàm printf để định dạng dòng ra Lập trình C - Việt Nhật 2007 47 Hàm scanf Có một số đặc điểm tương tự printf nhưng theo chiều ngược lại: đọc thông tin từ dòng vào. Cú pháp: int scanf(char *định_dạng, [danhsachdiachi]); Trả về giá trị EOF nếu có lỗi. Xâu định dạng có cú pháp: %[*][width]kýtựchuyểndạng int a; float x, y; char ch[6], ct[6]; scanf("%f%5f%3d%3s%s", &x, &y,&a, ch, ct); 54.32e-1 25 12357287a x = 5.432 y=25.0 a=123 ch="572" ct="87a Lập trình C - Việt Nhật 2007 48 Một số hàm nhập xuất khác getchar: Nhập một ký tự từ dòng vào: int getchar(); int c; c = getchar(); putchar putchar(int c); fflush(stdin): Làm sạch dòng vào. Nhập xuất xâu: char *gets(char *s) nhận dữ liệu cho đến khi gặp 'n' char ten[30]; int t; printf( "nTuoi:"); scanf("%d",&t); fflush(stdin); /* xoá mọi ký tự ở dòng vào */ printf( "nTen:"); scanf("%s",ten);
  • 13. 13 Ngôn ngữ lập trình C Các cấu trúc điều khiển Cao Tuấn Dũng 2007 Lập trình C - Việt Nhật 2007 50 Câu lệnh điều kiện if if (<dieu kien>) { /* cac lenh thuc hien neu dieu kien dung */ } … expression statement(s) Next statement True False Một quyết định có thể được đưa ra dựa trên bất cứ một biểu thức nào. 0 - false khác 0 - true Ví dụ: 3 - 4 - true Lập trình C - Việt Nhật 2007 51 Ví dụ 1. #include <stdio.h> 2. int main() { 3. int b; 4. printf("Enter a value:"); 5. scanf("%d", &b); 6. if (b < 0) 7. printf("The value is negativen"); 8. return 0; 9. } Lập trình C - Việt Nhật 2007 52 Ví dụ 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 ) printf( "Vuot qua ky thin" ); true false diemthi >= 5 print “Qua”
  • 14. 14 Lập trình C - Việt Nhật 2007 53 if … else … if (<dieu kien>) { /* cac lenh thuc hien neu dieu kien dung */ } else { /* cac lenh thuc hien neu dieu kien sai */ } … expression statement1 Next statement True False statement2 Lập trình C - Việt Nhật 2007 54 Ví dụ … printf(“1/X is: “); if(X) printf(“ %f n”, 1/X); else printf(“ undefined n”); … Lập trình C - Việt Nhật 2007 55 Ví dụ 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 nếu không thì sinh viên thi trượt. Mã chương trình C: if ( diemthi >= 5 ) printf( "Vuot qua ky thin" ); else printf( "Truotn" ); truefalse print “Truot” print “Qua” diemthi >= 5 Lập trình C - Việt Nhật 2007 56 Lỗi đơn giản nhưng dễ phạm 1. #include <stdio.h> 2. int main() { 3. int b; 4. printf("Enter a value:"); 5. scanf("%d", &b); 6. if (b == 5) 7. printf(“b is "); printf( “5 n”); 8. return 0; 9. }
  • 15. 15 Lập trình C - Việt Nhật 2007 57 Lỗi đơn giản nhưng dễ phạm 1. printf(“1/X is: “); 2. if(X < 0) ; 3. printf(“ X is negative n”); 4. … Lập trình C - Việt Nhật 2007 58 Lệnh if else if Khi muốn thực hiện 1 trong n quyết định if (biểu thức1) lệnh 1; else if (biểu thức2) lệnh 2; … else if (biểu thức_n_1) lệnh n -1; else lệnh n; Biểu thức 1 Biểu thức 2 Lệnh 1 Lệnh 1 + + - - Lệnh ứng với else cuối cùng Lập trình C - Việt Nhật 2007 59 Lệnh if else if Lập chương trình nhập vào mã trình độ và đưa ra tên trình độ tương ứng #include <stdio.h> int main() { int ma; printf("n Go vao ma trinh do:"); scanf("%d", &ma); if (ma==1) printf("n Trinh do trung cap"); else if (ma==2) printf("n Trinh do cao dang"); else if (ma==3) printf("n Trinh do dai hoc"); else printf("n Nhap sai ma"); return 0; } Lập trình C - Việt Nhật 2007 60 Ví dụ: Kiểm tra nhiều điều kiện 1. #include <stdio.h> 2. int main() { int b; 3. printf("Enter a value:"); 4. scanf("%d", &b); 5. if (b < 0) 6. printf("The value is negativen"); 7. else if (b == 0) 8. printf("The value is zeron"); 9. else 10. printf("The value is positiven"); 11. return 0; 12. } Bài tập: Viết chương trình giải phương trình bậc nhất: ax + b = 0. Biện luận các điều kiện có nghiệm của phương trình.
  • 16. 16 Lập trình C - Việt Nhật 2007 61 Điều kiện lồng nhau Câu lệnh if có thể được lồng vào nhau. 1. if ( X >= 0 ) { 2. if ( Y < 0 ) 3. Y = Y + sqrt(X); 4. } 5. else 6. Y = Y + sqrt(-X); 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. 1. if ( X >= 0 ) 2. if ( Y < 0 ) 3. Y = Y + sqrt(X); 4. else 5. Y = Y + sqrt(-X); Bài tập: Viết chương trình giải phương trình bậc 2: 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 62 Lặp - lệnh while while (bieu thuc dieu kien) {cac lenh} Khi biểu thức điều kiện (expression) còn khác 0 (TRUE), lệnh (statement) tiếp tục được thực hiện. Nếu expression bằng 0 (FALSE), lệnh while dừng và chương trình sẽ gọi lệnh kế tiếp sau while. Nếu lúc đầu expression bằng 0 thì (statement) trong while không bao giờ được gọi thực hiện. expression statement(s) Next statement True False Lập trình C - Việt Nhật 2007 63 Ví dụ In bảng đổi nhiệt độ từ độ Fahrenheit (oF) sang độ Celcius (oC). 1. #include <stdio.h> 2. int main() { 3. int a = 0; 4. while (a <= 100) { 5. printf("%4d degrees F = %4d degrees Cn",a, (a - 32)*5/9); 6. a = a + 10; 7. } 8. return 0; 9. } Lập trình C - Việt Nhật 2007 64 Ví dụ: Biến đếm điều khiển Biến đếm điều khiển vòng lặp Lặp cho đến khi bộ đếm đạt tới một giá trị nhất định Vòng lặp xác định: số vòng lặp được biết trước. Ví dụ: Một lớp học 120 sinh viên phải làm bài tập. Điểm số (số nguyên trong khoảng 0 đến 10). Tính điểm bài tập trung bình cho cả lớp. Mã giả: Gán tong bang 0 Gán biến đếm sinh viên bằng 1 While biến đếm nhỏ hơn hay bằng 120 Nhập điểm số Cộng điểm số vào tổng Tăng 1 vào biến đếm Điểm trung bình = tổng / 120 Hiển thị ra màn hình
  • 17. 17 Lập trình C - Việt Nhật 2007 65 1. Khởi tạo biến 2. Thực hiện vòng lặp 3. Xuất kết quả 1 /* 2 Tinh diem trung binh kiem tra 3 Dieu khien vong lap bang bien dem */ 4 #include <stdio.h> 5 6 int main() 7 { 8 int counter, grade, total, average; 9 10 /* Khoi tao gia tri bien */ 11 total = 0; 12 counter = 1; 13 14 /* Xu ly */ 15 while ( counter <= 120 ) { 16 printf( "Nhap diem: " ); 17 scanf( "%d", &grade ); 18 total = total + grade; 19 counter = counter + 1; 20 } 21 22 /* Kêt thuc */ 23 average = total / 120; 24 printf( "Diem trung binh cua ca lop la %dn", average ); 25 26 return 0; /* Chuong trinh ket thuc thanh cong */ 27 } Lập trình C - Việt Nhật 2007 66 Thiết kế giải thuật Top-down Mở rộng bài toán: Viết chương trình tính điểm trung bình mà số sinh viên không được biết trước. Làm cách nào để kết thúc chương trình? Sử dụng giá trị có vai trò "lính canh" Còn có tên gọi khác là cờ Chỉ ra dấu hiệu “kết thúc nhập dữ liệu.” Vòng lặp kết thúc khi người sử dụng nhập vào giá trị lính canh. Giá trị này được chọn sao cho không thể nhầm lẫn nó với các giá trị nhập vào thông thường khác. Lập trình C - Việt Nhật 2007 67 Thiết kế giải thuật Top-down Tiếp cận top-down, giải quyết từng bước vấn đề Bắt đầu với vấn đề cần giải quyết từ đỉnh (top): Xác định điểm trung bình kiểm tra của cả lớp Phân chia bài toán thành các công việc nhỏ hơn và liệt kê chúng theo trình : Khởi tạo biến Nhập dữ liệu, tính tổng và đếm số sinh viên Tính điểm trung bình Nhiều chương trình trải qua ba giai đoạn: Khởi tạo: khởi tạo giá trị các biến Xử lý: nhập giá trị dữ liệu và thao tác trên chúng Kết thúc: Tính toán và hiển thị kết quả cuối cùng Lập trình C - Việt Nhật 2007 68 1. Khởi tạo biến 2. Nhập liệu 2.1 Thi hành vòng lặp 1 /* 2 Chương trình tính điểm trung bình 3 sự dụng biến canh để điều khiển vòng lặp */ 4 #include <stdio.h> 5 6 int main() 7 { 8 float average; 9 int counter, grade, total; 10 11 /* khởi tạo */ 12 total = 0; 13 counter = 0; 14 15 /* xử lý */ 16 printf( "Nhập điểm, -1 để thoát ra: " ); 17 scanf( "%d", &grade ); 18 19 while ( grade != -1 ) { 20 total = total + grade; 21 counter = counter + 1; 22 printf( "Enter grade, -1 to end: " ); 23 scanf( "%d", &grade ); 24 }
  • 18. 18 Lập trình C - Việt Nhật 2007 69 Các cấu trúc điều khiển lồng nhau Bài toán Một học viện có một danh sách kết quả kiểm tra của 120 sinh viên (1 = qua, 2 = trượt) Viết chương trình phân tích kết quả Nếu có hơn 90 sinh viên thi đỗ, hiển thị "Chất lượng đạt" Chú ý Chương trình phải xử lý 120 kết quả Biến đếmđiều khiển vòng lặp có thể được sử dụng Có thể dùng hai biến đếm Một cho số lượng thi đỗ, một cho số thi trượt Kiểm tra kết quả thi—hoặc 1 hoặc 2 Nếu kết quả không phải là 1, coi như là 2 Lập trình C - Việt Nhật 2007 70 1 /* Fig. 3.10: fig03_10.c 2 Phân tích kết quả thi */ 3 #include <stdio.h> 4 5 int main() 6 { 7 /* Khai báo khởi tạo biến */ 8 int passes = 0, failures = 0, student = 1, result; 9 10 /* Làm việc với 120 sinh viên; vòng lặp điều khiển bởi biến đếm */ 11 while ( student <= 120 ) { 12 printf( "Enter result ( 1=pass,2=fail ): " ); 13 scanf( "%d", &result ); 14 15 if ( result == 1 ) /* if/else lồng trong while */ 16 passes = passes + 1; 17 else 18 failures = failures + 1; 19 20 student = student + 1; 21 } 22 23 printf( "Passed %dn", passes ); 24 printf( "Failed %dn", failures ); 25 26 if ( passes > 90 ) 27 printf( "Chất lượng đạtn" ); 28 29 return 0; /* kết thúc thành công */ 30 } Lập trình C - Việt Nhật 2007 71 Lặp - lệnh for for (biểu thức khởi tạo; biểu thức kiểm tra; biểu thức tăng giảm) {Các lệnh} Khởi động. Sau đó, nếu điều kiện (test) khác 0: lệnh (statement) được thi hành, lệnh điều chỉnh lại “biến đếm” được gọi thi hành. test statement(s) Next statement True False adjustment initialization Lập trình C - Việt Nhật 2007 72 Ví dụ In ra các số từ 1 đến 10 Biểu thức khởi tạo được thực hiện một lần, còn các biểu thức còn lại được tính nhiều lần. Vòng lặp for có thể lồng nhau nhiều lần Câu lệnh break làm máy thoát khỏi vòng lặp for sâu nhất chứa lệnh đó. In ra các chữ cái hoa for (i = 1; i <= 10; i++) printf("%d ", i); char ch; for (ch = 'A'; ch <= 'Z'; ch++) printf("%c ", ch);
  • 19. 19 Lập trình C - Việt Nhật 2007 73 Lệnh for (tiếp theo) Giữa hai dấu ; có thể có nhiều hơn một biểu thức Phân cách nhau bởi dấu phảy. Example: for (i = 0, j = 0; j + i <= 10; j++, i++) printf( "%dn", j + i ); Lập trình C - Việt Nhật 2007 74 Ví dụ 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. 1. #include <stdio.h> 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 Cn", 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 75 Ví dụ Sum is 2550 1 /* Tính tổng các số chẵn 2 từ 1 đến 100 */ 3 #include <stdio.h> 4 5 int main() 6 { 7 int sum = 0, number; 8 9 for ( number = 2; number <= 100; number += 2 ) 10 sum += number; 11 12 printf( "Sum is %dn", sum ); 13 14 return 0; 15 } Lập trình C - Việt Nhật 2007 76 Lặp - lệnh do while do {statement(s)} while (expression) ; Thực hiện lệnh (statement). Kiểm tra biểu thức điều kiện (expression). Nếu (expression) bằng 0, dừng. Nếu không, thực hiện (statement). Lệnh do while thực hiện (statement) ít nhất một lần. expression statement(s) Next statementTrue False
  • 20. 20 Lập trình C - Việt Nhật 2007 77 Ví dụ - giao diện chương trình 1. #include <stdio.h> 2. #define PTB1 1 3. #define PTB2 2 4. #define STOP 3 5. int main() 6. { 7. int i; 8. do { 9. printf(“ Chuong trinh giai phuong trinh bac thap n”); 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”); Lập trình C - Việt Nhật 2007 78 14. printf(“ 3. Thoat chuong trinh nn”); 15. printf(“ Chon muc so (1/2/3) ? “); 16. scanf(“%d”, &i); 17. if(i == PTB1) 18. printf(“Giai phuong trinh bac 1: hien chua con”); 19. else if(i == PTB2) 20. printf(“Giai phuong trinh bac 2: chua cai datnn”); 21. } while (i != STOP); 22. return 0; 23. } 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 79 break dùng để thoát khỏi vòng lặp giữa chừng. cú pháp: break; Thường sử dụng cùng với lệnh if để kiểm tra điều kiện dừng trước khi dùng lệnh break. Bài tập: Viết chương trình nhập vào một số rồi tìm số nguyên tố đầu tiên lớn hơn số vừa nhập for… { for… { for… { …. break; … } … } } Lập trình C - Việt Nhật 2007 80 Tìm số nguyên tố lớn Cho trước một số tự nhiên N, tìm số nguyên tố lớn hơn gần nhất. Giải pháp: Số nguyên tố là số nguyên tố không chia hết cho các số nhỏ hơn căn bậc hai của nó. Không tính trường hợp 2, 3 thì số nguyên tố phải lẻ. Thuật toán sơ khởi: Kiểm tra N và so sánh với 2, 3 Nếu N chẵn thì tăng lên 1. Bắt đầu thử với một số lẻ. Nếu N là hợp số thì tăng N lên 2 Thử thế nào? Thử tất cả các số nhỏ hơn căn bậc hai của N, nếu N chia hết cho một số trong chúng thì N là hợp số.
  • 21. 21 Lập trình C - Việt Nhật 2007 81 Tìm số nguyên tố lớn 1. #include <stdio.h> 2. #define TRUE 1 3. main(void) 4. { 5. unsigned long int sochia, ucv_nguyento; 6. int la_nguyento; 7. printf(“Nhap vao so khoi dau: “); 8. scanf(“%lu”, & ucv_nguyento); 9. if(ucv_nguyento <= 2) 10. ucv_nguyento = 2; 11. else if(ucv_nguyento !=3 ) 12. { 13. if(ucv_nguyento %2 == 0) 14. ucv_nguyento ++; /* Phai la so le */ Lập trình C - Việt Nhật 2007 82 16. for( ; ; ucv_nguyento += 2) 17. { 18. la_nguyento = !TRUE; 19. for(sochia = 3; ucv_nguyento % sochia; sochia += 2) 20. if(sochia * sochia > ucv_nguyento) 21. { la_nguyento = TRUE; 22. break; 23. } 24. if (la_nguyento) 25. break; 26. } 27. } 28. printf(“So nguyen to lon hon gan nhat la %lun”, ucv_nguyento); 29. } Lập trình C - Việt Nhật 2007 83 continue bỏ qua các lệnh kế tiếp trong một vòng lặp và bắt đầu vòng lặp tiếp theo. cú pháp: continue; chỉ áp dụng với lệnh lặp. Bài tập: Viết chương trình nhập vào một số và tìm ra tất cả các thừa số nguyên tố của số đó. while(i<10) { scanf(“%d”, &grade); if ((grade<0) || (grade>100)) { printf(“Illegal grade – try againn”); continue; } ……. } Lập trình C - Việt Nhật 2007 84 Ví dụ minh họa: continue 1 /* Ví dụ minh họa 2 Sử dụng continue trong vòng lặp for */ 3 #include <stdio.h> 4 5 int main() 6 { 7 int x; 8 9 for ( x = 1; x <= 10; x++ ) { 10 11 if ( x == 5 ) 12 continue; 13 14 15 printf( "%d ", x ); 16 } 17 18 printf( "nDung lenh continue de bo qua gia tri 5n" );19 return 0; 20 } 1 2 3 4 6 7 8 9 10 Dung lenh continue de bo qua gia tri 5
  • 22. 22 Lập trình C - Việt Nhật 2007 85 Tìm thừa số nguyên tố 1. #include <stdio.h> 2. main(void) 3. { 4. unsigned long N, thuasonguyento, phanconlai; 5. printf(“Nhap vao mot so tu nhien: “); 6. scanf(“%lu”, &N); 7. thuasonguyento = 2; phanconlai = N; 8. while(thuasonguyento * thuasonguyento <= phanconlai) 9. { 10. if(phanconlai % thuasonguyento == 0) Lập trình C - Việt Nhật 2007 86 12. { /* Tim ra mot thua so */ 13. printf(“%lu”, thuasonguyento); 14. phanconlai /= thuasonguyento; 15. continue; 16. } 17. /* Khong phai la thua so nguyen to */ 18. if(thuasonguyento == 2) thuasonguyento = 3; 19. else 20. thuasonguyento += 2; 21. } 22. /* thua so nguyen to cuoi cung */ 23. printf(“%lun”, phanconlai); 24. } Lập trình C - Việt Nhật 2007 87 Thực hành lệnh lặp Viết chương trình giải các bài tập sau sử dụng lần lượt các cấu trúc lặp: while, for, do while Nhập vào một số nguyên năm chữ số (giả sử người dùng tuân thủ nghiêm ngặt quy tắc nhập). In các chữ số này trên năm hàng Viết chương trình in ra giai thừa của số tự nhiên N. Nhập mười số nguyên từ bàn phím và đưa ra số lớn nhất. Lập trình C - Việt Nhật 2007 88 Lệnh switch Bài tập: Viết chương trình lấy ngẫu nhiên 1000 số nguyên và đếm số lần xuất hiện ở hàng đơn vị các số chẵn (2, 4, 6, 8), số lẻ (1, 3, 5, 7, 9) và số 0. Nếu chúng ta dùng cấu trúc lệnh if ... else ... if … thì phức tạp và có thể đòi hỏi nhiều phép thử. Lý do: if ... else ... : rẽ nhánh hai chiều. Thử cài đặt bài toán bằng if...else...
  • 23. 23 Lập trình C - Việt Nhật 2007 89 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 case1: case case2: <statements>; break; /* … */ case casen: <statements>; break; default: <statements>; break; } Lập trình C - Việt Nhật 2007 90 Sơ đồ xử lý lệnh switch true false . . . case a case a action(s) break case b case b action(s) break false false case z case z action(s) break true true default action(s) Lập trình C - Việt Nhật 2007 91 Giải bài bằng switch 1. #include <stdlib.h> 2. #include <stdio.h> 3. #include <time.h> 4. int main(void) 5. { int n,i; 6. int n_even = n_odd = n_zero = 0; 7. randomize(); 8. for(i=0; i<1000; i++) 9. { n = random(1000); 10. switch (n%10) { 11. case 2: 12. case 4: 13. case 6: 14. case 8: 15. n_even++; 16. break; Lập trình C - Việt Nhật 2007 92 17. case 1: 18. case 3: 19. case 5: 20. case 7: case 9: 21. n_odd++; 22. break; 23. case 0: 24. n_zero++; 25. break; 26. } 27. } 28. // print out the summary 29. printf(“Number of even_eding number: %dn” Number of odd_ending number: %dn” Number of zero_ending number: %dn”, n_even, n_odd, n_zero); 30. return 0; 31. }
  • 24. 24 Lập trình C - Việt Nhật 2007 93 Thống kê điểm số (American system) Hệ thống điểm của học sinh, sinh viên Mỹ: A, B, C, D, E, F Chương trình nhập điểm của sinh viên, kết thúc bằng EOF In ra số lượng sinh viên đạt điểm số tương ứng Lập trình C - Việt Nhật 2007 94 1. Khởi tạo các biến 2. Nhập liệu 2.1 Dùng switch để cập nhật các biến đếm 1 /* 2 Thống kê điểm số */ 3 #include <stdio.h> 4 5 int main() 6 { 7 int diem; 8 int aCount = 0, bCount = 0, cCount = 0, 9 dCount = 0, fCount = 0; 10 11 printf( "Nhập phân loại học tập theo chữ hoa.n" ); 12 printf( "Nhấn ký tự EOF để kết thúc nhập liệu.n" ); 13 14 while ( ( grade = getchar() ) != EOF ) { 15 16 switch ( grade ) { /* switch lồng trong while */ 17 18 case 'A': /* điểm là hạng A */ 19 ++aCount; 20 break; 21 22 case 'B': /* điểm là hạng B */ 23 ++bCount; 24 break; 25 26 case 'C': /* điểm là hạng C */ 27 ++cCount; 28 break; 29 30 case 'D': /* điểm là hạng D */ 31 ++dCount; 32 break; Lập trình C - Việt Nhật 2007 95 3. In kết quả 33 34 case 'F': /* điểm là hạng F */ 35 ++fCount; 36 break; 37 38 case 'n': case' ': /* ignore these in input */ 39 break; 40 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" ); 44 break; 45 } 46 } 47 48 printf( "Kết quả phân loại sinh viên:n" ); 49 printf( "A: %dn", aCount ); 50 printf( "B: %dn", bCount ); 51 printf( "C: %dn", cCount ); 52 printf( "D: %dn", dCount ); 53 printf( "F: %dn", fCount ); 54 55 return 0; 56 } Lập trình C - Việt Nhật 2007 96 Bài tập 1) Viết chương trình tính giá trị biểu thức "a op b" như "2 + 3" a, b toán hạng – op : toán tử 2) Yêu cầu người sử dụng nhập vào một tháng. In ra số ngày trong tháng đó. Tháng có 31 ngày: 1, 3, 5, 7, 8, 10, 12 Tháng có 30 ngày: 4, 6, 9, 10 Tháng có 28 hoặc 29 ngày : 2
  • 25. 25 Lập trình C - Việt Nhật 2007 97 In số ngày trong một tháng #include <stdio.h> int main () { int thang; printf("n Nhap vao thangs trong nam "); scanf("%d",&thang); switch(thang) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: printf("n Thang %d co 31 ngay ",thang); break; case 4: case 6: case 9: case 11: printf("n Thang %d co 30 ngay ",thang); break; case 2: printf (" Thang 2 co 28 hoac 29 ngay"); break; default : printf("n Khong co thang %d", thang); break; } return 0; } Lập trình C - Việt Nhật 2007 98 Tính ex với độ chính xác 10-5 ex = 1+x/1!+x2/2!+……………..+ xn/n! #include <stdio.h> void main() { float epsilon = 0.00001; float x,add; int i=1; float result = 0.0; add = 1; printf("Chương trình tính giá trị e mũ x. n"); printf("x="); scanf("%f", &x); while (add > epsilon) { result = result + add; add = add*x/i; i = i+1; } printf ("Ket qua la:%f", result); } Lập trình C - Việt Nhật 2007 99 Một số toán tử và lệnh khác Toán tử ‘,’ được dùng để khởi động nhiều biến trong vòng lặp. Ví dụ: for(i = 0, j = 0; i < 5; i++, j += i++) printf(“i = %d, j = %d, i+j = %dn”, i, j, i+j); Kết quả là: ???? Toán tử ba ngôi <TestExpr> ? <YesExpr> : <NoExpr> Ví dụ: Max = (Y > Z) ? Y : Z; Lập trình C - Việt Nhật 2007 100 Một số toán tử và lệnh khác Lệnh goto cho phép nhảy không điều kiện đến bất kỳ nơi nào trong chương trình. Cú pháp: goto <label> Ví dụ: xem chương trình ví dụ. Lệnh goto làm mất cấu trúc chương trình.
  • 26. 26 Lập trình C - Việt Nhật 2007 101 Từ khóa của C auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Lập trình C - Việt Nhật 2007 102 Từ khóa của C #define để khai báo hằng số và … typedef để khai báo kiểu dữ liệu riêng Toán tử sizeof xác định số byte được dùng để chứa một đối tượng ví dụ: typedef unsigned long int Int32; /* ... */ int x; x = sizeof(Int32); // x = 4 Ngôn ngữ lập trình C Chương 4: Mảng Cao Tuấn Dũng 2007 Lập trình C - Việt Nhật 2007 104 Mảng Mảng là tập hợp các giá trị cùng kiểu. 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]; số phần tử trong mảng: array_size; int a[array_size]; n = array_size; Truy cập phần tử mảng qua chỉ số của phần tử: i array[i]; // 0 <= i <= array_size-1, i ∈N0 a[n-1]a[n-1]…a[2]a[1]a[0] a
  • 27. 27 Lập trình C - Việt Nhật 2007 105 Mảng Các phần tử mảng hoạt động như các biến thông thường c[ 0 ] = 3; printf( "%d", c[ 0 ] ); Các thao tác toán học trên chỉ số mảng If x bằng 3 c[ 5 - 2 ] == c[ 3 ] == c[ x ] Ví dụ khai báo mảng: int c[ 10 ]; float myArray[ 3284 ]; Khai báo nhiều mảng cùng kiểu Tương tự khi khai báo nhiều biến cùng kiểu int b[ 100 ], x[ 27 ]; Tên mảng (Các phần tử mảng có cùng chung một tên, c) Vị trí của một phần tử trong mảng c c[6] -45 6 0 72 1543 -89 0 62 -3 1 6453 78 c[0] c[1] c[2] c[3] c[11] c[10] c[9] c[8] c[7] c[5] c[4] Lập trình C - Việt Nhật 2007 106 Chú ý C không kiểm tra giới hạn của chỉ số truy cập phần tử mảng. Truy cập đến phần tử i>=array_size không có cảnh báo, nhưng giá trị không kiểm soát được. Kích thước mảng phải là một hằng số. Kích thước mảng có thể được khai báo tường minh hoặc thông qua một giá trị định nghĩa trước (#define) Lập trình C - Việt Nhật 2007 107 Ví dụ thực tế Bài toán: Sử dụng bảng số liệu về lượng mưa hàng tháng trong năm. Input: tháng Output: Lượng mưa trung bình cho tháng đó tháng lượng mưa TB (in mm) 0 30 1 40 2 45 3 95 4 130 5 220 6 210 7 185 8 135 9 80 10 40 11 45 Bảng lượng mưa Lập trình C - Việt Nhật 2007 108 Khởi tạo mảng Mảng có thể được khởi tạo bởi một dãy các giá trị thích hợp. int n[ 5 ] = { 1, 2, 3, 4, 5 }; Nếu không đủ các giá trị khởi tạo cho tất cả, các phần tử bên phải sẽ có giá trị 0 int n[ 5 ] = { 0 } => Tất cả phần tử = 0. Nếu kích thước mảng bị bỏ qua, chương trình dịch tự phát hiện kích thước cần thiết. int n[ ] = { 1, 2, 3, 4, 5 }; Các khai báo sau đây là hợp lệ int Squares[5] = {0,1,4,9,16}; int Squares[5] = {0,1,4}; int Squares[] = {0,1,4,9,16}; int Squares[];
  • 28. 28 Lập trình C - Việt Nhật 2007 109 Chú ý Không thể thực hiện các thao tác chép nội dung một mảng sang mảng khác. Chép từng phần tử mảng char A[3]={‘a’,’b’,’c’}; char B[3]; B = A; // ??? for(int i=0; i<3; i++) B[i] = A[i]; hoặc chép khối bộ nhớ (sẽ được đề cập sau) Không dùng phép so sánh trực tiếp (==) nội dung trong hai mảng. Phép so sánh (A==B) so sánh địa chỉ hai vùng nhớ mà A và B chỉ đến. Lập trình C - Việt Nhật 2007 110 Bảng lượng mưa (tiếp) #include <stdio.h> int main() { int thang; int bangmua[12] = { 30, 40, 45, 95, 130, 220, 210, 185, 135, 80, 40, 45 }; printf("Nhap thang: "); scanf("%d", &thang); printf("Luong mua trung binh cua thang: %d mm.n", bangmua[thang-1]); return 0; } Lập trình C - Việt Nhật 2007 111 Bảng lượng mưa (tiếp) #include <stdio.h> int main() { int thang; int bangmua[12] = { 30, 40, 45, 95, 130, 220, 210, 185, 135, 80, 40, 45 }; printf("Nhap thang: "); scanf("%d", &thang); printf("Luong mua trung binh cua thang: %d mm.n", bangmua[thang-1]); return 0; } Lập trình C - Việt Nhật 2007 112 Chú ý Các phần tử trong mảng được dùng như các biến đơn thông thường. Các hàm printf, scanf không làm việc với kiểu mảng, do đó việc nhập xuất giá trị là do người lập trình tự tiến hành với từng phần tử mảng. 1. // nhap gia tri cho cac phan tu mang 2. float a[4]; 3. for(int i=0; i<4; i++) 4. { 5. printf(“a[%d]=“,i); 6. scanf(“%f”, &a[i]); 7. } Chỉ số của phần tử mảng phải thuộc kiểu nguyên (int, short int, long int, char)
  • 29. 29 Lập trình C - Việt Nhật 2007 113 Khởi tạo mảng gồm các số chẵn từ 2 đến 20 #include <stdio.h> #define arraySize 10 int main() { int s[ arraySize ]; // mảng S có 10 phần tử int i; for ( i = 0; i < arraySize; i++ ) // khởi tạo giá trị s[ i ] = 2 + 2 * i; printf("Phan tu t Gia trin"); for ( i = 0; i < arraySize; i++ ) printf("%dt%dn", i, s[i]); return 0; } Lập trình C - Việt Nhật 2007 114 Nhập xuất dữ liệu cho mảng #include <stdio.h> #define SOTHANG 12 /* Lưu và hiển thị lượng mưa */ int main() { int solieu[SOTHANG]; int thang; for ( thang=0; thang < SOTHANG; thang++ ) { scanf("%d", &solieu[thang] ); } ... Lập trình C - Việt Nhật 2007 115 Nhập xuất dữ liệu cho mảng #include <stdio.h> #define SOTHANG 12 ... /* Print from January to December */ for ( thang=0; thang< SOTHANG; thang++ ) { printf( "%5d ” , solieu[thang] ); } printf("n"); /* Print from December to January */ for ( thang = SOTHANG - 1; thang >= 0; thang-- ) { printf( "%5d ” , solieu[month] ); } printf("n"); return 0; } Lập trình C - Việt Nhật 2007 116 1 /* 2 Histogram printing program */ 3 #include <stdio.h> 4 #define SIZE 10 5 6 int main() 7 { 8 int n[ SIZE ] = { 19, 3, 15, 7, 11, 9, 13, 5, 17, 1 }; 9 int i, j; 10 11 printf( "%s%13s%17sn", "Element", "Value", "Histogram" );12 13 for ( i = 0; i <= SIZE - 1; i++ ) { 14 printf( "%7d%13d ", i, n[ i ]) ; 15 16 for ( j = 1; j <= n[ i ]; j++ ) 17 printf( "%c", '*' ); 18 19 printf( "n" ); 20 } 21 22 return 0; 23 } Element Value Histogram 0 19 ******************* 1 3 *** 2 15 *************** 3 7 ******* 4 11 *********** 5 9 ********* 6 13 ************* 7 5 ***** 8 17 ***************** 9 1 *
  • 30. 30 Lập trình C - Việt Nhật 2007 117 Đổ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 111 2 51 2 21 2 10 2 0110111 Lập trình C - Việt Nhật 2007 118 Đổi số nguyên dương sang nhị phân Lập trình C - Việt Nhật 2007 119 Khởi tạo ngẫu nhiên Tạo dãy số nguyên dương 500 số. Mỗi số có giá trị trong khoảng 1 đến 1000. In ra màn hình giá trị trung bình của dãy, số phần tử trong dãy bằng, nhỏ hơn, lớn hơn giá trị trung bình. Tạo số ngẫu nhiên #include <stdlib.h> #include <time.h> srand(time(NULL)); /* Khởi tạo bộ sinh số ngẫu nhiên, gọi một lần duy nhất */ rand() ; sinh số nguyên ngẫu nhiên 1+ rand() % N Lập trình C - Việt Nhật 2007 120 Sắp xếp mảng Sắp xếp dữ liệu Là một ứng dụng quan trọng Hầu hết mọi cơ quan tổ chức cần sắp xếp dữ liệu Sắp xếp nổi bọt Duyệt mảng vài lần So sánh các cặp phần tử liên tiếp Nếu thứ tự tăng (hay bằng nhau) không thay đổi gì. Nếu thứ tự giảm: tráo đổi hai phần tử. Lặp lại bước trên cho mọi phần tử
  • 31. 31 Lập trình C - Việt Nhật 2007 121 Sắp xếp mảng Lập trình C - Việt Nhật 2007 122 Sắp xếp mảng Lập trình C - Việt Nhật 2007 123 Sắp xếp mảng for (pass = 0; pass < arraySize - 1; pass++ ) for (j = 0; j < arraySize - 1; j++ ) // So sánh hai phần tử kề nhau và đổi chỗ chúng khi // phần từ trước lớn hơn phần tử sau if ( a[ j ] > a[ j + 1 ] ) { hold = a[ j ]; a[ j ] = a[ j + 1 ]; a[ j + 1 ] = hold; } Lập trình C - Việt Nhật 2007 124 Mảng hai chiều Một phần tử mảng có thể là một mảng khác Mảng của các mảng được gọi là mảng nhiều chiều Hàng 0 Hàng 1 Hàng 2 Cột 0 Cột 1 Cột 2 Cột 3 a[ 0 ][ 0 ] a[ 1 ][ 0 ] a[ 2 ][ 0 ] a[ 0 ][ 1 ] a[ 1 ][ 1 ] a[ 2 ][ 1 ] a[ 0 ][ 2 ] a[ 1 ][ 2 ] a[ 2 ][ 2 ] a[ 0 ][ 3 ] a[ 1 ][ 3 ] a[ 2 ][ 3 ] Chỉ số hàng Tên mảng Chỉ số cột
  • 32. 32 Lập trình C - Việt Nhật 2007 125 Mảng nhiều chiều Khai báo mảng 2 chiều: type name[row_size][column_size]; Khởi tạo int SumSquares[2][3] = { {0,1,4}, {1,2,5} }; int SumSquares[2][3] = { {0,1,4} }; int SumSquares[ ][3] = { {0,1,4}, {1,2,5} }; int SumSquares[ ][3] = { {0,1, }, {1} }; int SumSquares[ ][3]; 0 1 4 1 2 5 SumSquares 0 1 4 1 2 5 Lập trình C - Việt Nhật 2007 126 Ví dụ: Lượng mưa hàng năm Bài toán: thao tác trên bảng vũ biểu • input: tháng và năm • output: lượng mưa trung bình vào năm, tháng đó 0 1 2 3 4 5 6 7 8 9 10 11 0 30 40 75 95 130 220 210 185 135 80 40 45 1 25 25 80 75 115 270 200 165 85 5 10 0 2 35 45 90 80 100 205 135 140 170 75 60 95 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 nam thang Lượng mưa trung bình năm (mm) Lập trình C - Việt Nhật 2007 127 #define NYEARS 5 #define NMONTHS 12 int main() { int table[NYEARS][NMONTHS] = { {30,40,75,95,130,220,210,185,135,80,40,45}, {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} }; int year, month; printf("Enter year and month: "); scanf("%d %d", &year, &month); if ((0 <= year) && (year < NYEARS) && (0 <= month) && (month < NMONTHS)) printf("Rainfall for year %d month %d is %d", year, month, table[year-1][month-1]); else { printf("Data non available"); } return 0; } Lập trình C - Việt Nhật 2007 128 Nhập Mảng 2 chiều 1. #define MAX_STUDENT 5 2. #define MAX_SUBJECT 6 3. int StudentScore[MAX_STUDENT][MAX_SUBJECT]; 4. void read_Score(int Score[MAX_STUDENT][MAX_SUBJECT], int nStudents, int nSubjects) 5. { 6. int i,j; 7. for(i=0; i<nStudents; i++) 8. for(j=0; j<nSubjects; j++) 9. scanf(“%d”, &Score[i][j]); 10. }
  • 33. 33 Lập trình C - Việt Nhật 2007 129 Biểu diễn mảng 2 chiều 0 1 4 1 2 5 StudentScores 0 1 4 1 2 5 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Student1 Student2 Student1 Student2 Student3 Student4 Student5 Lập trình C - Việt Nhật 2007 130 Bài tập Viết chương trình nhập vào một dãy số theo thứ tự tăng, nếu 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 cho không thay đổi tính chất của dãy. In lại dãy mới để kiểm tra. Viết chương trình nhập vào một ma trận nguyên m hàng n 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 Ngôn ngữ lập trình C Hàm (function) Cao Tuấn Dũng 2007 Lập trình C - Việt Nhật 2007 132 Lập trình cấu trúc Với chương trình máy tính Với chương trình đơn giản, tất cả các xử lý nên được đặt trong hàm main. Chia để trị: Phân chia chương trình thành các phần nhỏ - các chương trình con (routine) hay còn gọi là các hàm (function) Chương trình con A Các biến Câu lệnh Kết thúc chương trình con A Chương trình con B Các biến Câu lệnh Kết thúc chương trình con B Chương trình Trong thực tế: Sản xuất bằng cách lắp ghép các module: các module được lắp ghép lại thành sản phẩm, các module có thể được cải tiến nhưng không ảnh hưởng đến các module khác trong sản phẩm.
  • 34. 34 Lập trình C - Việt Nhật 2007 133 Hàm – Mô đun hóa chương trình Trong C mọi chương trình con là hàm, không có sự phân biệt giữa hàm và thủ tục. Cách tiếp cận phân tích bài toán theo hướng top-down: xác định chức năng của các hàm. 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 gọi lẫn nhau và truyền các thông tin qua lại giữa các hàm. Các hàm có thể được dùng lại nhiều lần thành lập các thư viện 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 134 Hàm Là một đoạn mã lệnh độc lập, được đặt tên, thực hiện một nhiệm vụ cụ thể và có thể trả về một giá trị cho chương trình gọi 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ý Thống nhất các đoạn mã tương tự nhau, sử dụng nhiều lần trong một chương trình Tái sử dụng mã lệnh trong nhiều hơn một chương trình Lập trình C - Việt Nhật 2007 135 Các hàm toán học ceil(9.2) bằng 10.0 ceil(-9.8) bằng -9.0 làm tròn số nguyên nhỏ nhất không lớn hơn x ceil(x) nếu x≥0 thì fabs(x) bằng x nếu x<0 thì fabs(x) bằng -x giá trị tuyệt đối của x fabs(x) log10(1.0) bằng 0.0 log10(10.0) bằng 1.0 logarithm thập phân (cơ số 10) của x log10(x) log(2.718282) bằng 1.0 logarithm tự nhiên (cơ số e) của x log(x) exp(1.0) bằng 2.718282 hàm mũ exexp(x) sqrt(9.00) bằng 3.0Căn bậc 2 của x sqrt(x) Ví dụMô tảHàm tan(0.0) bằng 0.0tan của x (x theo radian) tan(x) cos(0.0) bằng 1.0cos của x (x theo radian) cos(x) sin(0.0) bằng 0.0sin của x (x theo radian) sin(x) fmod(13.657,2.333) bằng 1.992 phần dư của phép chia x cho y fmod(x,y ) pow(2,7) bằng 128x mũ y (xy)pow(x,y) floor(9.2) bằng 9.0 floor(-9.8) bằng -10.0 làm tròn số nguyên lớn nhất không nhỏ hơn x floor(x) Lập trình C - Việt Nhật 2007 136 Ví dụ đầu tiên: Hàm lập phương Cho vao mot so nguyen: 6 Gia tri lap phuong cua 6 la 216 #include <stdio.h> /* nguyen mau ham*/ int cube(int x); void main() { int a, kq; printf("Cho mot so nguyen:"); scanf("%d", &a); kq=cube(a); printf("n Gia tri lap phuong cua %d la %d", a, kq); } /* Ham tinh lap phuong */ int cube(int x) { int x3; x3 = x*x*x; return x3; }
  • 35. 35 Lập trình C - Việt Nhật 2007 137 Hoạt động của hàm Các lệnh trong hàm chưa được thực hiện cho tới khi hàm được gọi. Khi hàm được gọi điều khiển thực hiện các lệnh được chuyển cho hàm. Chương trình gửi thông tin đến hàm: các đối số Hàm gửi lại thông tin: giá trị trả về Bốn bước cơ bản khi gọi hàm: Chương trình Khai báo Biến; Lệnh; …. Lời gọi hàm Lệnh ……. Kết thúc Hàm danh sách đối số Giá trị trả về Cấp phát bộ nhớ cho các đối số và biến cục bộ Gán giá trị của các tham số thực cho đối số tương ứng 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 138 Khai báo hàm Nguyên mẫu hàm (prototype): nguyên mẫu hàm cho chương 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 số, kiểu giá trị trả về cụ thể Cú pháp: 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); int Min(int x, int y); Nguyên mẫu hàm chỉ cần thiết khi phần định nghĩa hàm nằm phía sau lời gọi hàm trong chương trình. Nguyên mẫu hàm được chương trình dịch sử dụng để kiểm tra tính hợp lệ của hàm Nguyên mẫu hàm phải khớp với định nghĩa hàm Lập trình C - Việt Nhật 2007 139 Tệp tiêu đề Chứa các nguyên mẫu hàm của các hàm trong thư viện stdio.h, stdlib.h, math.h, time.h Chứa nguyên mẫu các hàm do người dùng định nghĩa Sử dụng tệp tiêu đề do người dùng định nghĩa Khai báo các hàm vào lưu trong file riêng Đặt tên filename.h Khai báo sử dụng các hàm này ở chương trình khác #include "filename.h" Sử dụng lại các hàm đã khai báo Lập trình C - Việt Nhật 2007 140 Định nghĩa hàm Nơi cài đặt thực sự mã cho một hàm Dòng đầu tiên tương tự như nguyên mẫu hàm, tuy nhiên chỉ định rõ danh sách đối số không kết thúc bằng dấu ; Phần tiếp theo là thân hàm
  • 36. 36 Lập trình C - Việt Nhật 2007 141 Các thành phần của hàm Tên hàm (name) danh sách tham số (list of parameters) kiểu trả về (return type) thân hàm (function body) lệnh trả về (return) <return_type> function_name (<list_of_parameters>) Các hàm phải được khai báo trước khi được gọi thi hành. giao diện (interface) của hàm Lập trình C - Việt Nhật 2007 142 Hàm radian Hàm chuyển đổi số đo góc từ độ sang radian #include <stdio.h> #include <math.h> #define PI 3.14159 /* nguyen mau ham*/ float Radian(float x); void main() { float ang, adj, opp; printf("Nhap gia tri cua goc (theo do:"); scanf("%f", &ang); printf("Nhap canh ke:"); scanf("%f", &adj); opp= adj*tan(Radian(ang)); printf("Canh doi la: %fn", opp); } /* 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 143 Thành phần của hàm – Tên hàm Tên hàm là một định danh (identifier), do đó nó tuân theo các quy định của ngôn ngữ C cho định danh. (xem bảng các toán tử) Nên đặt tên có ý nghĩa. Không đặt tên trùng với tên các hàm hệ thống trong C hoặc các từ khóa của C. Lập trình C - Việt Nhật 2007 144 Danh sách tham số Danh sách tham số xác định các đối số được đưa vào hàm. Các đối số được khai báo trong phần mô tả cài đặt của hàm thì được gọi là các tham số hình thức (formal parameters). Mỗi tham số hình thức là một cặp: <type> <identifier>. Từ khoá void có thể được dùng nếu không có tham số hình thức nào cần khai báo. Các tham số trong các hàm khác nhau có thể trùng tên. 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.
  • 37. 37 Lập trình C - Việt Nhật 2007 145 Ví dụ /* ... */ 1. float max(float x, float y) 2. { 3. return (x > y ? x : y); 4. } /*…*/ 6. int main() 7. { 8. float z = 4.7; 9. float x = max(4.5, z); 10. } Lập trình C - Việt Nhật 2007 146 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 giá trị: giá trị trả về. Chương trình gọi hàm có thể sử dụng giá trị trả về. 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. Kiểu int sẽ là kiểu của trị trả về nếu không chỉ rõ kiểu giá trị trả về trong khai báo hàm. ví dụ: afunction() { /*…*/ } Lập trình C - Việt Nhật 2007 147 Thân hàm (function body) { /* các đoạn mã trong thân hàm */ } Các biến có thể được khai báo bên trong hàm biến cục bộ (local variable) Biến cục bộ không được trùng tên với tham số hình thức trong khai báo hàm. Các biến cục bộ chỉ có giá trị trong phạm vi của hàm. Lập trình C - Việt Nhật 2007 148 Hàm tìm USCLN giữa hai số int USCLN(int a, int b) { a=abs(a); b=abs(b); while(a!=b) { if(a>b) a=a-b; else b=b-a; } return a; }
  • 38. 38 Lập trình C - Việt Nhật 2007 149 Phạm vi truy cập của biến Phạm vi truy cập (scope) của biến xác định vùng chương trình có thể truy cập đến biến. Biến được khai báo trong khối lệnh (nằm giữa { }) có thể được truy cập bởi các lệnh nằm trong cùng khối và các lệnh thuộc các khối con. Biến được khai báo “ngoài cùng” có phạm vi truy cập trong toàn chương trình biến toàn cục (global variables). Biến cục bộ (local variable) được khai báo và sử dụng trong phạm vi một khối lệnh và các khối lệnh con. Biến thuộc phạm vi trong cùng được tham chiếu đến đầu tiên. Lập trình C - Việt Nhật 2007 150 Ví dụ 1. int i=1; /* i là biến toàn cục vì nằm ở ngoài các khối lệnh */ 2. { /* block A */ 3. int i=2; 4. printf (“%dn”, i); /* outputs 2 */ } 5. { /* Block B */ 6. int i=3; 7. printf (“%dn”, i); /* outputs 3 */ 8. { /* Block C */ 9. int i=4; 10. printf (“%dn”, i); /* outputs 4 */ } 11. { /* Block D */ 12. printf (“%dn”, i); /* outputs 3 */ } 13. } 14. { /* Block E */ 15. printf (“%dn”, i); /* outputs 1 */ } Lập trình C - Việt Nhật 2007 151 Phạm vi của biến Lập trình C - Việt Nhật 2007 152 Biến tự động ( automatic) Biến automatic được khai báo trong một hàm và nó là biến cục bộ trong hàm này, tức là phạm vi của nó chính là hàm mà trong đó biến này được khai báo. 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.
  • 39. 39 Lập trình C - Việt Nhật 2007 153 Cấp lưu trữ và phạm vi các đối tượng Cấp lưu trữ ( storage class): thời gian sống + phạm vi Có tất cả bốn cấp lưu trữ trong C: tự động, toàn cục (ngoài), tĩnh và register được khai bao thông qua các từ khóa auto, extern, static và register. Mặt khác cũng lưu ý rằng cấp lưu trữ của một biến đôi khi được xác định bởi chính vị trí khai báo của biến đó trong chương trình. Trong các trường hợp khác người ta dùng bốn từ khóa nói trên trong khai báo Ví dụ: auto int a,b,c; extern float r1,r2; static int count = 0; extern char star; Lập trình C - Việt Nhật 2007 154 Biến tự động /* Hoán vị giá trị hai số nguyên */ #include <stdio.h> void swap(auto int* , auto int* ); /* Hàm nguyên mẫu */ main() { auto int x, y; x = 10; y = 20; printf("Ban đầu x = %d, y = %d", x, y); swap( &x, &y); printf("Sau đó x = %d, y = %d", x, y); } /* Định nghĩa hàm swap */ void swap( auto int* a, auto int* b) { auto int temp; temp = *a; *a = *b; *b = temp; } Lập trình C - Việt Nhật 2007 155 Biến toàn cục (global, external). Phạm vi (scope) của biến toàn cục là toàn bộ chương trình Các biến toàn cục được định nghĩa bên ngoài mọi hàm do vậy chúng có tiềm năng sử dụng cho nhiều hàm. Biến toàn cục còn đóng một vai trò quan trọng khi phát triển các ứng dụng lớn, vì khi đó hiển nhiên là các chương trình nguồn để trên nhiều tệp khác nhau. Từ khoá extern thông báo với trình biên dịch rằng tên và kiểu của biến đặt sau đã được khai báo ở đâu đó rồi. Lập trình C - Việt Nhật 2007 156 Biến toàn cục #include <stdio.h> long fibo(int count); long f1=1,f2=1;/* khai báo biến toàn cục */ main() { int count,n; printf("nn =");scanf("%d",&n); for(count=1;count<=n; ++count) printf("ni=%2d F=%ld", count,fibo(count)); } long int fibo(int count) { long int f; f=(count<3) ? 1: f1+f2; f2=f1; f1=f; return f; }
  • 40. 40 Lập trình C - Việt Nhật 2007 157 Biến tĩnh (static) 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 trình. 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; #include <stdio.h> int inc(){ static int n=0; n++; return n; } void main(){ printf("n Ket qua inc lan 1: %d", inc()); printf("n Ket qua inc lan 2: %d", inc()); } Ket qua inc lan 1: 1 Ket qua inc lan 2: 2 Lập trình C - Việt Nhật 2007 158 Biến tĩnh #include<stdio.h> long fibo(int count); main() { int count,n; printf("nn =");scanf("%d",&n); for(count=1;count<=n; ++count) printf("ni=%2d F=%ld", count,fibo(count)); } long int fibo(int count) { static long int f1=1,f2=1; long int f; f=(count<3) ? 1: f1+f2; f2=f1; f1=f; return f; } Nếu bỏ từ khóa static ở trong thân hàm main ???? Lập trình C - Việt Nhật 2007 159 Biến register Từ khoá register được đặt trước khai báo của các biến tự động để đề nghị trình biên dịch duy trì giá trị biến đó trong thanh ghi của máy tính, giúp nâng cao tốc độ thực hiện. Trình biên dịch có thể bỏ qua từ khoá register khi không còn đủ thanh ghi nữa và nói chung chúng chỉ áp dụng cho các biến kiểu int và char. Ví dụ sau đề nghị trình biên dịch đặt biến counter vào 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 160 Lệnh return kết thúc hàm và trả quyền điều khiển về cho phần chương trình có lời gọi hàm. cú pháp: return Expr; hoặc return; hàm tự kết thúc khi thực hiện hết lệnh cuối cùng.
  • 41. 41 Lập trình C - Việt Nhật 2007 161 Một số ví dụ Tính tổng các số nguyên tố nhỏ hơn n Lập trình C - Việt Nhật 2007 162 Một số ví dụ minh họa Chương trình tính tổng các số nguyên tố Lập trình C - Việt Nhật 2007 163 Một số bài tập Viết chương trình tính tiền thuê máy dịch vụ Internet và in ra màn hình kết quả. Dữ liệu nhập vào là giờ bắt đầu thuê (GBD), giờ kết thúc thuê (GKT) và số máy thuê Điều kiện dữ liệu: 6 <=GBD < GKT <= 21, Giờ nguyên. Đơn giá 2500 đ cho mỗi giờ trước 5h30 và 3500 cho mỗi giờ sau 5h30 Nhập vào ba số thực a, b, c kiểm tra xem chúng có phải là ba cạnh một tam giác không. Nếu có tính diện tích, độ dài 3 đường cao. Lập trình C - Việt Nhật 2007 164 Truyền tham số khi gọi hàm Truyền tham chiếu (call by reference): các tham chiếu đến các tham số hình thức là tham chiếu đến các đối số. Giá trị của các đối số có thể được thay đổi từ xử lý bên trong hàm. Truyền giá trị (call by value): các đối số đưa vào hàm được chép vào các tham số hình thức. Các giá trị của các đối số được sử dụng trong hàm nhưng những thay đổi của tham số hình thức trong hàm không làm thay đổi giá trị của các đối số truyền vào.
  • 42. 42 Lập trình C - Việt Nhật 2007 165 Truyền giá trị 1. /* Swapping routine that doesn’t work */ 2. #include <stdio.h> 3. void Swap(int x, int y) { int Temp; 4. Temp = x; 5. x = y; 6. y = Temp; 7. } 8. main(void) 9. { int Left, Right; 10. Left = 5; Right = 7; 11. Swap(Left, Right); 12. printf(“Left = %d, Right = %dn”, Left, Right); 13. } Lập trình C - Việt Nhật 2007 166 Tại sao truyền giá trị không làm thay đổi giá trị đối số main#1::Left = 5 main#1::Right = 7 Swap#1::Temp = ? Swap#1::x = 5 Swap#1::y = 7 M1 M2 M3 M4 M5 main#1::Left = 5 main#1::Right = 7 Swap#1::Temp = 5 Swap#1::x = 7 Swap#1::y = 5 M1 M2 M3 M4 M5 Lập trình C - Việt Nhật 2007 167 Truyền bằng tham chiếu /* Swapping routine that does work */ #include <stdio.h> void Swap(int &x, int &y) { int Temp; Temp = x; x = y; y = Temp; } main(void) { int Left, Right; Left = 5; Right = 7; Swap(Left, Right); printf(“Left = %d, Right = %dn”, Left, Right); } Lập trình C - Việt Nhật 2007 168 Tại sao truyền bằng tham chiếu làm thay đổi giá trị đối số main#1::Left = 5 main#1::Right = 7 Swap#1::Temp = ? Swap#1::x Swap#1::y M1 M2 M3 M4 M5 main#1::Left = 7 main#1::Right = 5 Swap#1::Temp = 5 Swap#1::x Swap#1::y M1 M2 M3 M4 M5
  • 43. 43 Lập trình C - Việt Nhật 2007 169 Dừng chương trình và mã lỗi Thông thường main trả về giá trị kiểu int có thể sử dụng khai báo: void main() Nên sử dụng giá trị trả về để kiểm soát xử lý của chương trình. Sử dụng hàm exit(<exitcode>); để dừng chương trình và trả về mã lỗi. 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 170 Truyền mảng cho hàm Lập trình C - Việt Nhật 2007 171 Truyền mảng cho hàm Lập trình C - Việt Nhật 2007 172 1 /* Fig. 6.13: fig06_13.c 2 Passing arrays and individual array elements to functions */ 3 #include <stdio.h> 4 #define SIZE 5 5 6 void modifyArray( int [], int ); /* appears strange */ 7 void modifyElement( int ); 8 9 int main() 10 { 11 int a[ SIZE ] = { 0, 1, 2, 3, 4 }, i; 12 13 printf( "Effects of passing entire array call " 14 "by reference:nnThe values of the " 15 "original array are:n" ); 16 17 for ( i = 0; i <= SIZE - 1; i++ ) 18 printf( "%3d", a[ i ] ); 19 20 printf( "n" ); 21 modifyArray( a, SIZE ); /* passed call by reference */ 22 printf( "The values of the modified array are:n" ); 23 24 for ( i = 0; i <= SIZE - 1; i++ ) 25 printf( "%3d", a[ i ] ); 26 27 printf( "nnnEffects of passing array element call " 28 "by value:nnThe value of a[3] is %dn", a[ 3 ] ); 29 modifyElement( a[ 3 ] ); 30 printf( "The value of a[ 3 ] is %dn", a[ 3 ] ); 31 return 0; 32 }
  • 44. 44 Lập trình C - Việt Nhật 2007 173 Hàm truy cập, in Mảng 2 chiều 1. void print_Score(int Score[MAX_STUDENT][MAX_SUBJECT], int nStudents, int nSubjects) 2. { 3. int i,j; 4. for(i=0; i<nStudents; i++) 5. { 6. for(j=0; j<nSubjects; j++) 7. printf(“%2dt”, &Score[i][j]); 8. printf(“n”); 9. } 10. } Lập trình C - Việt Nhật 2007 174 Chương trình 1. main(void) 2. { 3. int nStudents, nScores; 4. scanf(“%d %d”, &nStudents, &nScores); 5. if(nStudents <= MAX_STUDENT && nScores <= MAX_SCORES) 6. read_Score(StudentScore, nStudents, nScores); 7. print_Score (StudentScore, nStudents, nScores); 8. return 0; 9. } Lập trình C - Việt Nhật 2007 175 Sử dụng hàm như tham số. Ví dụ: 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); … Ngôn ngữ lập trình C Đệ Quy Cao Tuấn Dũng 2007
  • 45. 45 Lập trình C - Việt Nhật 2007 177 Nguyên lý Trong C cho phép trong thân một hàm có thể gọi ngay đến chính nó, cơ chế này gọi là đệ qui. Có thể định nghĩa hàm đệ qui là hàm sẽ gọi đến chính 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ó những điểm chung sau. 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 nhất (hay còn gọi là trường hợp cơ sở). Nếu hàm được gọi trong các trường hợp phức tạp hơn, hàm đệ qui sẽ 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 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 ban đầu. Để đả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 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 trường hợp cơ sở. Lập trình C - Việt Nhật 2007 178 Tính giai thừa Ví dụ: Viết chương trình nhập số tự nhiên n và tính giai thừa : n!. Giải quyết bài toán bằng vòng lặp 1. #include <stdio.h> 2. unsigned long int factorial(int n) 3. { unsigned long f = 1; 4. for (int i = 1; i<=n; i++) 5. f *= i; 6. return f; 7. } 8. int main(void) 9. { int n; 10. printf(“Nhap n:”); scanf(“%d”, &n); printf(“n! = %d! = %ln”, n, factorial(n)); 11. return 0; 12. } Lập trình C - Việt Nhật 2007 179 Tính giai thừa Tuy nhiên cũng có thể định nghĩa đệ qui hàm giai thừa như sau : nếu n>0 n! = n * (n-1)! nếu n=0 n! = 0! = 1 1. #include <stdio.h> 2. unsigned long int factorial(int n) 3. { if(n==0) 4. return 1; 5. return (n* factorial(n-1)); 6. } 7. int main(void) 8. { int n; 9. printf(“Nhap n:”); scanf(“%d”, &n); 10. printf(“n! = %d! = %ln”, n, factorial(n)); 11. return 0; 12. } Lập trình C - Việt Nhật 2007 180 Dãy Fibonaci Một ví dụ thứ hai dùng đệ qui là tính dẫy số Fibonaci Dãy số Fibonaci gồm những số 0, 1, 1, 2, 3, 5, 8, 13, 21 ... Bắt đầu từ hai số 0 và 1, tiếp sau đó các số Fibonaci sau bằng tổng của 2 số Fibonaci trước nó. Dẫy Fibonaci có thể định nghĩa đệ qui như sau: fibonaci(0) = 0; fibonaci(1) = 1; fibonaci(n) = fibonaci(n-1) + fibonaci(n-2) ∀n>1
  • 46. 46 Lập trình C - Việt Nhật 2007 181 Dãy Fibonaci (tiếp) /* Tính dẫy số Fibonaci phương pháp đệ qui */ #include <stdio.h> long fibo( long ); /* Hàm nguyên mẫu */ main() { long result, n; printf("Hãy nhập vào một số nguyên : "); scanf("%ld", &n); result = fibo(n); printf("Fibonaci thứ %ld là : %ldn", number, result); return 0; } /* Định nghĩa đệ qui hàm fibonaci */ long fibo( long n) { if ( n = 0 || n = 1 ) return n; else return fibo(n-1) + fibo(n-2); } Lập trình C - Việt Nhật 2007 182 Lời gọi hàm đệ quy và Điều kiện dừng của thuật giải đệ quy Bài toán giải bằng thuật giải đệ quy phải có điều kiện dừng. Thuật toán đệ quy trên máy tính có thể bị giới hạn bởi dung lượng bộ nhớ do lời gọi hàm liên tiếp. factorial (4) factorial (3) factorial (2) factorial (1) main 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 183 Bài toán Tháp Hà Nội Có 3 cái cột và một chồng đĩa ở cột thứ nhất. Hãy chuyển chồng đĩa sang cột thứ ba với điều kiện mỗi lần di chuyển chỉ một đĩa và các đĩa bé luôn nằm trên đĩa lớn. 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 184 Thuật giải Chuyển (n-1) đĩa sang cột trung gian. Chuyển đĩa lớn nhất sang cột đích. Chuyển (n-1) đĩa từ cột trung gian sang cột đích.
  • 47. 47 Lập trình C - Việt Nhật 2007 185 Cài đặt bằng đệ quy 1. MoveDisk(disk_number, starting_post, target_post, intermediate_post) 2. { 3. if(disk)number > 1) 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); 8. } 9. else 10. printf(“Move disk number 1 from post %d to post %d.n”, starting_post, target_post); 11. } Ngôn ngữ lập trình C CON TRỎ Cao Tuấn Dũng 2007 Lập trình C - Việt Nhật 2007 187 Con trỏ Biến con trỏ Khai báo biến con trỏ Địa chỉ và giá trị Truyền tham chiếu trong lời gọi hàm Lập trình C - Việt Nhật 2007 188 Truyền tham số qua trị 1. #include <stdio.h> 2. void move_one(int x, int y) 3. { 4. x = x-1; 5. y = y+1; 6. } 7. int main(void) 8. { 9. int a = 4, b = 7; 10. move_one(a, b); 11. print(“%d, %dn”, a, b); 12. return 0; 13. }
  • 48. 48 Lập trình C - Việt Nhật 2007 189 Bộ nhớ main#1::a main#1::b move_one#1::x move_one#1::y M1 M2 M3 M4 Lập trình C - Việt Nhật 2007 190 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ị. Khi chúng ta khai báo một biến, máy sẽ cấp phát cho biến đó 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 bytes .v.v 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. Bằng cách nào? x địa chỉtên biến giá trị41024: Lập trình C - Việt Nhật 2007 191 Kiểu dữ liệu Con trỏ Địa chỉ của biến được tính là số thứ tự của byte đầu tiên trong dãy các bytes được cấp cho biến. Tuỳ theo kích thước bộ nhớ người ta biểu diễn địa chỉ bằng hai hoặc bốn bytes. Một biến kiểu con trỏ (pointer) chứa một tham chiếu (reference) đến một biến loại khác. Nói khác đi, biến con trỏ chứa địa chỉ ô nhớ của một biến. int x; int* xp; /* con tro tro toi mot so nguyen */ x = 4; xp = &x; 41024: x 1024: xp Lập trình C - Việt Nhật 2007 192 Khai báo, toán tử và sử dụng trong hàm Khai báo kiểu dữ liệu con trỏ: int * “con trỏ đến kiểu int” float * “con trỏ đến kiểu float” char * “con trỏ đến kiểu character” Toán tử & địa chỉ của một đối tượng * giá trị của vùng nhớ biến con trỏ chỉ đến Con trỏ được dùng như tham số hình thức trong khai báo hàm để truyền và lấy các đối số có giá trị thay đổi.
  • 49. 49 Lập trình C - Việt Nhật 2007 193 Con trỏ và địa chỉ biến Địa chỉ của hai biến ký tự liên tiếp sẽ cách nhau một byte trong khi địa chỉ của hai biến nguyên liên tiếp cách nhau hai byte còn địa chỉ của hai biến thực liên tiếp cách nhau tới bốn bytes. Người ta phân biệt các con trỏ theo các kiểu địa chỉ chứa trong các con trỏ: con trỏ kiểu nguyên dùng để chứa địa chỉ 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. ... p a Lập trình C - Việt Nhật 2007 194 Sử dụng Con trỏ Truy cập vùng nhớ được chỉ bởi một con trỏ int x=1,y=2,z[10]; int *pi; /*pi là một biến con trỏ có kiểu nguyên*/ pi = &x; /*Địa chỉ của x được gán cho pi, và pi trỏ tới biến x*/ y=*pi; /*y có giá trị bằng 1*/ *pi=15; /*Từ bây giờ x có giá trị bằng 15*/ pi=&z[0]; /*Từ đây pi chứa địa chỉ của z[0], tức là địa chỉ của mảng z*/ 151024: x 1024: pi Lập trình C - Việt Nhật 2007 195 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 ,long) để cho kết quả là một con trỏ cùng kiểu. Xét ví dụ sau: int *x=2,*px,*py; px = &x; py = px+1; /*py trỏ đến số nguyên nằm sau x trong bộ nhớ*/ Phép trừ hai con trỏ cùng kiểu được coi là hợp lệ và cho kết quả là một số nguyên biểu thị "khoảng cách" (ở đây bằng số phần tử ) giữa hai biến con trỏ. Ví dụ 3.?. Quay về ví dụ trên, câu lệnh x=py-px; 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 phép nhân, chia hai con trỏ. Lập trình C - Việt Nhật 2007 196 Các phép toán trên con trỏ Có thể áp dụng các phép so sánh, phép gán cho các con trỏ. Trong các phép toán này đòi hỏi các toán hạng con trỏ phải có cùng kiểu. Mọi sự chuyển đổi kiểu tự động từ các kiểu khác thành các kiểu con trỏ phải luôn luôn được cân nhắc và hỏi ý kiến bởi chương trình biên dịch. int *addr1; char *addr2; addr1=0; /*Chương trình dịch sẽ đưa ra một warning */ addr2 = (char *)0; /*Phép gán này hợp lệ*/ if(addr1==addr2) /*Gây ra một warning*/ { addr1 = (int *)addr2; /*Hợp lệ*/ }
  • 50. 50 Lập trình C - Việt Nhật 2007 197 Sử dụng con trỏ như tham số #include <stdio.h> void move_one(int* xPtr, int* yPtr) { *xPtr = *xPtr-1; *yPtr = *yPtr+1; } int main(void) { int a, b; a=4; b=7; move_one(&a, &b); print(“%d, %dn”, a, b); return 0; } Lập trình C - Việt Nhật 2007 198 Bộ nhớ main#1::a main#1::b move_one#1::xPtr move_one#1::yPtr M1 M2 M3 M4 Lập trình C - Việt Nhật 2007 199 Hàm swap void swap(int *px, int *py) { int temp; temp = *px; *px = *py; *py = temp; } main(void) { int a, b; a=2; b=9; swap(&a, &b); } Lập trình C - Việt Nhật 2007 200 Bài tập Lập chương trình: - Nhập từ bàn phím một số nguyên. - Đưa ra màn hình dưới dạng hexadecimal nội dung byte cao và byte thấp của số nguyên. #include <stdio.h> main() { int n; char *pc; printf("Nhap vao so nguyen : "); scanf("%d",&n); pc=(char *)&n; /*pc trỏ đến byte cao cua n, pc+1 trỏ đến byte thấp*/ printf("Byte cao %x ,byte thap %x",*pc ,*pc++); }
  • 51. 51 Lập trình C - Việt Nhật 2007 201 Con trỏ void Kiểu dữ liệu void Giá trị trả về của hàm: không cần giá trị trả về Tham số trong hàm: không có tham số Con trỏ kiểu void được khai báo như sau: void *tên_con_trỏ; Đâ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 lệ: void *px,*py; int x=1; float y=0.1; px=&x; py=&y; Lập trình C - Việt Nhật 2007 202 Con trỏ và mảng một chiều Xét câu lệnh: int a[10]; 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à: a tương đương với &a[0] Nếu pa là một con trỏ kiểu nguyên, int *pa; Khi đó phép gán pa = &a[0]; Đem pa trỏ đến phần tử thứ nhất (có chỉ số là 0) của a; nghĩa là pa chứa địa chỉ của a[0]. a[9]a[8]a[7]a[6]a[5]a[4]a[3]a[2]a[1]a[0] a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] pa Lập trình C - Việt Nhật 2007 203 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] 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. ... ... a[9]pa+9 pa+1 a[1] a[0]pa Lập trình C - Việt Nhật 2007 204 Con trỏ và mảng một chiều 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ệ. 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). 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. Tính tổng của chúng
  • 52. 52 Lập trình C - Việt Nhật 2007 205 #include <stdio.h> main() { float a{5],s; int i for(i=0;i<5;i++) [ printf("na[%d] = ",i) ; scanf("%f",a+i); } s=0; for(i=0;i<5;i++) s+=a[i]; printf("n tong la: %10.2f",s); } #include <stdio.h> main() { float a{5],s; int i for(i=0;i<5;i++) [ printf("na[%d] = ",i) ; scanf("%f",&a[i]); } s=0; for(i=0;i<5;i++) s+=a[i]; printf("n tong la: %10.2f",s); } Lập trình C - Việt Nhật 2007 206 #include <stdio.h> main() { float a{5],s,*p; int i; p=a; for(i=0;i<5;i++) [ printf("na[%d] = ",i) ; scanf("%f",p+i); } s=0; for(i=0;i<5;i++) s+=*(p+i); printf("n tong la: %10.2f",s); } #include <stdio.h> main() { float a{5],s, *p; int i; p=a; for(i=0;i<5;i++) [ printf("na[%d] = ",i) ; scanf("%f",&p[i]); } s=0; for(i=0;i<5;i++) s+=p[i]; printf("n tong la: %10.2f",s); } Lập trình C - Việt Nhật 2007 207 Truyền mảng qua con trỏ 1. #include <stdio.h> 2. #define SIZE 5 3. void getArray(int *a, int size); 4. main() 5. {int an_array[SIZE]; 6. getArray(an_array, SIZE); 7. return 0; 8. } 9. void getArray(int *a, int size) 10. {for(int i=0; i<size; i++) { 11. printf(“a[%d]=“); 12. scanf(“%d”, &a[i]); 13. } 14. } Lập trình C - Việt Nhật 2007 208 Con trỏ và mảng nhiều chiều Phép toán lấy địa chỉ nói chung không dùng được đối với các thành 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). Phép cộng địa chỉ trong mảng hai chiều Xét khai báo float a[2][3]; 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 thứ tự sau: 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 nó; phép cộng địa chỉ phải hiểu như sau: C cho rằng mảng hai chiều là 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ử của nó là một dãy gồm 3 số thực. Vì vậy a trỏ tới đầu hàng thứ nhất (phần tử a[0][0] a+1 trỏ tới đầu hàng thứ hai (phần tử a[1][0]
  • 53. 53 Lập trình C - Việt Nhật 2007 209 Con trỏ và mảng hai chiều Để truy xuất vào các phần tử của mảng hai chiều ta vẫn có thể dùng con trỏ theo cách sau: float *p, a[2][3]; p=(float*)a; Khi đó p trỏ tới a[0][0] p+1 trỏ tới a[0][1] p +2 trỏ tới a[0][2] p +3 trỏ tới a[1][0] p +4 trỏ tới a[1][1] p +5 trỏ tới a[1][2] Lập trình C - Việt Nhật 2007 210 Tính địa chỉ từng phần tử Để ý 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 a trỏ đến dòng thứ nhất a+1 trỏ đến dòng thứ hai Để tính toán được địa chỉ của phần tử ở dòng i cột j chúng ta phải dùng phép chuyển đổi kiểu bắt buộc đối với a: (float * )a, đây là 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). 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ỉ cuả thành phần a[0]..[0] (k chỉ số 0) là (type *)a, và địa chỉ của a[i1][i2]...[ik] được tính như sau ( *)type a i n ij l k j k l j k + + = − = + ∑ ∏1 1 1 Lập trình C - Việt Nhật 2007 211 Ví dụ: Đọc dữ liệu cho mảng hai chiều #include <stdio.h> main() { float a[2][3],*p; int i,m,n; p=(float*)a; for(i=0;i<2;i++) for(j=0;j<3;j++) { printf("a[%d][%d] = ",i,j); scanf("%f",p+i*3+j); } for(i=0;i<2;i++) for(j=0;j<3;j++) { printf("%6.2f",a[i][j]); if(j==2) printf("n"); } } Lập trình C - Việt Nhật 2007 212 Mảng các con trỏ Chúng ta có thể khai báo một mảng các con trỏ bằng câu lệnh sau: type *pointer_array[size]; ví dụ câu lệnh, char *ma[10]; Sẽ khai báo một mảng 10 con trỏ char có thể được dùng để khai báo một mảng để lưu trữ địa chỉ của mười chuỗi ký tự nào đó. Nếu các con trỏ được chuẩn bị để chỉ đến một biến nào đó đã 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 biến đó có liên tiếp hay không.
  • 54. 54 Lập trình C - Việt Nhật 2007 213 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ỉ của các biến int có giá trị và vị trí bất kỳ. Chúng ta sẽ dùng một hàm để sắp xếp lại các địa chỉ này trong mảng để sao cho cá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. Không làm thay đổi vị trí hoặc thay đổi các giá trị của các biến nhưng mảng vẫn có vẻ như là một mảng chỉ đến các giá trị đã sắp xếp có thứ tự. #include <stdio.h> main() { int i,j,*x; int d=10,e=3,f=7; int a=12,b=2,c=6; int *ptr_array[6]; Lập trình C - Việt Nhật 2007 214 /*Gán các thành phần của mảng*/ ptr_array[0]=&a; ptr_array[1]=&b; ptr_array[2]=&c; ptr_array[3]=&d; ptr_array[4]=&e; ptr_array[5]=&f; /*Sắp xếp lại dãy số*/ for(i=0;i<5;i++) for(j=i+1;j<6;j++) if(*ptr_array[i]>*ptr_array[j]) { x=ptr_array[i]; ptr_array[i]=ptr_array[j]; ptr_array[j]=x; } /*In kết quả sau khi sắp xếp*/ for(i=0;i<6;i++) printf(" %d n",*ptr_array[i]); } Lập trình C - Việt Nhật 2007 215 Mảng nhiều chiều và mảng con trỏ Giữa mảng nhiều chiều và mảng các con trỏ tồn tại nhiều điểm khác nhau: Mảng nhiều chiều thực sự là mảng có khai báo, do đó có chỗ đầy đủ cho tất cả các phần tử của nó. Còn mảng các con trỏ chỉ mới có chỗ cho các biến con trỏ mà thôi. Việc sử dụng mảng các con trỏ có hai ưu điểm: Việc truy xuất đến các phần tử là truy xuất gián tiếp thông qua các con trỏ và như vậy, vị trí của các mảng con này có thể là bất kỳ, và chúng có thể là những mảng đã có bằng cách xin cấp phát chỗ động hay bằng khai báo biến mảng bình thường, tùy ý. Các mảng con của nó được chỉ đến bởi các con trỏ, có thể có độ dài tùy ý, hoặc có thể không có (nếu con trỏ đó không được chuẩn bị, hoặc được gán bằng NULL). Lập trình C - Việt Nhật 2007 216 Cấp phát động Ý nghĩa của việc cấp phát bộ nhớ động là cho phép chương trình sử dụng vừa đúng khối lượng bộ nhớ mà chương trình cần, và khi không cần dùng tới nữa thì có thể giải phóng để cho các công việc tiếp theo có thể sử dụng được. Như vậy, cùng một vùng bộ nhớ có thể được sử dụng cho các mục đích khác nhau trong thời gian thực hiện của chương trình. Vùng nhớ heap được sử dụng cho mục đích cấp phát động các khối nhớ có kích thước thay đổi. Có nhiều cấu trúc dữ liệu sử dụng cách cấp phát động, ta có thể liệt kê một vài loại như: cây (tree), các loại danh sách liên kết.