Tài liệu Giáo trình ngôn ngữ lập trình C: Lý thuyết và thực hành: BÙI THẾ HỒNG 
 GI ÁO TR ÌNH 
Ngôn ngữ lập trình C: 
L Ý THUYẾT VÀ THỰC HÀNH 
NHÀ XUẤT BẢN KHOA HỌC KỸ THUẬT 
 LỜI NÓI ĐẦU 
Phần I: Các khái niệm cơ bản 
Bài 1 Mở đầu 
Tóm tắt lịch sử phát triển của ngôn ngữ C 
Ngôn ngữ lập trình C do Dennis Ritchie tạo lập vào năm 1972 tại Bell Telephone 
Laboratories. Mục đích ban đầu của ngôn ngữ này là để thiết kế hệ điều hành UNIX. 
C là một ngôn ngữ mạnh và mềm dẻo nên nó đã được rất nhiều người sử dụng. Khắp nơi 
người ta đã bắt đầu dùng nó để viết mọi loại chương trình. Tuy nhiên, các tổ chức khác 
nhau đã bắt đầu sử dụng các version khác nhau của C, và đã phát sinh nhiều sự khác nhau 
trong các thao tác làm cho người lập trình phải đau đầu. Để khắc phục vấn đề này, năm 
1983, Viện Tiêu chuẩn Quốc gia của Mỹ (ANSI) đã thành lập một ủy ban để đưa ra một 
định nghĩa chuẩn cho ngôn ngữ C, được gọi là ANSI Standard C. 
Có lẽ phải nói thêm một chút về tên của ngôn ngữ. Ngôn ngữ này có tên là C vì trước nó đã 
có một ngôn ngữ được gọ...
                
              
                                            
                                
            
 
            
                 195 trang
195 trang | 
Chia sẻ: putihuynh11 | Lượt xem: 2362 | Lượt tải: 0 
              
            Bạn đang xem trước 20 trang mẫu tài liệu Giáo trình ngôn ngữ lập trình C: Lý thuyết và thực hành, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
BÙI THẾ HỒNG 
 GI ÁO TR ÌNH 
Ngôn ngữ lập trình C: 
L Ý THUYẾT VÀ THỰC HÀNH 
NHÀ XUẤT BẢN KHOA HỌC KỸ THUẬT 
 LỜI NÓI ĐẦU 
Phần I: Các khái niệm cơ bản 
Bài 1 Mở đầu 
Tóm tắt lịch sử phát triển của ngôn ngữ C 
Ngôn ngữ lập trình C do Dennis Ritchie tạo lập vào năm 1972 tại Bell Telephone 
Laboratories. Mục đích ban đầu của ngôn ngữ này là để thiết kế hệ điều hành UNIX. 
C là một ngôn ngữ mạnh và mềm dẻo nên nó đã được rất nhiều người sử dụng. Khắp nơi 
người ta đã bắt đầu dùng nó để viết mọi loại chương trình. Tuy nhiên, các tổ chức khác 
nhau đã bắt đầu sử dụng các version khác nhau của C, và đã phát sinh nhiều sự khác nhau 
trong các thao tác làm cho người lập trình phải đau đầu. Để khắc phục vấn đề này, năm 
1983, Viện Tiêu chuẩn Quốc gia của Mỹ (ANSI) đã thành lập một ủy ban để đưa ra một 
định nghĩa chuẩn cho ngôn ngữ C, được gọi là ANSI Standard C. 
Có lẽ phải nói thêm một chút về tên của ngôn ngữ. Ngôn ngữ này có tên là C vì trước nó đã 
có một ngôn ngữ được gọi là B. Ngôn ngữ B do Ken Thompson phát triển cũng tại Bell 
Labs. Có thể, B là để chỉ tên của phòng thí nghiệm, còn C chắc là do đứng sau B. 
Tại sao lại dùng C? 
Trong thế giới lập trình ngày nay, có rất nhiều ngôn ngữ lập trình bậc cao để lựa chọn, chẳng 
hạn như C, Pascal, BASIC, Modula, v.v. Tất cả đều là những ngôn ngữ tuyệt vời phù hợp 
cho hầu hết các công việc lập trình. Nhưng tuy vậy, vẫn có một vài lý do để nhiều nhà lập 
trình chuyên nghiệp cảm thấy C là ngôn ngữ đứng đầu, vì: 
 C là một ngôn ngữ mạnh và mềm dẻo. Hạn chế duy nhất của C chính là sự hạn chế 
trong tư duy trừu tượng của chính người lập trình mà thôi. C được sử dụng cho nhiều 
mục đích khác nhau, như thiết kế các hệ điều hành, các bộ soạn thảo văn bản, đồ hoạ, 
trang tính, và thậm chí làm các chương trình dịch cho các ngôn ngữ khác. 
 C là một ngôn ngữ bình dân được nhiều người lập trình chuyên nghiệp ưa dùng. Bằng 
chứng là đã có rất nhiều chương trình dịch khác nhau và nhiều tiện ích kèm theo. 
 C là một ngôn ngữ chuyển đổi được tức là một chương trình C được viết cho một hệ 
máy vi tính (ví dụ IBM PC) có thể được dịch và chạy trên một hệ thống khác (ví dụ 
DEC VAX) mà chỉ cần thay đổi chút ít hoặc không cần thay đổi gì cả. 
 C là một ngôn ngữ ngắn gọn, nó chỉ bao gồm một số các từ được gọi là từ khóa 
(keyword) làm cơ sở để tạo ra các câu lệnh của ngôn ngữ. 
Với các đặc điểm trên, C là sự lựa chọn tuyệt vời đối với một ngôn ngữ lập trình. Nhưng có 
lẽ người ta còn nghe nói về C++ và một kỹ thuật lập trình mới được gọi là lập trình hướng 
đối tượng. Cũng không có gì đáng hoang mang cho lắm vì thực ra C++ chỉ là một ngôn ngữ 
siêu C với tất cả những gì mà C có cộng thêm với kỹ thuật lập trình hướng đối tượng. Nếu ai 
đó bắt đầu ngay bằng việc học C++, thì tất cả những gì đã được dạy khi học C vẫn còn 
được áp dụng cho C++. Trong khi học C, không những chỉ học một ngôn ngữ lập trình phổ 
thông và mạnh nhất hôm nay mà còn tự chuẩn bị cho mình một kỹ thuật lập trình hướng đối 
tượng cho ngày mai. 
Chuẩn bị cho việc lập trình 
Khi giải một bài toán cần phải tiến hành một số bước nhất định. Đầu tiên phải hình thành hay 
còn gọi là định nghĩa bài toán. Nếu không biết bài toán cần phải giải quyết là gì thì không thể 
nào tìm ra kết qủa được. Một khi bài toán đã được định rõ, thì có thể lập ra một kế hoạch 
để giải quyết nó. Sau khi đã có một kế hoạch thì việc thực hiện bài toán này sẽ trở nên dễ 
dàng. Cuối cùng, một khi kế hoạch đã được thực thi, thì các kết qủa phải được thử lại để 
kiểm chứng xem liệu bài toán đã được giải quyết đúng và trọn vẹn chưa. Một logic như trên 
có thể được áp dụng cho nhiều lĩnh vực khác nhau bao gồm cả việc lập trình trên máy tính. 
Khi tạo lập một chương trình trong C, chúng ta nên tiến hành theo các bước sau đây: 
1. Xác định các mục tiêu của chương trình. 
2. Xác định các phương pháp mà ta muốn sử dụng trong khi viết chương trình. 
3. Tạo lập chương trình để giải quyết bài toán. 
4. Chạy chương trình để xem xét các kết qủa. 
Chu trình phát triển chương trình 
Chu trình phát triển chương trình có các bước riêng của nó. Trong bước thứ nhất, một bộ 
soạn thảo sẽ được sử dụng để tạo ra một tệp đĩa chứa các mã gốc (source code). Tại bước 
thứ hai, các mã gốc sẽ được dịch để tạo ra một tệp đích (object file). Tại bước thứ ba, các 
mã đã được dịch sẽ được kết nối lại để tạo ra một tệp chương trình có thể chạy được 
(executable file). Cuối cùng, bước bốn là để chạy chương trình và kiểm tra xem nó có làm 
việc như đã dự kiến không. 
Tạo lập mã gốc 
Các mã gốc là một loạt các câu lệnh, hoặc lệnh được sử dụng để chỉ thị cho máy tính thực 
hiện các công việc mà người lập trình mong muốn. Ví dụ, dưới đây là một dòng của mã gốc: 
printf ("Chào các bạn"); 
Lệnh này chỉ thị cho máy tính hiện trên màn hình thông điệp Chào các bạn. 
Hầu hết các chương trình dịch đều có một bộ sạon thảo để soạn ra các tệp mã gốc. Tuy 
nhiên, ta cũng có thể sử dụng bất kỳ một bộ soạn thảo văn bản nào để viết các chương trình 
C. Chỉ cần lưu ý là khi cất giữ tệp thì phải chọn kiểu tệp là văn bản và có phần mở rộng là 
.C. 
Dịch các mã gốc 
Các máy tính chỉ hiểu được một ngôn ngữ, đó là ngôn ngữ máy (machine language). Bởi 
vậy, trước khi một chương trình C có thể chạy được trên một máy tính, nó phải được dịch 
từ các mã gốc sang mã máy bằng một chương trình được gọi là chương trình dịch 
(compiler). Chương trình dịch lấy các mã gốc làm input và cho ra một tệp đĩa chứa các câu 
lệnh mã máy tương ứng với các câu lệnh mã gốc. Các câu lệnh mã máy được tạo ra bởi 
chương trình dịch được gọi là các mã đích và tệp đĩa chứa chúng được gọi là tệp đích. Tệp 
đích có tên trùng với tên tệp gốc và luôn có đuôi là OBJ. Đuôi OBJ để chỉ rằng tệp này là 
tệp đích và sẽ được sử dụng bởi chương trình kết nối (linker). 
Kết nối để tạo ra một tệp có thể chạy được 
Cần phải thực hiện thêm một bước nữa thì chương trình mới có thể chạy được. Trong C có 
một bộ phận được gọi là thư viện các hàm, nó chứa các mã đích của các hàm đã được lập 
sẵn. Ví dụ, hàm printf được sử dụng trong ví dụ trước là một hàm thư viện. Các hàm thư 
viện thực hiện các công việc thường hay phải dùng đến, chẳng hạn như hiện các thông tin lên 
màn hình, đọc số liệu từ các tệp đĩa. Nếu một chương trình có sử dụng các hàm thư viện thì 
tệp đích của nó cần phải được kết nối với mã đích của các hàm này để tạo ra một chương 
trình có thể chạy được. Tệp này có tên trùng với tên tệp gốc và có đuôi là .EXE. 
Hoàn thiện chu trình phát triển 
Sau khi dịch và kết nối cần phải chạy thử chương trình. Nếu kết qủa nhận được không như 
mong muốn thì cần phải tìm ra nguyên nhân dẫn đến các sai sót và sửa lại chương trình cho 
đúng. Khi có bất kỳ một thay đổi nào trong mã gốc, chương trình phải được dịch và kết nối 
lại. Chu trình trên sẽ phải lặp đi lặp lại cho đến khi nào nhận được kết qủa như mong muốn. 
Một điểm lưu ý cuối cùng về việc dịch và kết nối là, mặc dù việc dịch và kết nối như đã trình 
bầy trên đây là hai giai đoạn tách biệt nhưng nhiều chương trình dịch, chẳng hạn các chương 
trình dịch chạy dưới DOS lại gộp hai bước trên thành một. Cho dù như vậy nhưng vẫn cứ 
phải hiểu rằng đây là hai qúa trình riêng rẽ ngay cả khi chúng chỉ được thực hiện bằng một 
lệnh. 
Chu trình phát triển một chương trình C 
Bước 1: Sử dụng một bộ soạn thảo để viết các mã gốc. 
Thông thường các tệp gốc của C có đuôi là .C 
Bước 2: Dùng một chương trình để dịch chương trình gốc. 
Nếu chương trình dịch không phát hiện một sai sót nào trong chương trình gốc, 
nó sẽ sinh ra một tệp đích. Tệp này có cùng tên với tệp chương trình gốc với 
đuôi là .OBJ. Nếu chương trình dịch tìm thấy một lỗi nào đó, nó sẽ thông báo 
lên màn hình và ta phải trở lại từ bước một để sửa các lỗi đã được phát hiện 
trong chương trình gốc. 
Bước 3: Kết nối chương trình bằng bộ kết nối. 
Nếu không có lỗi, bộ kết nối sẽ sinh ra một tệp đĩa có tên trùng với tên chương 
trình đích với đuôi .EXE, chứa một chương trình có thể chạy được. 
Bước 4: Chạy chương trình. 
Cần phải chạy thử chương trình sau khi kết thúc bước 3 để xem nó đã hoạt 
động đúng như mong muốn chưa. Nếu chưa thì phải quay lại từ bước 1 và tiến 
hành các thay đổi cần thiết trong mã gốc. 
Kết luận 
C là một công cụ lập trình mạnh, thông dụng và chuyển đổi được. Bài này giải thích các 
bước khác nhau trong qúa trình viết một chương trình C. Đó là chu trình soạn 
thảo/dịch/kết nối/kiểm tra. 
Việc mắc lỗi trong khi phát triển chương trình là một điều không thể tránh khỏi. Chương 
trình dịch của C sẽ phát hiện các lỗi trong mã gốc và hiện một thông báo lỗi về kiểu lỗi và vị 
trí của lỗi. Các thông tin này giúp người lập trình sửa lại mã gốc một cách dễ dàng. Tuy 
nhiên, chương trình dịch không phải lúc nào cũng thông báo được chính xác kiểu của lỗi 
cũng như vị trí của chúng. Đôi khi người lập trình phải vận dụng các kiến thức về C để phát 
hiện các sai sót đã gây ra thông báo lỗi. 
Câu hỏi và trả lời 
1. Nếu muốn cho một người nào đó một chương trình C thì phải cho tệp nào? 
C là một ngôn ngữ có chương trình dịch. Điều đó có nghĩa là, sau khi mã gốc đã được 
dịch sẽ phát sinh một chương trình có thể chạy được. Chương trình này có thể tự chạy 
một mình. Nếu muốn chuyển CHAO đến những người nào đó, điều duy nhất cần làm là 
hãy chuyển cho họ tệp CHAO.EXE. Họ không cần tệp gốc CHAO.C, hoặc tệp đích 
CHAO.OBJ và thậm chí ngay cả chương trình dịch C nữa. 
2. Sau khi đã tạo được tệp có thể chạy được, thì có cần phải giữ tệp gốc hoặc tệp 
đích nữa không? 
Nếu tệp gốc bị mất thì sau này không còn cách nào khác để có thể thay đổi được 
chương trình đã dịch và kết nối. Do vậy nên giữ lại các tệp gốc. Nhưng đối với các tệp 
đích thì lại khác. Có một vài lý do cần phải giữ lại các tệp đích, tuy nhiên nếu nói ngay 
bây giờ thì còn hơi sớm. Do vậy, có thể xóa tệp đích một khi đã có tệp có thể chạy 
được. Nếu cần tệp đích, có thể dịch lại từ tệp gốc. 
3. Liệu có thể bỏ qua các thông báo nhắc nhở không? 
Một số thông báo nhắc nhở không làm ảnh hưởng đến việc chạy của chương trình. Một 
số khác lại có. Nếu chương trình dịch cho ra một thông báo nhắc nhở, thì đó là một dấu 
hiệu báo rằng có một cái gì đó không hoàn toàn đúng. Hầu hết các chương trình dịch 
đều cho phép đặt mức nhắc nhở. Bằng cách dó thì chỉ nhận được các nhắc nhở từ mức 
này trở đi. Trong chương trình, cần phải xem từng nhắc nhở một và quyết định nên khắc 
phục nó hay là không. Tốt nhất là nên sửa lại mã gốc sao cho không còn một thông báo 
lỗi và nhắc nhở nào. 
Luyện lập 
Mục Luyện tập sẽ cho các câu hỏi để giúp người đọc củng cố lại kiến thức của mình và các 
bài tập nhằm cung cấp các kinh nghiệm khi sử dụng những điều đã học. 
Câu hỏi 
1. Hãy nêu ba lý do tại sao C là một lựa chọn tốt nhất về ngôn ngữ lập trình. 
2. Chương trình dịch làm công việc gì? 
3. Các bước trong chu trình phát triển chương trình là gì? 
4. Cần phải gõ lệnh nào để dịch một chương trình có tên PROG1.C bằng chương trình 
dịch C ? 
5. Chương trình dịch C kết nối và dịch bằng một lệnh hay bằng hai lệnh riêng rẽ? 
6. Nên dùng đuôi nào cho các tệp gốc của C? 
7. FILENAME.TXT có hợp lệ cho một tệp gốc của C không? 
8. Nếu một chương trình đã được dịch và nó không làm việc như mong muốn, thì nên làm 
như thế nào ? 
9. Mã máy là gì? 
10. Bộ kết nối làm công việc gì? 
Bài tập 
1. Hãy nhập và dịch chương trình sau đây. Chương trình này làm việc gì? (không viết các 
số hiệu dòng khi soạn chương trình) 
1: include 
2: int bankinh, dientich; 
3: main() 
4:  
5: printf( "Nhập bán kính: " ); 
6: scanf( "%d", &bankinh ); 
7: dientich = 3.14159 * bankinh * bankinh; 
8: printf( "\n\nDiện tích = %d", dientich ); 
9: return 0; 
10:  
2. Nhập và dịch chương trình sau. Chương trình này làm việc gì? 
1: #include 
2: int x,y; 
3: main() 
4:  
5: for ( x = 0; x < 10; x++, printf( "\n" ) ) 
6: for ( y = 0; y < 10; y++ ) 
7: printf( "X" ); 
8: return 0; 
9:  
3. Chương trình sau đây có một lỗi. Hãy nhập và dịch nó. Các dòng nào sinh ra 
 thông báo lỗi? 
1: #include 
2: main(); 
3:  
4: printf( "Hãy nhìn xem!" ) 
5: printf( "Bạn sẽ tìm thấy nó!" ); 
6: return 0; 
7:  
4. Chương trình sau đây cũng có lỗi. Hãy nhập và dịch nó. Các dòng nào sinh 
 ra thông báo lỗi? 
1: #include 
2: main() 
3:  
4: printf( "Đây là một chương trình" ) 
5: do_it( "có lỗi!" ); 
6: return 0; 
7:  
5. Thay lệnh số 7 trong chương trình ở bài tập 2 như dưới đây. Dịch và chạy lại 
 chương trình. Bây giờ chương trình này làm gì? 
 7: printf( "%c", 1 ); 
Bài 2. Các thành phần của một chương trình 
C 
Mọi chương trình C đều chứa một số các thành phần được liên kết với nhau theo một cách 
nhất định. Để có một bức tranh tổng thể, trước hết, ta bắt đầu xét một chương trình C ngắn 
nhưng hoàn chỉnh với tất cả các thành phần của nó. 
Một chương trình C ngắn 
Chương trình 2.1 là mã gốc của một chương trình có tên là NHAN.C. Đây là một chương 
trình rất đơn giản: nó chỉ bao gồm việc nhập hai số từ bàn phím và tính tích của chúng. Ở 
đây, chúng ta chưa cần phải hiểu tất cả các lệnh của chương trình, mà chủ yếu là làm quen 
với các thành phần của một chương trình C mà thôi. 
Trước khi xét chi tiết chương trình này, cần phải hiểu rõ khái niệm về hàm (function), vì hàm 
là một yếu tố cơ bản trong việc lập trình C. Một hàm là một bộ phận độc lập của chương 
trình, được đặt tên và thực hiện một công việc nào đó. Bằng việc gọi tên một hàm, chương 
trình chính có thể thực hiện các lệnh trong hàm này. Chương trình chính có thể gửi các thông 
tin, được gọi là đối số (argument) cho hàm, và hàm có thể đưa trở lại thông tin cho chương 
trình. Có hai kiểu hàm: các hàm thư viện - một bộ phận của chương trình dịch của C; và 
các hàm do người sử dụng tự định nghĩa. 
Chương trình 2.1. NHAN.C. 
1: /* Chương trình tính tích của hai số. */ 
2: #include 
3: int a,b,c; 
4: int tich(int x, int y); 
5: main() 
6:  
7: /* Nhập số thứ nhất */ 
8: printf("Nhập một số nằm giữa 1 và 100: "); 
9: scanf("%d", &a); 
10 
11: /* Nhập số thứ hai */ 
12: printf("Nhập một số khác nằm giữa 1 và 100: "); 
13: scanf("%d", &b); 
14: 
15: /* Tính và hiện kết qủa */ 
16: c = tich(a,b); 
17: printf( "\n%d nhân %d = %d", a, b, c); 
18:  
19: 
20: /* Hàm tính tích của hai đối số */ 
21: int tich(int x, int y) 
22:  
23: return ( x * y ); 
24:  
Chương trình này sẽ hiện trên màn hình các dòng kết qủa sau: 
 Nhập một số nằm giữa 1 và 100: 35 
 Nhập một số khác nằm giữa 1 và 100: 23 
 35 nhân 23 = 805 
Các thành phần của chương trình 
Sau đây là mô tả của các thành phần khác nhau của chương trình mẫu nói trên. Các số hiệu 
dòng không phải là thành phần của chương trình mà chỉ được đưa vào để tiện việc theo dõi 
và phân tích. 
Hàm main() (Các dòng 5-18) 
Bộ phận duy nhất cần phải có trong mọi chương trình C là hàm main(). Dạng đơn giản 
nhất của nó là: main theo sau là một cặp dấu ngoặc tròn ( ) và một cặp dấu ngoặc nhọn . 
Nằm giữa các dấu ngoặc nhọn là các lệnh tạo nên thân chính của chương trình. Dưới điều 
kiện bình thường, chương trình sẽ bắt đầu thực hiện từ lệnh thứ nhất trong main() và kết 
thúc bằng lệnh cuối cùng trong main(). 
Chỉ thị #include (Dòng 2) 
Chỉ thị #include báo cho chương trình dịch của C biết phải thêm vào chương trình một tệp 
trong khi dịch. Một tệp được thêm vào là một tệp đĩa riêng biệt chứa các thông tin cần thiết 
cho chương trình dịch. Một số tệp trong các tệp này (đôi khi còn gọi là các tệp tiêu đề - 
header files) được cung cấp cùng với chương trình dịch. Tất cả các tệp này đều có đuôi là 
.H. 
Chỉ thị #include trong ví dụ này có nghĩa là "Cộng thêm nội dung của tệp stdio.h". Hầu hết 
các chương trình C đều yêu cầu một hoặc nhiều tệp cộng thêm. 
Định nghĩa biến 
Một biến là một tên gọi cho một đại lượng hoặc một đối tượng nào đó. Mỗi biến đều được 
phân bổ một địa chỉ trong bộ nhớ. Chương trình C sử dụng các biến để lưu giữ các loại dữ 
liệu khác nhau trong quá trình thực hiện chương trình. Trong C, một biến chỉ có thể được sử 
dụng sau khi đã được định nghĩa. Một định nghĩa biến cho chương trình dịch biết tên của 
biến và kiểu của dữ liệu mà nó lưu giữ. Trong ví dụ mẫu, định nghĩa ở dòng 3, int 
a,b,c, định nghĩa ba biến có tên là a, b, và c và mỗi biến sẽ chứa một giá trị nguyên 
(integer). Trong Bài 3 sẽ nói rõ hơn về các biến và các định nghĩa biến. 
Khai báo hàm (Dòng 4) 
Một khai báo hàm cho chương trình dịch biết tên và đối số của hàm sẽ được sử dụng trong 
chương trình. Khai báo này phải xuất hiện trước khi hàm được sử dụng. Khai báo hàm khác 
với định nghĩa hàm. Một định nghĩa hàm chứa các lệnh tạo ra hàm đó. 
Các lệnh chương trình (Các dòng 8, 9, 12, 13, 16, 17, 23) 
Công việc thực sự của một chương trình C được thực hiện bởi các câu lệnh của nó. Các 
câu lệnh của C là: hiện các thông tin trên màn hình, đọc các phím được gõ vào từ bàn phím, 
thực hiện các phép số học, gọi các hàm, đọc các tệp đĩa, và tất cả các thao tác khác mà 
một chương trình cần phải thực hiện. Từ bài này trở đi, mỗi một lệnh C được viết riêng trên 
một dòng và luôn luôn được kết thúc bằng một dấu chấm phẩy (;). Sau đây là các giải thích 
ngắn gọn cho các câu lệnh của chương trình NHAN.C. 
printf() 
Lệnh printf() (các dòng 8, 12, và 17) là một hàm thư viện dùng để hiện thông tin lên màn 
hình. Lệnh này có thể dùng để hiện một thông báo văn bản đơn giản (như ở dòng 8 và 12) 
hoặc một thông báo và một hoặc nhiều biến chương trình (như ở dòng 17). 
scanf() 
Lệnh scanf() (các dòng 9 và 13) cũng là một hàm thư viện dùng để đọc dữ liệu từ bàn 
phím và gán dữ liệu này cho một hoặc nhiều biến chương trình. 
c = tich (a, b); 
Lệnh này gọi một hàm có tên là tich().Nghĩa là, nó thực hiện các lệnh chương trình chứa 
trong hàm tich(). Nó còn gửi các đối số a và b cho hàm. Sau khi các lệnh trong hàm 
tich() được hoàn tất, tich() còn đưa ra một giá trị cho chương trình. Giá trị này được 
gán và lưu giữ trong biến có tên là c. 
return (x * y); 
Lệnh này là một bộ phận của hàm tich(). Nó tính tích của các biến x và y rồi đưa kết 
qủa trở lại cho chương trình đã gọi tich(). 
Định nghĩa hàm (Các dòng 21-24) 
Một hàm là một đoạn mã gốc độc lập thực hiện một nhiệm vụ nhất định. Mỗi hàm đều có 
một tên, và các mã gốc của hàm này được thực hiện mỗi khi tên hàm được gọi trong một 
chương trình. 
Hàm có tên tich() ở các dòng 21-24 là một hàm do người sử dụng tự định nghĩa và tự 
viết trong quá trình phát triển chương trình. Hàm này rất đơn giản: nó chỉ làm mỗi một việc là 
nhân hai giá trị với nhau và đưa trở lại kết qủa cho chương trình đã gọi nó. 
C còn có các hàm thư viện được cung cấp cùng với chương trình dịch. Các hàm thư viện 
thực hiện hầu hết các công việc cơ bản nhất (như các thao tác trên màn hình, bàn phím, 
đọc/ghi đĩa) mà một chương trình cần đến. Trong ví dụ này, printf() và scanf() là 
các hàm thư viện. 
Các chú thích trong chương trình (Các dòng 1, 7, 11, 15, 20) 
Bất kỳ phần nào mà khởi đầu bằng /* và kết thúc bằng */ đều được gọi là một chú thích. 
Chương trình dịch bỏ qua tất cả các chú thích và như vậy thì chúng tuyệt đối không ảnh 
hưởng đến sự hoạt động của một chương trình. Có thể viết bất cứ thứ gì trong một chú thích 
và nó sẽ không ảnh hưởng đến hoạt động của chương trình. Một chú thích có thể chỉ chiếm 
một phần nào đó của một dòng, toàn bộ một dòng, hoặc cũng có thể nhiều dòng. Tuy nhiên, 
không nên lồng chú thích này trong một chú thích khác. Hầu hết các chương trình dịch đều 
không chấp nhận loại hình này. 
Nhiều người mới bắt đầu học lập trình cho rằng không cần đến chú thích và làm như vậy là 
mất thì giờ. Thực ra thì không hẳn là như vậy. Các thao tác trong chương trình có thể rất rõ 
ràng và dễ theo dõi trong khi đang viết. Nhưng khi chương trình trở nên dài hơn và phức tạp 
dần lên, hoặc khi cần phải sửa đổi một chương trình đã viết từ nhiều tháng trước thì các chú 
thích thật là có giá trị. Rõ ràng là cần phải tạo ra một thói quen dùng các chú thích để làm 
sáng tỏ tất cả các cấu trúc và các hoạt động trong chương trình. 
Các dấu ngoặc nhọn (Các dòng 6, 18, 22, 24) 
Các dấu ngoặc nhọn () dùng để nhóm các dòng lệnh thành một khối. Các lệnh của một 
hàm, kể cả hàm main(), phải được để trong các dấu ngoặc này. 
Chạy chương trình 
1. Chuyển vào thư mục chứa chương trình dịch C. 
2. Khởi động bộ soạn thảo. 
3. Nhập mã gốc của NHAN.C (lưu ý, không nhập các số hiệu dòng). 
4. Cất giữ tệp chương trình. 
5. Dịch và kết nối chương trình bằng cách ra các lệnh thích hợp với chương trình dịch hiện 
có. Nếu không có sai sót gì trong qúa trình dịch cũng như kết nối, chạy chương trình 
bằng lệnh run, hoặc bằng cách gõ nhan tại dấu nhắc của DOS. 
6. Nếu xuất hiện một hoặc nhiều thông báo lỗi, quay lại từ bước hai và sửa các lỗi đã mắc 
phải. 
Kết luận 
Bài này tuy ngắn nhưng rất quan trọng. Nó cho biết cấu trúc và các thành phần của một 
chương trình C. Bộ phận duy nhất mà một chương trình C yêu cầu là hàm main(). Công 
việc thực sự của chương trình được thực hiện bởi các lệnh chương trình. Các lệnh này 
hướng dẫn cho máy tính thực hiện các hành động mà chương trình mong muốn. Bài này còn 
nói về các biến và các định nghĩa biến, và chỉ ra lợi ích của việc sử dụng các chú thích và 
cách dùng chúng. 
Ngoài hàm main() ra, một chương trình C còn có thể sử dụng hai kiểu hàm khác: các hàm 
thư viện được cung cấp cùng với bộ chương trình dịch và các hàm do người sử dụng tự định 
nghĩa. 
Câu hỏi và trả lời 
1. Các chú thích có tác động gì trên một chương trình? 
Các chú thích là để cho người lập trình. Khi chương trình dịch chuyển các mã gốc thành 
các mã đích, nó sẽ vứt bỏ các chú thích và các vùng trắng. Điều đó có nghĩa là chúng 
không hề có một tác động nào lên chương trình có thể chạy được. Các chú thích có làm 
cho chương trình gốc dài hơn, nhưng điều đó cũng không đáng kể lắm. Về lâu dài, nên 
sử dụng các chú thích và các khoảng trống để chương trình gốc dễ đọc và dễ hiểu hơn. 
2. Sự khác nhau giữa một lệnh và một khối là gì? 
Một khối là một nhóm các lệnh được đặt giữa các dấu ngoặc nhọn. Một khối có thể 
được sử dụng ở hầu hết các vị trí mà tại đó có thể sử dụng một lệnh. 
3. Làm thế nào để tìm thấy danh sách các hàm thư viện có thể sử dụng? 
Nhiều chương trình dịch có kèm theo tài liệu mô tả các hàm thư viện. Chúng thường 
được xếp theo thứ tự từ điển. 
Luyện tập 
Câu hỏi 
1. Thuật ngữ nào dùng để chỉ một nhóm của một hoặc nhiều lệnh C được để giữa các dấu 
ngoặc nhọn? 
2. Thành phần nào phải luôn luôn có mặt trong một chương trình C? 
3. Làm thế nào để thêm vào các chú thích chương trình và tại sao phải dùng chúng? 
4. Một hàm là gì? 
5. Trong C có hai kiểu hàm. Các hiểu đó là kiểu gì và chúng khác nhau như thế nào? 
6. Chỉ thị #include dùng để làm gì? 
Bài tập 
I. Hãy viết một chương trình C ngắn nhất. 
II. Dùng chương trình sau đây để trả lời cho các câu hỏi sau: 
A. Các dòng nào chứa các lệnh? 
B. Các dòng nào chứa các định nghĩa biến? 
C. Các dòng nào chứa các khai báo hàm? 
D. Các dòng nào chứa các định nghĩa hàm? 
E. Các dòng nào chứa các chú thích? 
1: /* BT2-2.C */ 
2: #include 
3: void in_dong(void); 
4: main() 
5:  
6: in_dong(); 
7: printf("\n Tự dạy chương trình C trong 21 ngày!\n"; 
8: in_dong(); 
9: return 0 
10: /* In một dòng dấu sao */ 
11: void in_dong(void) 
12:  
13: int dem; 
14: for(dem = 0; dem = 21; dem ++) 
15: printf("*"); 
16:  
17: /* kết thúc chương trình */ 
III. Chương trình sau đây làm việc gì? 
1: /*BT2-3.C */ 
2: #include 
3: main() 
4:  
5: int ctr; 
6: for ( ctr=65; ctr,91; ctr++ ); 
7: printf( "%c", ctr ); 
8: return 0; 
9:  
10: /* kết thúc chương trình */ 
IV. Chương trình sau đây làm việc gì? 
1: /* BT2-4.C */ 
2: #include 
3: main() 
4:  
5: char buffer(256); 
6: printf( "Hãy viết tên của bạn và ấn :\n"; 
7: gets(buffer); 
8: printf( "\nTên của bạn có %d chữ cái và dấu cấch!", 
 strlen( bufferr); 
9: return 0; 
10:  
Bài 3. Các biến và các hằng 
Các chương trình máy tính thường làm việc với các kiểu dữ liệu khác nhau và cần một cách 
nào đó để lưu giữ các giá trị sẽ phải sử dụng. Các giá trị này có thể là các số hoặc các ký 
tự. C có hai cách để lưu giữ các giá trị số - biến (variable) và hằng (contstant) - với nhiều 
tuỳ chọn khác nhau cho mỗi cách. Một biến là một tên gọi và được phân bổ một địa chỉ lưu 
trữ. Địa chỉ này được dùng để lưu giữ giá trị của biến. Giá trị này có thể thay đổi trong khi 
chương trình thực hiện. Ngược lại, một hằng có một giá trị cố định không được thay đổi. 
Trước khi đi vào các biến, cần biết qua một chút về sự hoạt động của bộ nhớ máy tính. 
Bộ nhớ của máy tính 
Máy tính dùng một bộ nhớ truy nhập ngẫu nhiên (random access memory - RAM) để lưu 
giữ thông tin trong khi đang hoạt động. RAM được đặt trong các mạch tích hợp, hay còn 
gọi là chip, nằm bên trong máy tính. Các thông tin lưu trong RAM thường xuyên có thể bị 
xóa và được thay thế bằng các thông tin mới. Ngoài ra RAM chỉ nhớ thông tin khi máy đang 
chạy và các thông tin sẽ bị mất khi tắt máy. 
Một byte là đơn vị cơ bản của bộ nhớ dữ liệu của máy tính. Các kiểu dữ liệu khác nhau 
được lưu giữ bằng các số lượng byte khác nhau. Bảng 3.1. cho thấy sự khác nhau này. 
Bảng 3.1. Số lượng bytes cần thiết để lưu giữ dữ liệu 
 Dữ liệu Số bytes cần thiết 
 Chữ cái x 1 
 Số 100 2 
 Số 120.145 4 
 Cụm từ Tự học ngôn ngữ C 17 
 Một trang đánh máy 3000 (gần đúng) 
RAM trong máy tính được tổ chức theo kiểu tuần tự, byte này tiếp byte kia. Mỗi byte có 
một địa chỉ duy nhất để định danh cho nó và phân biệt nó với tất cả các byte khác trong bộ 
nhớ. Các địa chỉ được gán cho các vị trí bộ nhớ một cách tuần tự, khởi đầu từ 0 cho đến 
hết bộ nhớ của máy. 
Các biến 
Một biến là một tên gọi và được phân bổ một địa chỉ lưu trữ. Địa chỉ này được dùng để lưu 
giữ giá trị của biến. Giá trị này có thể thay đổi trong khi chương trình thực hiện. 
Các tên biến 
Trong C, các tên biến phải tuân theo các qui tắc sau: 
 Tên biến có thể chứa các chữ, các chữ số và dấu gạch dưới (_). 
 Ký tự đầu tiên của tên phải là một chữ. Có thể dùng dấu gạch dưới như là ký tự đầu 
tiên, nhưng không nên làm như vậy vì xem ra không được hợp lý cho lắm. 
 Chữ hoa và chữ thường là khác nhau. Tức là, Count và count là hai biến khác nhau. 
 Các từ giành riêng (keyword) của C không được sử dụng làm tên biến. Một keyword là 
một từ mà C sử dụng như là một thành phần của nó. 
Sau đây là một số ví dụ về các tên biến hợp lệ và không hợp lệ trong C: 
phantram /* hợp lệ */ 
y2x5_fg7h /* hợp lệ */ 
loinhuan_hangnam /* hợp lệ */ 
_1995_thue /* hợp lệ, nhưng không nên dùng */ 
Taikhoan#tk /* không hợp lệ, vì ký tự # không hợp lệ */ 
double /* không hợp lệ, vì là keyword của C */ 
9thang /* không hợp lệ, vì ký tự đầu tiên là số */ 
Vì C phân biệt chữ hoa với chữ thường, nên phantram, Phantram, và PHANTRAM là 
tên của ba biến khác nhau. Dù rằng không bắt buộc, nhưng các tên biến trong C thường 
được viết bằng chữ thường, còn tên của các hằng thì được viết bằng chữ hoa. 
Đối với nhiều chương trình dịch, một tên biến của C có thể dài tới 31 ký tự. Với độ dài như 
vậy, có thể tạo ra các tên biến nói lên được ý nghĩa của dữ liệu được lưu giữ trong chúng. 
Các tên có bao hàm ý nghĩa thường gồm nhiều từ. Có thể dùng dấu gạch dưới để phân cách 
các từ hoặc các cụm từ với nhau cho dễ hiểu. Còn một kiểu khác gọi là bướu lạc đà. Thay 
cho dấu cách, người ta viết hoa chữ đầu tiên của mỗi từ. Ví dụ, TyLeLaiSuat, hoặc 
TyleLaisuat. Kiểu này có vẻ thuận tiện hơn vì không phải gõ thêm dấu gạch dưới. 
Các kiểu biến số 
C cung cấp một số kiểu biến số khác nhau. Tại sao lại phải cần một số kiểu biến khác nhau? 
Đó là vì, các giá trị số khác nhau yêu cầu số bytes để lưu giữ khác nhau và các phép toán 
số học có thể thực hiện trên chúng cũng khác nhau. Các số nguyên nhỏ (ví dụ, 1, 19, -8) 
yêu cầu ít byte để lưu giữ, và các phép toán số học (cộng, nhân, v.v.) trên các số này có thể 
được thực hiện rất nhanh bằng máy tính. Ngược lại, những số nguyên lớn và các số dấu 
chấm động (ví dụ, 123000000 hoặc 0.000000345213) yêu cầu nhiều bytes hơn và thời gian 
tính toán với chúng cũng lâu hơn. Bằng cách dùng các kiểu biến số thích hợp, chương trình 
sẽ chạy một cách hiệu qủa hơn. 
Các biến số của C rơi vào hai nhóm chính sau đây: 
 Các biến nguyên (interger) dùng để lưu giữ các giá trị không có phần thập phân. Các 
biến nguyên lại phân thành hai lớp: biến nguyên có dấu để ghi các giá trị nguyên dương 
và nguyên âm; biến nguyên không dấu chỉ để ghi các giá trị không âm (bao gồm cả số 
0). 
 Các biến dấu chấm động (Floating-point) giữ các giá trị có phần thập phân (số thực). 
Lưu ý một điểm là, trước đây ta vẫn quen dùng dấu phẩy để ngăn cách phần nguyên và 
phần phân của một số, nên mới có tên gọi dấu phẩy động, nay theo chuẩn của ANSI 
thì người ta dùng dấu chấm thập phân, bởi vậy mới có tên gọi dấu chấm động. Từ nay 
trở đi sẽ dùng dấu chấm thập phân thay cho dấu phẩy thập phân. Còn dấu phẩy được 
dùng làm dấu ngăn cách hàng nghìn, hàng triệu, hàng tỷ, v.v. 
Bảng 3.2. Các kiểu dữ liệu số của C 
Kiểu biến Keyword Số bytes Miền giá trị 
ký tự char 1 -128 đến 127 
nguyên int 2 -32768 đến 32767 
nguyên ngắn short 2 -32768 đến 32767 
nguyên dài long 4 -2147483648 đến 2147483647 
ký tự không dấu unsigned char 1 0 đến 255 
nguyên không dấu unsigned int 2 0 đến 65535 
nguyên ngắn kh. dấu unsigned short 2 0 đến 65535 
nguyên dài kh. dấu unsigned long 4 0 đến 4294967295 
dấu chấm động float 4 1.2E-38 đến 3.4E38* 
độ chính xác đơn 
dấu chấm động float 8 2.2E-308 đến 1.8E308** 
độ chính xác gấp đôi 
*
 Miền gần đúng; độ chính xác= 7 chữ số. 
**
 Miền gần đúng; độ chính xác= 19 chữ số. 
Miền gần đúng trong bảng này có nghĩa là các giá trị cao nhất và thấp nhất mà một biến đã 
cho có thể nhận. Độ chính xác nghĩa là đúng đến bao nhiêu chữ số của giá trị được ghi trong 
biến đã cho. (Ví dụ, nếu tính 1/3, thì kết qủa đúng là 0.333333.... với các số 3 cứ tiếp tục 
đến vô hạn. Một biến với độ chính xác đơn chỉ ghi được 7 con 3.) 
Trong bảng này, các kiểu int và short int là giống nhau. Nhưng tại sao lại phải có hai 
kiểu khác nhau như vậy? Các kiểu biến int và short int là giống hệt nhau trên các hệ 
thống tương thích với các máy tính IBM-PC 16-bit, nhưng chúng có thể sẽ khác nhau trên 
các phần cứng kiểu khác. Trên một hệ VAX, một biến short và một biến int không 
cùng cỡ với nhau. Một biến short chiếm 2 bytes, nhưng một biến int lại chiếm những 4 
bytes. Do vậy, đối với các máy PC, hai kiểu này là như nhau. 
Các khai báo biến 
Mọi biến cần phải được khai báo trước khi được đem ra sử dụng. Một khai báo biến cho 
chương trình dịch biết tên và kiểu của một biến và có thể khởi tạo cho biến một giá trị nào 
đó. Một khai báo biến có dạng sau: 
tênkiểu tênbiến 
Tênkiểu xác định kiểu của biến và phải là một trong các keyword đã cho trong Bảng 3.2. 
Tênbiến là tên của biến được đặt theo các qui tắc đã nói ở phần trước. Có thể khai báo 
nhiều biến cùng một kiểu trên một dòng, mỗi biến cách nhau bằng một dấu phẩy. Ví dụ: 
int tuoi, luong, con; /* ba biến nguyên */ 
float phantram, tong; /* hai biên động */ 
Keyword typedef 
Từ khóa typedef được sử dụng để tạo lập một tên mới cho một kiểu dữ liệu đã có. Nói một 
cách chính xác, typedef tạo ra một kiểu đồng nghĩa. Ví dụ, lệnh 
typedef int integer; 
tạo ra kiểu integer từ kiểu int. Sau đó, có thể dùng integer để định nghĩa các biến 
kiểu int. 
Khởi tạo các biến số 
Khi một biến được khai báo, chương trình dịch sẽ dành ra một vùng nhớ cho biến này. Tuy 
nhiên, giá trị được nhớ tại vùng này chưa được xác định. Nó có thể là 0, hoặc có thể là một 
giá trị ngẫu nhiên nào đó còn lại sau khi vùng này được giải phóng. Bởi vậy, trước khi một 
biến được sử dụng, nó phải được khởi tạo bằng một giá trị xác định nào đó. Việc này có thể 
thực hiện hoàn toàn độc lập với việc khai báo biến bằng cách dùng một lệnh gán. Ví dụ: 
int count; /* Dành một vùng nhớ cho biến count */ 
count = 0; /* Ghi 0 vào count */ 
Có thể khởi tạo một biến ngay khi khai báo bằng cách viết dấu bằng và giá trị khởi tạo sau 
tên biến. Ví dụ 
int count = 0; 
double phantram = 0.01, tyle_thue = 20.5; 
Các hằng 
Giống như một biến, một hằng cũng là một địa chỉ trong bộ nhớ được sử dụng bởi một 
chương trình. Nhưng không giống như một biến, giá trị được ghi trong một hằng không được 
thay đổi trong quá trình thực hiện của chương trình. C có hai kiểu hằng, mỗi kiểu lại có các 
cách sử dụng khác nhau. 
Các hằng thực sự (literal constants) 
Một hằng literal là một giá trị được gõ trực tiếp vào chương trình gốc bất cứ khi nào nó 
được cần đến. Đây là hai thí dụ: 
int count = 20; 
float tyle_thue = 0.20; 
Các giá trị 20 và .20 là các hằng literal. Các lệnh trên lưu giữ các giá trị này vào các biến 
count và tyle_thue. 
Một hằng literal với dấu chấm thập phân là một hằng dấu chấm động và được chương trình 
dịch C xem như một số có độ chính xác gấp đôi. 
Một hằng không có dấu chấm thập phân được chương trình dịch biểu diễn như một số 
nguyên. Các hằng nguyên có thể được viết theo ba dạng dưới đây: 
 Một hằng khởi đầu bằng một chữ số khác 0 được xem là số nguyên cơ số 10 
(decimal). Một hằng nguyên hệ 10 có thể chứa các số 0-9 và một dấu trừ hoặc + ở 
đằng trước. 
 Một hằng khởi đầu bằng chữ số 0 được xem là một số nguyên cơ số 8 (octal). Các 
hằng hệ 8 có thể chứa các chữ số 0-7 và phía trước có thể có thêm dấu trừ hoặc cộng. 
 Một hằng khởi đầu bằng 0x hoặc 0X được xem là một hằng hệ cơ số 16 
(hexadecimal). Các hằng hệ 16 có thể chứa các chữ số 0-9 và các chữ A, B, C, D và 
F, và một dấu trừ hoặc cộng ở đằng trước. 
Các hằng ký hiệu (symbolic constants) 
Một hằng ký hiệu là một hằng được ký hiệu bởi một tên gọi (symbolic) nào đó. Giống như 
một hằng literal, giá trị của một hằng symbolic không thể thay đổi. Giá trị thực sự của hằng 
symbolic chỉ được nhập vào khi nó được định nghĩa. Việc sử dụng một hằng kiểu này y hệt 
như sử dụng một biến. 
Hằng ký hiệu có hai ưu điểm nổi bật so với hằng thực sự. Ví dụ, để tính chu vi và diện tích 
của một hình tròn khi biết bán kính, có thể viết như sau: 
chu_vi = 3.14 * (2 * ban_kinh); 
dien_tich = 3.14 * ban_kinh * ban_kinh; 
Tuy nhiên, nếu định nghĩa một hằng ký hiệu với tên là PI và giá trị là 3.14, thì đoạn chương 
trình trên có thể thay bằng: 
chu_vi = PI * (2 * ban_kinh); 
dien_tich = PI * ban_kinh * ban_kinh; 
Hai lệnh này trở nên sáng sủa và dễ hiểu hơn. Thay vì phải băn khoăn về giá trị 3.14 có 
nghĩa là gì, ở đây có thể hiểu ngay được PI là gì. 
Ưu điểm thứ hai của hằng ký hiệu là ở chỗ dễ thay đổi. Tiếp tục ví dụ trên, để cho kết qủa 
của chương trình chính xác hơn cần phải thay giá trị của PI là 3.14 bằng một giá trị khác với 
độ chính xác cao hơn, chẳng hạn 3.14159. Nếu đã chót dùng một hằng thực sự cho số PI, 
thì bây giờ phải duyệt lại tất cả chương trình để thay các giá trị 3.14 bằng 3.14159. Với một 
hằng ký hiệu, chỉ cần thay ở đúng một chỗ, nơi hằng này được định nghĩa. 
C có hai phương pháp dùng để định nghĩa một hằng ký hiệu: chỉ thị #define và từ khóa 
const. Chỉ thị #define là một trong những chỉ thị tiền xử lý sẽ được trình bầy trong các 
bài sau. Chỉ thị này được sử dụng như sau: 
#define TENHANG giatri 
Dòng này tạo ra một hằng có tên là TENHANG với giá trị là giatri. Trong đó 
giatri chính là một hằng số, còn TENHANG tuân theo các qui luật giống như của tên 
biến. Để cho dễ phân biệt giữa một hằng và một biến, ta nên viết các tên hằng bằng chữ 
hoa, còn tên biến viết bằng chữ thường. Trong ví dụ trước, để định nghĩa hằng ký hiệu PI, 
có thể viết như sau: 
 #define PI 3.14159 
Chú ý là, chỉ thị #define không kết thúc bằng dấu chấm phẩy (;).#define có thể được 
đặt ở bất cứ chỗ nào trong chương trình gốc, nhưng chỉ có hiệu lực cho phần mã gốc đứng 
sau nó mà thôi. Nói chung, người ta hay gom tất cả các #define lại với nhau và đặt 
chúng ở đầu tệp ngay trước hàm main(). 
Cách thứ hai để định nghĩa một hằng ký hiệu là dùng từ khóa const. Một biến được khai 
báo là const thì không thể thay đổi giá trị trong khi chương trình thực hiện. Đây là một vài 
ví dụ: 
const int count = 100; 
const float pi= 3.14159; 
const long no= 12000000, float tyle_thue = 0.21; 
const tác động lên tất cả các biến trên dòng khai báo. Trong dòng thứ ba, no và 
tyle_thue là các hằng ký hiệu. Nếu trong chương trình có một lệnh nào đó định thay đổi 
giá trị của một hằng, thì chương trình dịch sẽ sinh ra một thông báo lỗi. Ví dụ, 
const int count = 100; 
count = 200; /* Sai! Không thể gán lại hoặc thay đổi 
 giá trị của một hằng */ 
Các hằng ký hiệu được định nghĩa bằng chỉ thị #define và các hằng được định nghĩa bằng 
const có sự khác nhau. Sự khác nhau này có liên quan đến các con trỏ và phạm vi của 
các biến. Con trỏ và phạm vi của các biến là hai khái niệm quan trọng sẽ được trình bầy 
trong các bài sau. 
Kết luận 
Bài này đã trình bầy kỹ về các biến số dùng để lưu giữ các dữ liệu trong qúa trình thực hiện 
chương trình. Có hai lớp lớn các biến số: nguyên và dấu chấm động. Trong mỗi lớp lại có 
một số kiểu. Việc sử dụng kiểu nào - int, long, float, hoặc double - cho một ứng 
dụng là tuỳ thuộc vào bản chất của các dữ liệu được ghi trong biến. Một biến cần phải được 
khai báo trước khi được sử dụng. Một khai báo biến cho chương trình dịch biết tên và kiểu 
của biến. 
Bài này còn nói về hai kiểu hằng của C, hằng thực sự, và hằng ký hiệu. Không giống như 
các biến, giá trị của một hằng không được thay đổi trong quá trình thực hiện chương trình. 
Các hằng thực sự là những giá trị được gõ trực tiếp vào chương trình gốc bất cứ khi nào giá 
trị này được cần đến. Các hằng ký hiệu được đặt tên và tên này được sử dụng bất cứ khi 
nào giá trị của hằng được cần đến. Các hằng ký hiệu có thể được định nghĩa và khởi tạo 
bằng chỉ thị #define hoặc từ khóa const. 
Câu hỏi và trả lời 
1. Các biến long int chứa được các số nguyên lớn hơn, vậy tại sao không dùng 
chúng thay cho các biến int ? 
Một biến long int chiếm nhiều RAM hơn một biến int. Trong các chương trình nhỏ, 
điều này không ảnh hưởng lắm. Tuy nhiên, khi chương trình càng ngày càng lớn hơn, thì 
nên nghĩ đến việc sử dụng thật tiết kiệm bộ nhớ. 
2. Điều gì sẽ xảy ra, nếu gán một số thập phân cho một biến nguyên? 
Có thể gán một số thập phân cho một biến nguyên. Nếu biến nguyên này là một biến 
hằng thì chương trình dịch sẽ cho ra một thông báo nhắc nhở (warning). Giá trị được gán 
sẽ bị chặt đi phần thập phân. Ví dụ, nếu gán giá trị 3.14 cho một biến nguyên tên gọi là 
pi, thì pi sẽ chỉ chứa 3. Phần phân .14 bị bỏ đi. 
3. Điều gì sẽ xẩy ra, nếu gán một số vào một kiểu không đủ lớn để chứa nó? 
Nhiều chương trình dịch cho phép làm điều này mà không thông báo gì cả. Tuy nhiên, số 
này sẽ bị biến đổi để vừa với kiểu đã cho và đôi khi còn làm thay đổi hẳn giá trị của nó. 
Ví dụ, nếu gán 32768 cho một biến kiểu nguyên có dấu hai bytes, thì biến này sẽ nhận 
giá trị nguyên là -1. Nếu gán giá trị 65535 cho biến nguyên này, thì nó sẽ chứa giá trị 
-32768. Nguyên tắc biến đổi này là, lấy giá trị lớn nhất mà kiểu được gán có thể chứa 
được trừ đi giá trị được gán và chứa kết qủa vào biến này. 
4. Điều gì sẽ xảy ra, nếu gán một số âm cho một biến kiểu không dấu? 
Cũng giống như câu hỏi trước, chương trình dịch có thể sẽ không bảo gì cả nhưng kết 
qủa lại không đúng như mong muốn. Ví dụ, nếu gán -1 cho một biến nguyên không dấu 
hai bytes, thì chương trình dịch sẽ đặt vào biến này số cao nhất có thể (65535). 
Luyện lập 
Câu hỏi 
1. Có gì khác nhau giữa một biến nguyên và một biến dấu chấm động? 
2. Hãy cho biết hai lý do tại sao lại phải dùng một biến dấu chấm động độ chính xác gấp 
đôi (kiểu double) thay cho một biến dấu chấm động độ chính xác đơn (kiểu float.) 
3. Việc dùng một hằng ký hiệu thay cho một hằng thực sự có những ưu điểm nào? 
4. Hãy nêu hai phương pháp để định nghĩa một hằng ký hiệu có tên là MAXIMUM và có giá 
trị là 100. 
5. Các ký tự nào có thể được dùng để đặt tên các biến C? 
6. Sự khác nhau giữa một hằng ký hiệu và một hằng thực sự là gì? 
Bài tập 
I. Nên dùng kiểu biến nào để lưu giữ một cách tốt nhất cho các giá trị sau đây: 
A. Tuổi của một người trong những năm sắp tới. 
B. Trọng lượng của một người bằng kilogram. 
C. Bán kính của một đường tròn. 
D. Lương tháng của công chức. 
E. Giá của một mặt hàng nào đó. 
F. Nhiệt độ. 
G. Khoảng cách bằng kilomet từ trái đất đến một ngôi sao nào đó. 
II. Hãy tạo lập các tên biến thích hợp cho các giá trị trong bài tập một. 
III. Hãy viết các khai báo cho các biến trong bài tập hai. 
IV. Các biến nào là hợp lệ và không hợp lệ trong số các biến sau? Tại sao không 
hợp lệ? 
A. 123abc 
B. x 
C. tong_so 
D. trong_luong_#s 
E. mot 
F. tyle-phantram 
G. BAN_KINH 
H. Ban_kinh 
I. ban_kinh 
J. day_la_mot_bien_de_chua_chieu_rong_hop 
Bài 4. Câu lệnh, Biểu thức và Phép toán 
Các chương trình C chứa các câu lệnh (statement), và hầu hết các câu lệnh đều chứa các 
biểu thức (expression) và các phép toán (operator). Bài này bao gồm các mục sau: 
 Một câu lệnh là gì? 
 Một biểu thức là gì? 
 Các phép toán toán học, quan hệ, và logic. 
 Tiền toán tử là gì? 
 Câu lệnh if. 
Các câu lệnh 
Một câu lệnh là một phương hướng hành động hướng dẫn cho máy tính thực hiện một nhiệm 
vụ nào đó. Trong C, các câu lệnh thường được viết trên một dòng, nhưng cũng có những 
câu lệnh phải viết trên nhiều dòng. Mọi câu lệnh của C đều kết thúc bằng một dấu chấm 
phẩy (;) (trừ các chỉ thị, chẳng hạn như #include và #define). 
Ví dụ, 
x = 2+3; 
là một câu lệnh gán. Nó chỉ ra cho máy tính lấy số 2 cộng với số 3, rồi gán kết qủa vào biến 
x. 
Các câu lệnh và các khoảng trắng 
Thuật ngữ khoảng trắng (whitespace) là để chỉ các dấu cách, tabs và các dòng rỗng trong 
chương trình gốc. Chương trình dịch C không xét đến các khoảng trắng. Khi chương trình 
dịch đọc một câu lệnh trong một chương trình gốc, nó sẽ đọc tất cả các ký tự trong câu lệnh 
này cho đến khi gặp dấu chấm phẩy nhưng bỏ qua tất cả các khoảng trắng. Vì vậy, câu lệnh 
x=2+3; trên đây là hoàn toàn tương đương với 
x = 2 + 3; 
và với 
 x = 
2 
+ 
 3 ; 
Mỗi câu lệnh nên viết trên một dòng và nên có một dấu cách đứng trước và một dấu cách 
đứng sau mỗi tên biến. Trước và sau mỗi khối nên có một dòng trống để dễ nhận biết từng 
khối một. Ngoài ra, có thể dùng tabs để đẩy một khối thụt vào so với lề trái nhằm tạo ra một 
cấu trúc có tính phân cấp trong chương trình. 
Tuy nhiên, trong một xâu các ký tự (string constant) thì các dấu cách và các dấu tabs lại 
không bị bỏ qua, chúng được coi là một bộ phận của xâu ký tự này. Một xâu là một dãy 
các ký tự. Các hằng ký tự thực sự là các xâu được đặt trong các dấu nháy và được chương 
trình dịch xử lý từng ký tự một. 
Nếu chỉ có đúng một dấu chấm phẩy trên một dòng nào đó, thì dòng này được gọi là câu 
lệnh rỗng. Câu lệnh rỗng không thực hiện bất kỳ hành động nào. 
Các câu lệnh phức hợp 
Một câu lệnh phức hợp (compound statement), hay còn gọi là một khối (block), là một 
nhóm gồm hai hay nhiều câu lệnh của C được đặt trong các dấu ngoặc nhọn. Đây là một ví 
dụ về một khối: 
  
 printf("Xin chào, "); 
 printf(các bạn!"); 
  
Trong C, một câu lệnh phức hợp có thể được sử dụng ở bất cứ nơi nào mà một câu lệnh 
đơn có thể được sử dụng. Chú ý rằng, có thể có một vài cách để viết các dấu ngoặc. Câu 
lệnh phức hợp sau đây là tương đương với câu lệnh trên: 
 printf("Xin chào, "); 
 printf(các bạn!"); 
Tuy nhiên, việc để các dấu ngoặc trên hai dòng riêng biệt làm cho dễ nhận ra nơi bắt đầu và 
nơi kết thúc của một khối hơn. 
Các biểu thức 
Trong C, một biểu thức là một mệnh đề dùng để tính ra một giá trị nào đó. Các biểu thức C 
có thể có độ phức tạp tùy ý. 
Các biểu thức đơn giản 
Biểu thức đơn giản nhất của C là biểu thức chỉ có một biến, một hằng thực sự, hoặc một 
hằng ký hiệu. Đây là mấy ví dụ: 
PI /* một hằng ký hiệu (đã định nghĩa trong chương trình )*/ 
20 /* một hằng thực sự */ 
ty_le /* một biến */ 
-1.25 /* một hằng thực sự khác */ 
Các biểu thức phức tạp 
Các biểu thức phức tạp hơn bao gồm các biểu thức đơn giản hơn được liên kết với nhau 
bằng các phép toán. 
Ví dụ, 
2 + 10 
là một biểu thức gồm các biểu thức con 2 và 10, và phép toán +. Biểu thức 2+10 cho ra 
kết qủa là 12. Có thể viết các biểu thức phức tạp hơn: 
1.25 / 8 + 5 * ty_le + ty_le * ty_le / gia 
Khi một biểu thức chứa nhiều phép toán, thì việc tính toán biểu thức này phụ thuộc vào thứ 
tự thực hiện và độ ưu tiên của phép toán. 
Các biểu thức của C còn có rất nhiều điều thú vị. Hãy xét biểu thức sau: 
x = a + 10; 
Câu lệnh này tính biểu thức a + 10 và gán kết qủa cho x. Ngoài ra, toàn bộ câu lệnh này 
đến lượt nó cũng là một biểu thức tính ra giá trị của biến số nằm bên trái dấu bằng. Vì vậy 
có thể viết câu lệnh sau: 
y = x = a + 10; 
Câu lệnh này gán giá trị của biểu thức a + 10 cho cả hai biến x và y. Cũng có thể viết 
x = 6 + (y = 4 + 5); 
Kết qủa của câu lệnh này là y nhận giá trị 9 và x nhận giá trị 15. Hãy chú ý đến các dấu 
ngoặc đơn, chúng rất cần cho việc dịch lệnh này. 
Các phép toán 
Một phép toán là một ký hiệu báo cho C biết phải thực hiện một hoạt động, hoặc một thao 
tác nào đó trên một hoặc nhiều toán hạng (operand). Một toán hạng là một biểu thức. Các 
phép toán của C được chia thành một số nhóm. 
Phép gán 
Phép gán được ký hiệu là dấu bằng (=). Cách dùng của nó trong C có khác một chút so với 
cách dùng của nó trong toán học thông thường. Nếu viết 
x = y; 
trong một chương trình C, thì không có nghĩa là "x bằng y.", mà là "gán giá trị của y cho 
x." Trong một câu lệnh gán, vế phải có thể là một biểu thức bất kỳ còn vế trái phải là một 
tên biến. Vì vậy, dạng của câu lệnh gán là: 
tenbien = bieuthuc; 
Khi được thực hiện, bieuthuc được ước lượng và giá trị của kết qủa được gán cho 
tenbien. 
Các phép toán học 
C có hai phép toán học một toán hạng (đơn nguyên) và năm phép toán học hai toán hạng 
(nhị nguyên). 
Các phép toán học đơn nguyên 
Bảng 4.1. Các phép toán học đơn nguyên 
Phép toán Ký hiệu Hành động Ví dụ 
 Tăng ++ Tăng giá trị của toán hạng lên một ++x, x++ 
 Giảm -- Giảm giá trị của toán hạng đi một --x, x-- 
Các phép tăng và giảm chỉ được sử dụng với các biến, không được sử dụng với các hằng. 
++x; là tương đương với x = x + 1; 
--y; là tương đương với y = y - 1; 
Các phép toán đơn nguyên có hai kiểu: kiểu trước và kiểu sau. Hai kiểu này là không 
tương đương. Chúng khác nhau ở chỗ khi nào việc tăng hoặc giảm được thực hiện. 
 Khi được sử dụng ở kiểu trước, phép tăng và giảm sẽ thay đổi giá trị của toán 
hạng trước khi toán hạng được sử dụng. 
 Khi được sử dụng ở kiểu sau, phép tăng và giảm sẽ thay đổi giá trị của toán 
hạng sau khi toán hạng được sử dụng. 
Hãy xét hai câu lệnh sau đây thì sẽ thấy rõ điều đó: 
x = 10; 
y = x++; 
Sau khi các lệnh này được thực hiện, x có giá trị là 11, còn y có giá trị là 10 vì giá trị của x 
được gán cho y, và sau đó x mới được tăng thêm một. Ngược lại, các câu lệnh 
x = 10; 
y = ++x; 
cho x và y cùng một giá trị là 11 vì x được tăng thêm một, sau đó giá trị của nó mới được 
gán cho y. 
Chương trình sau minh họa sự khác nhau giữa kiểu trước và kiểu sau. 
Chương trình 4.1. MOT.C 
1: /*Minh hoạ kiểu trước và kiểu sau của phép toán một toán hạng */ 
2: 
3: #include 
4: 
5: int a , b; 
6: 
7: main() 
8:  
9: /* Đặt cả a và b bằng 5 */ 
10: 
11: a = b =5; 
12: 
13: /* Sau đó in chúng và giảm đi một */ 
14: /* dùng kiểu trước cho b và kiểu sau cho a */ 
15: 
16: printf("\n%d %d", a--, --b); 
17: printf("\n%d %d", a--, --b); 
18: printf("\n%d %d", a--, --b); 
19: printf("\n%d %d", a--, --b); 
20: printf("\n%d %d", a--, --b); 
21: 
22: return 0; 
23:  
Kết qủa của chương trình là 
 5 4 
 4 3 
 3 2 
 2 1 
 1 0 
Các phép toán học nhị nguyên 
Bảng 4.2. là danh sách các phép toán học nhị nguyên. Bốn phép đầu rất quen thuộc. Phép 
thứ năm, modulus (lấy phần dư của phép chia), có thể là còn mới. Modulus cho ra phần dư 
của phép chia toán hạng thứ nhất cho toán hạng thứ hai. Ví dụ, 11 modulus 4 bằng 3 (tức là, 
11 chia cho 4 được 2 dư 3), 10 modulus 5 bằng 0 (10 chia cho 5 được 2 dư 0, hay nói 
cách khác, 10 chia hết cho 5). 
Bảng 4.2. Các phép toán học nhị nguyên 
Phép toán Ký hiệu Hành động Ví dụ 
 Cộng + Cộng hai toán hạng x + y 
 Trừ - Toán hạng thứ nhất trừ toán hạng thứ hai x - y 
 Nhân * Nhân hai toán hạng của nó x * y 
 Chia / Toán hạng thứ nhất chia toán hạng thứ hai x / y 
 Modulus % Lấy phần dư của phép chia x % y 
Thứ tự thực hiện các phép toán và các dấu ngoặc 
Trong một biểu thức có chứa từ hai phép toán trở lên thì thứ tự để thực hiện chúng như thế 
nào? Tầm quan trọng của câu hỏi này có thể được minh họa bằng câu lệnh gán sau đây: 
x = 4 + 5 * 3; 
Nếu phép cộng được thực hiện trước, thì 
x = 9 *3; 
và x được gán giá trị 27. Ngược lại, nếu phép nhân được thực hiện trước, thì lại khác: 
x = 4 + 15; 
và x được gán giá trị 19. Như vậy là cần phải có các luật nào đó về thứ tự thực hiện của 
các phép toán trong một biểu thức. Thứ tự này được gọi là quyền ưu tiên của các phép 
toán. Mỗi phép toán đều có một quyền ưu tiên xác định. Khi một biểu thức được tính toán, 
các phép toán có quyền ưu tiên cao hơn sẽ được thực hiện trước. 
Thứ tự thực hiện của các phép toán là: 
 Tăng và giảm đơn nguyên. 
 Nhân, chia, và modulus. 
 Cộng và trừ 
Nếu một biểu thức chứa từ hai phép toán trở lên có cùng một hạng ưu tiên, thì chúng được 
thực hiện theo thứ tự từ trái sang phải. 
Trở lại ví dụ trước, câu lệnh x = 4 + 5 *3; gán giá trị 19 cho x bởi vì phép nhân được 
thực hiện trước phép cộng. 
Vẫn ví dụ này, nếu muốn cộng 4 với 5 sau đó mới nhân với 3 thì phải làm như thế nào? C 
dùng dấu ngoặc để điều chỉnh lại thứ tự thực hiện phép toán. Một biểu thức con được đóng 
trong các dấu ngoặc được tính trước cho dù nó chứa phép toán nào. Vì vậy, để thực hiện ý 
định trên, có thể viết: 
x = (4 + 5) * 3; 
Biểu thức con 4 + 5 trong các dấu ngoặc đơn được tính trước và vì thế giá trị được gán 
cho x là 27. 
Có thể dùng nhiều cặp dấu ngoặc lồng nhau trong một biểu thức. Khi các dấu ngoặc lồng 
nhau, việc tính toán được thực hiện từ biểu thức nằm trong cùng, sau đó tiến dần ra ngoài. 
Hãy xét biểu thức: 
x = 25 - (2 * (10 + (8 / 2)) 
Biểu thức này được xử lý theo các bước sau: 
1. Biểu thức trong nhất, 8 / 2, được tính trước nhất và cho ra giá trị 4. 
25 - (2 * (10 + 4)) 
2. Chuyển ra ngoài, biểu thức tiếp theo, 10 + 4, được tính và cho ra giá trị 14. 
25 - (2 * 14) 
3. Biểu thức ngoài cùng, 2 * 14, được tính và cho giá trị 28. 
25 - 28 
4. Biểu thức cuối cùng, 25 - 28, được tính và gán giá trị -3 cho biến x. 
x = -3 
Thứ tự tính toán các biểu thức con 
Như đã nói ở trên, nếu một biểu thức C chứa nhiều hơn một phép toán có cùng hạng ưu tiên 
thì chúng được thực hiện theo thứ tự từ trái qua phải. Ví dụ, trong biểu thức 
w * x / y * z 
w được nhân với x trước, chia kết qủa thu được cho y, và cuối cùng mới nhân kết qủa của 
phép chia này với z. 
Tuy nhiên với các mức ưu tiên khác nhau thì không có gì để đảm bảo thứ tự từ_trái_qua_ 
phải. Xét biểu thức sau: 
w * x / y + z / y 
Theo thứ tự ưu tiên, phép nhân và chia được thực hiện trước phép cộng. Tuy nhiên C không 
xác định biểu thức con w * x / y được tính trước hay tính sau biểu thức con z / y. Hãy xem 
một ví dụ khác: 
w * x / ++y + z / y 
Nếu biểu thức con thứ nhất được tính trước, thì y được tăng thêm 1 khi biểu thức con thứ 
hai được tính. Nếu biểu thức con thứ hai được tính trước, thì y chưa được tăng và kết qủa 
sẽ khác nhau. Do vậy, nên tránh viết các biểu thức kiểu này trong chương trình. 
Các phép quan hệ 
Các phép quan hệ của C được sử dụng để so sánh các biểu thức, chúng đặt ra các câu hỏi, 
ví như, "x có lớn hơn 100 không?", hoặc "y có bằng 0 không ?". Một biểu thức chứa một 
phép quan hệ chỉ có một trong hai giá trị, đúng - true (1) hoặc sai - false (0). Sáu phép quan 
hệ của C được cho trong Bảng 4.4. 
Bảng 4.4. Các phép quan hệ của C 
Phép toán Ký hiệu Câu hỏi được đặt ra Ví dụ 
Bằng == Toán hạng 1 có bằng toán hạng 2 không? x == y 
Lớn hơn > Toán hạng 1 có lớn hơn toán hạng 2 không? x > y 
Nhỏ hơn < Toán hạng 1 có nhỏ hơn toán hạng 2 không? x < y 
Lớn hơn >= Toán hạng 1 có lớn hơn hoặc bằng toán x>=y 
hoặc bằng hạng 2 không? 
Nhỏ hơn <= Toán hạng 1 có nhỏ hơn hoặc bằng toán hạng x<=y 
hoặc bằng 2 không? 
Không bằng != Toán hạng 1 không bằng toán hạng 2 x != y 
 phải không? 
Lưu ý: Khi làm việc với các phép quan hệ, đúng (true) là 1 còn sai (false) là 0. Đừng nhầm 
lẫn phép quan hệ == với phép gán =. Đây là một trong những lỗi mà người lập trình C 
hay mắc phải. 
Câu lệnh if 
Các phép quan hệ được sử dụng chủ yếu để xây dựng các biểu thức quan hệ dùng trong 
các câu lệnh if và while. Các câu lệnh trong một chương trình C thường được thực 
hiện từ trên xuống dưới theo đúng thứ tự xuất hiện của chúng trong chương trình gốc. Một 
câu lệnh điều khiển có thể sẽ làm thay đổi thứ tự thực hiện này. Các câu lệnh điều khiển 
chương trình có thể làm cho các câu lệnh khác thực hiện nhiều lần hoặc không thực hiện lần 
nào phụ thuộc vào từng hoàn cảnh cụ thể. Câu lệnh if là một trong các câu lệnh điều khiển 
chương trình của C. Ngoài ra còn có các câu lệnh khác, như for, do ... while, và 
while. 
Ở dạng thông thường, câu lệnh if ước lượng một biểu thức và thay đổi sự thực hiện 
chương trình theo kết qủa của việc ước lượng này. Dạng của if như sau: 
if (biểu thức) 
câu_lệnh; 
Nếu biểu thức được đánh giá là đúng thì câu_lệnh được thực hiện. Nếu biểu thức nhận 
giá trị sai, thì câu_lệnh không được thực hiện và điều khiển chuyển đến câu lệnh đứng 
ngay sau câu lệnh if. 
Chú ý là cả hai dòng if (biểu thức) và câu_lệnh; mới hợp thành một câu lệnh 
if hoàn chỉnh và chúng không phải là hai câu lệnh tách rời. Do vậy, không được viết dấu 
chấm phẩy ở cuối dòng if. 
Một câu lệnh if có thể điều khiển việc thực hiện của nhiều câu lệnh khác bằng cách dùng 
một khối. Vì vậy, có thể viết một câu lệnh if như sau: 
if (biểu_thức) 
 
 câu_lệnh1; 
 câu_lệnh2; 
 /* có thể viết thêm nhiều câu lệnh nữa tại đây */ 
 câu_lệnhn; 
 
Trong khi lập trình, câu lệnh if rất hay được sử dụng cùng với các biểu thức quan hệ, hay 
nói một cách khác, "hãy thực hiện (các) câu lệnh sau đây chỉ khi điều kiện như thế này 
là đúng". Ví dụ, 
if ( x > y ) 
 y = x; 
Câu lệnh này gán giá trị của x cho y chỉ khi x lớn hơn y. Nếu x không lớn hơn y, thì không có 
phép gán nào xẩy ra cả. 
Một câu lệnh if có thể chứa một mệnh đề else (ngược lại). Mệnh đề này được đưa vào if 
như sau: 
if (biểu_thức) 
 câu_lệnh1; 
else 
 câu_lệnh2; 
Nếu biểu_thức là đúng, câu_lệnh1 được thực hiện. Nếu biểu_thức là sai, 
câu_lệnh2 được thực hiện. Cả câu_lệnh1 và câu_lệnh2 đều có thể là một khối. 
Cú pháp của câu lệnh if 
Dạng 1 
if ( biểu_thức ) 
 câu _lệnh1 
câu_lệnh_tiếp_theo 
Đây là dạng đơn giản nhất của câu lệnh if. Nếu biểu_thức là đúng, thì câu_lệnh1 
được thực hiện. Nếu biểu_thức là không đúng, thì câu_lệnh1 bị bỏ qua. 
Dạng 2 
if ( biểu_thức ) 
 câu _lệnh1 
else 
 câu _lệnh2 
câu_lệnh_tiếp_theo 
Đây là dạng chung nhất của câu lệnh if. Nếu biểu_thức mà đúng thì câu_lệnh1 được 
thực hiện, ngược lại câu_lệnh2 được thực hiện. 
Dạng 3 
if (biểu_thức1) 
 câu _lệnh1 
else if(biểu_thức2) 
 câu _lệnh2 
 else 
 câu_lệnh3 
 câu_lệnh_tiếp_theo 
Đây là dạng if lồng nhau. Nếu biểu_thức1 là đúng, thì câu_lệnh1 được thực hiện, 
ngược lại, biểu_thức2 được kiểm tra. Nếu biểu_thức1 là không đúng, và 
biểu_thức2 là đúng, thì câu_lệnh2 được thực hiện. Nếu cả hai cùng không đúng, 
câu_lệnh3 được thực hiện. Chỉ có một trong ba câu lệnh này được thực hiện mà thôi. 
Ví dụ 1 
if ( luong > 10,000,000 ) 
 thue = .50; 
else 
 thue = .25; 
Ví dụ 2 
if ( tuoi <15 ) 
 printf( "Thiếu niên"); 
else if( tuoi < 65 ) 
 printf("Người lớn"); 
 else 
 printf("Người già"); 
Tính giá trị các biểu thức quan hệ 
Các biểu thức quan hệ cho ra một giá tri hoặc sai (0) hoặc đúng (1). Dù rằng các biểu thức 
quan hệ được dùng phổ biến nhất trong các câu lệnh if và các cấu trúc điều kiện khác, 
chúng còn được sử dụng như các giá trị số thuần tuý. Chương trình 4.5 sau đây minh họa 
cho cách dùng này. 
Chương trình 4.5. Trình diễn cách tính các biểu thức quan hệ 
1: /* Minh họa cách tính các biểu thức quan hệ */ 
2: 
3: #include 
4: 
5: int a; 
6: 
7: main() 
8:  
9: a = (5 == 5); /* cho ra giá trị 1 */ 
10: printf("\na = (5 == 5)\na = %d", a); 
11: 
12: a = (5 != 5); /* cho ra giá trị 0 */ 
13: printf("\na = (5 != 5)\na = %d", a); 
14: 
15: a = (12 == 12) + (5 != 1); /* cho ra 1 + 1 */ 
16: printf("\na = (12 == 12) + (5 != 1)\na = %d", a); 
17: return 0; 
18:  
Kết qủa của chương trình này là: 
 a = (5 == 5) 
 a = 1 
 a = (5 != 5) 
 a = 0 
 a = (12 == 12) + (5 != 1) 
 a = 2 
Xem ra kết qủa này mới đầu có vẻ hơi lạ. Khi dùng các biểu thức quan hệ, có một lỗi mà 
người ta hay mắc phải là chỉ dùng một dấu = cho phép quan hệ bằng. Biểu thức 
x = 5 
cho giá trị 5 đồng thời gán 5 cho x. Thế nhưng, biểu thức 
x == 5 
thì lại cho ra 0 hoặc 1 (phụ thuộc vào giá trị của x có bằng 5 hay không) và không làm thay 
đổi giá trị của x. Nếu do sơ xuất mà viết là 
if (x = 5) 
 printf("x bằng 5"); 
thì thông báo này luôn luôn được in ra vì biểu thức được kiểm tra ở câu lệnh if bao giờ 
cũng nhận giá trị đúng bất kể x có bằng 5 hay không. 
Cuối cùng, cần phải nhắc lại một lần nữa là, các phép quan hệ được dùng để tạo lập các 
mối quan hệ giữa các biểu thức. Một biểu thức quan hệ cho ra một giá trị số, bằng 0 (nếu 
biểu thức có nghĩa là sai) hoặc bằng 1 (nếu biểu thức có nghĩa là đúng). 
Thứ tự thực hiện của các phép quan hệ 
Giống như các phép toán học đã nói trong bài trước, các phép quan hệ cũng có các quyền 
ưu tiên riêng, quyết định thứ tự mà chúng được thực hiện trong một biểu thức có nhiều phép 
toán. Ngoài ra, cũng có thể dùng các dấu ngoặc để thay đổi thứ tự thực hiện trong một biểu 
thức có dùng các phép quan hệ. 
Trước hết, tất cả các phép quan hệ đều có quyền ưu tiên thấp hơn các phép toán học. Vì 
vậy, nếu viết 
if ( x + 2 > y) 
thì 2 được cộng với x trước, sau đó, kết qủa này mới được đem so sánh với y. Mệnh đề trên 
tương đương với 
if ( (x + 2 ) > y) 
trong đó, dấu ngoặc được sử dụng để làm rõ thêm thứ tự thực hiện của các phép trong 
biểu thức. 
Thứ tự ưu tiên của các phép quan hệ: 
 Phép toán Quyền ưu tiên tương đối 
 >= 1 
 != == 2 
Vì vậy, 
x == y > z; là tương đương với x == ( y > z ); 
bởi vì, đầu tiên C tính biểu thức y > z để cho ra một giá trị hoặc là 0 hoặc là 1. Tiếp theo, 
C xác định xem x có bằng giá trị thu được từ bước một hay không. 
Không nên dùng câu lệnh gán trong các câu lệnh if. Điều đó có thể làm cho người đọc 
chương trình dễ nhầm lẫn. Họ có thể hiểu nhầm là chương trình viết không đúng và sửa lại 
phép gán thành phép bằng logic. Đồng thời cũng không nên dùng phép "không bằng" (!=) 
trong một câu lệnh if có chứa một mệnh đề else. Tốt hơn cả là nên dùng phép "bằng" 
(==) cùng với else. Ví dụ, câu lệnh 
if (x != y ) 
 câu_lệnh1; 
else 
 câu_lệnh2; 
nên thay bằng câu lệnh sau thì dễ hiểu hơn 
if ( x == y ) 
 câu_lệnh2; 
else 
 câu_lệnh1; 
Các phép logic 
Đôi khi, cần phải đặt ra nhiều câu hỏi về các mối quan hệ của các biểu thức nào đó cùng 
một lúc. Các phép logic của C đảm bảo việc kết nối hai hay nhiều biểu thức quan hệ thành 
một biểu thức để tính ra được một giá trị hoặc là đúng hoặc là sai. Ba phép logic của C 
được cho trong Bảng 4.7. 
Bảng 4.7. Các phép logic của C 
 Phép toán Ký hiệu Ví dụ 
 và && biểu_thức1 && biểu_thức2 
 hoặc Ư Ư biểu_thức1 Ư Ư biểu_thức2 
 không ! ! biểu_thức1 
Phương thức làm việc của các phép logic được chỉ ra trong Bảng 4.8. 
Bảng 4.8. Phương thức làm việc của các phép logic 
 Biểu thức Cho ra giá trị 
 (biểu_thức1 && biểu_thức2) True (1) chỉ khi cả biểu_thức1 và 
 biểu_thức2 đều true; ngược lại false (0). 
 (biểu_thức1 Ư Ư biểu_thức2) True (1) nếu biểu_thức1 hoặc 
 biểu_thức2 là true; false (0) chỉ khi cả 
 hai đều false. 
 (! biểu_thức1) False (0) nếu biểu_thức1 là true; 
 true (1) nếu biểu_thức1 là false. 
Nói thêm về các giá trị True/False (Đúng/Sai) 
Các biểu thức quan hệ của C cho ra giá trị 0 để biểu hiện là sai và cho ra giá trị 1 để biểu 
hiện là đúng. Tuy nhiên, bất kỳ một giá trị số nào cũng có thể được biểu hiện như là đúng 
hoặc như là sai khi nó được sử dụng trong một biểu thức hoặc một câu lệnh đang chờ một 
giá trị logic (tức là, một giá trị đúng hoặc sai). Các qui tắc áp dụng cho các trường hợp này 
là: 
 Một giá trị bằng không được coi là sai. 
 Một giá trị khác không được coi là đúng. 
Các ví dụ sau đây minh họa cho các qui tắc này: 
x = 125; 
if (x) 
 printf ("%d", x); 
Trong trường hợp này, giá trị của x được in ra, vì x có giá trị khác không nên biểu thức (x) 
được câu lệnh if cho là đúng. Bởi vậy, đối với mọi biểu thức C thì cách viết 
(biểu_thức) là tương đương với cách viết (biểu_thức != 0) 
Cả hai đều cho giá trị đúng nếu biểu_thức khác không, và cả hai đều cho giá trị sai nếu 
biểu_thức bằng không. Bằng phép toán không (!), ta cũng có thể viết 
(! biểu_thức) hoặc tương đương là (biểu_thức == 0) 
Thứ tự thực hiện các phép logic 
Các phép logic cũng có một thứ tự ưu tiên ngay trong chúng và cả với các phép khác. Phép 
! (not) có cùng độ ưu tiên với các phép toán học một ngôi (đơn nguyên) ++ và -- Vì vậy, 
phép ! có thứ tự ưu tiên cao hơn tất cả các phép quan hệ và tất cả các phép toán học hai 
ngôi (nhị nguyên). 
Ngược lại, các phép && (and) và Ư Ư (or) có thứ tự ưu tiên thấp hơn nhiều, thấp hơn tất 
cả các phép toán học và các phép quan hệ. && có thứ tự ưu tiên cao hơn Ư Ư . Cũng như 
đối với các phép khác, dấu ngoặc có thể được sử dụng để thay đổi thứ tự thực hiện khi 
dùng các phép logic. 
Các phép gán phức hợp 
Các phép gán phức hợp (compound assignment) của C cung cấp một giải pháp ngắn gọn 
kết nối một phép toán học nhị nguyên với một phép gán. Ví dụ, giả sử muốn tăng giá trị của 
x lên 5, hay nói cách khác cộng 5 với x và lại gán kết qủa cho x thì có thể viết như sau: 
x = x + 5; 
Dùng một phép gán phức hợp, gần như một cách viết tắt của phép gán, có thể viết ngắn gọn 
như sau: 
x += 5; 
Tổng quát, phép gán phức hợp có dạng cú pháp như sau (trong đó op là một phép nhị 
nguyên): 
 biểu_thức1 op= biểu_thức2 
lệnh này tương đương với 
 biểu_thức1 = biểu_thức1 op biểu_thức2; 
Phép điều kiện 
Phép điều kiện là phép tam nguyên duy nhất của C, tức là nó có ba toán hạng. Cú pháp của 
nó là: 
Biểu_thức1 ? biểu_thức2 : biểu_thức3 
Nếu biểu_thức1 cho ra true (khác 0), thì biểu thức tổng thể này có giá trị của biểu_thức2. 
Nếu biểu_thức1 cho ra false (bằng 0), thì biểu thức tổng thể có giá trị của biểu_thức3. Ví 
dụ, câu lệnh 
x = y ? 1 : 100; 
gán 1 cho x, nếu y là true, hoặc gán 100 cho x, nếu y là false. Tương tự như vậy, để z lấy 
giá trị lớn hơn trong hai giá trị x và y, có thể viết 
z = (x > y) ? x : y; 
Phép điều kiện hơi giống với câu lệnh if. Câu lệnh trên có thể viết như sau: 
if ( x > y) 
 z = x; 
else 
 z = y; 
Phép dấu phẩy 
Dấu phẩy được sử dụng thường xuyên trong C để làm dấu ngăn cách, tách biệt các khai 
báo biến, các đối số của hàm, v.v. Trong một số trường hợp nhất định, dấu phẩy lại hành 
động như một phép toán chứ không còn là một dấu phân cách nữa. Có thể tạo ra một biểu 
thức bằng cách ghép hai biểu thức con bằng dấu phẩy. Kết qủa như sau: 
 Cả hai biểu thức được tính nhưng biểu thức bên trái được tính trước. 
 Biểu thức tổng thể nhận giá trị của biểu thức bên phải. 
Ví dụ, câu lệnh 
x = (a++ , b ++ ); 
tăng a, rồi tăng b, sau đó gán giá trị của b (chưa tăng) cho x. Vì phép ++ ở đây là kiểu sau, 
nên giá trị của b - trước khi được tăng - được gán cho x. Dùng các dấu ngoặc là cần thiết 
bởi vì phép dấu phẩy có thứ tự ưu tiên thấp, thậm chí thấp hơn cả phép gán. 
Kết luận 
Trong bài này, nhiều khái niệm quan trọng của C đã được trình bày: một câu lệnh C là gì, 
các khoảng trắng không có ý nghĩa đối với chương trình dịch của C, và các câu lệnh của C 
đều kết thúc bằng một dấu chấm phảy. Ngoài ra, chúng ta cũng đã học về một câu lệnh 
phức hợp hay còn gọi là một khối. Một khối chứa từ hai câu lệnh trở lên và được đóng 
khung trong các dấu ngoặc nhọn, có thể được sử dụng ở bất kỳ chỗ nào mà một câu lệnh 
đơn có thể được sử dụng. 
Nhiều câu lệnh được tạo thành từ một số biểu thức và các phép toán. Một biểu thức là một 
mệnh đề nào đó dùng để tính ra được một giá trị số. Các biểu thức phức hợp có thể chứa 
nhiều biểu thức đơn giản hơn (biểu thức con). 
Các phép toán là các ký hiệu của C, dùng để hướng dẫn máy tính thực hiện một thao tác 
trên một hoặc nhiều biểu thức. Một số phép toán là đơn nguyên, có nghĩa là chúng thao tác 
trên một toán hạng đơn. Tuy nhiên, hầu hết các phép của C đều là nhị nguyên, thao tác trên 
hai toán hạng. Có một phép tam nguyên, đó là phép điều kiện. Các phép toán của C có một 
quyền ưu tiên phân cấp quyết định thứ tự mà theo đó các phép toán sẽ được thực hiện 
trong một biểu thức có chứa nhiều phép toán. 
Các phép toán của C trong bài này được chia làm ba kiểu: 
 Các phép toán học thực hiện các phép số học trên các toán hạng của chúng. 
 Các phép quan hệ thực hiện việc so sánh giữa các toán hạng của chúng. 
 Các phép logic thao tác trên các biểu thức true/false. 0 để biểu diễn cho false, 1 để biểu 
diễn cho true. 
Câu hỏi và trả lời 
1. Các dấu cách và các dòng trắng có làm ảnh hưởng đến sự thực hiện của một 
chương trình không? 
Các khoảng trắng (dấu cách, dòng trắng, tabs) thường được dùng để làm chương trình 
dễ đọc hơn. Khi chương trình được dịch, các khoảng trắng bị bỏ qua, và vì thế chúng 
không có hiệu lực gì đối với chương trình đã dịch và kết nối. 
2. Sự khác nhau giữa các phép đơn nguyên và nhị nguyên là gì? 
Các phép đơn nguyên thao tác trên một biến còn phép nhị nguyên làm việc với hai biến. 
3. Phép trừ (-) là đơn nguyên hay nhị nguyên? 
Là cả hai. Chương trình dịch đủ thông minh để biết phải làm gì với phép này. Nó biết 
phải dùng kiểu nào căn cứ vào số biến trong biểu thức được sử dụng. Trong câu lệnh 
sau, nó là đơn nguyên 
x = -y; 
ngược lại, trong trường hợp sau là nhị nguyên 
x = a - b; 
4. Một số âm được coi là đúng(true) hay sai (false)? 
0 là false, và mọi giá trị khác là true. Vì vậy số âm là true. 
Luyện tập 
Câu hỏi 
1. Câu lệnh sau đây được gọi là gì, và ý nghĩa của nó ra sao? 
x = 5 + 8; 
2. Một biểu thức là gì? 
3. Trong một biểu thức có chứa nhiều phép toán, cái gì quyết định thứ tự thực hiện của 
chúng? 
4. Nếu một biến x có giá trị là 10, giá trị của x và a là bao nhiêu sau mỗi lần các câu lệnh 
sau đây được thực hiện tách biệt? 
a = x++; 
a = ++x; 
5. Biểu thức 10 % 3 cho giá trị bao nhiêu? 
6. Còn biểu thức 5 + 3 * 8 / 2 + 2 ? 
7. Hãy viết thêm các dấu ngoặc vào biểu thức trong câu 6 để biểu thức mới có giá trị bằng 
16. 
8. Nếu một biểu thức được đánh giá là false thì biểu thức này có giá trị là mấy? 
9. Phép toán nào có thứ tự ưu tiên cao hơn? 
a. == hay < 
b. * hay + 
c. != hay == 
d. >= hay > 
10. Phép gán phức hợp là gì và nó có ích lợi như thế nào? 
Bài tập 
1. Chương trình sau viết không được hay lắm. Hãy nhập và dịch xem nó có chạy không. 
#include 
int x, y; main()  printf( 
―\nNhập hai số‖); scanf( 
―%d %d, &x,&y);printf( 
―\n\n%d lớn hơn‖,(x>y)?x:y);return 0; 
2. Hãy viết lại chương trình trên cho dễ đọc hơn. 
3. Hãy viết lại chương trình 4.1 để đếm theo chiều tăng dần thay cho giảm dần. 
4. Viết một câu lệnh if để gán giá trị của x cho biến y chỉ khi x nằm giữa 1 và 20. Giữ 
nguyên y không đổi nếu x không nằm trong miền này. 
5. Dùng phép điều kiện để thực hiện nhiệm vụ của câu 4. 
6. Viết lại các câu lệnh if lồng nhau sau đây bằng một câu lệnh if đơn và các phép logic. 
if (x < 1) 
 if ( x >10) 
 câu_lệnh; 
7. Các biểu thức sau cho ra giá trị nào ? 
a. (1 + 2 * 3) 
b. 10 % 3 * 3 - (1 + 2) 
c. ((1 + 2) * 3) 
d. (5 == 5) 
e. (x = 5) 
8. Nếu x = 4, y = 6, và z = 2, hãy xem các mệnh đề sau đây là true hay false 
a. if (x == 4) 
b. if (x != y - z) 
c. if (z = 1) 
d. if (y) 
9. Viết một câu lệnh if để khẳng định một người là người lớn (21 tuổi) nhưng chưa già (65 
tuổi). 
10. Hãy sửa cho chương trình sau đây chạy đúng. 
 /* một chương trình có lỗi ... */ 
#include 
int x = 1; 
main() 
 
 if (x == 1); 
 printf ( ― x bằng 1‖); 
 otherwise 
 printf (― x không bằng 1‖); 
 return 
 
Bài 5. Các khái niệm về hàm 
Các hàm là bộ phận trung tâm của ngôn ngữ lập trình C và là cơ sở triết lý trong thiết kế 
chương trình C. Trong bài này, sẽ trình bầy những vấn đề sau đây: 
 Một hàm là gì và bao gồm những thành phần nào. 
 Ưu điểm của lập trình có cấu trúc bằng các hàm. 
 Tạo lập một hàm như thế nào. 
 Khai báo các biến cục bộ trong một hàm. 
 Đưa trở lại một giá trị từ một hàm về chương trình như thế nào. 
 Truyền các đối số cho một hàm như thế nào. 
Định nghĩa một hàm 
Một hàm là một đoạn chương trình gốc độc lập, có tên gọi, thực hiện một nhiệm vụ 
nhất định và có thể đưa trở lại một giá trị cho chương trình gọi nó. 
Một hàm có tên gọi: Mỗi hàm có một tên duy nhất. Bằng cách viết tên hàm vào một phần 
khác của chương trình là có thể thực hiện các câu lệnh chứa trong hàm này. Người ta gọi 
việc đó là gọi hàm. Một hàm có thể được gọi bởi một hàm khác. 
Một hàm là độc lập: Một hàm có thể thực hiện nhiệm vụ của nó một cách độc lập với các 
phần khác của chương trình. Mỗi hàm thực hiện một nhiệm vụ nhất định. Một nhiệm vụ là 
một công việc rời rạc mà chương trình phải thực hiện như là một công đoạn trong toàn bộ 
thao tác của nó, chẳng hạn như gửi một dòng văn bản ra máy in, sắp xếp một mảng theo thứ 
tự tăng dần, hoặc tính căn bậc hai của một số. 
Một hàm có thể đưa trở lại một giá trị cho chương trình gọi nó: Khi chương trình gọi 
một hàm, các câu lệnh của hàm này được thực hiện. Các câu lệnh này, nếu muốn, có thể 
truyền thông tin trở lại cho chương trình đã gọi nó. 
Ví dụ về hàm 
Chương trình 5.1. Chương trình sử dụng một hàm tính lập phương của một số 
1: /* Minh họa một hàm đơn giản */ 
2: #include 
3: 
4: long lap_phuong(long x); 
5: 
6: long so, ket_qua; 
7: 
8: main() 
9:  
10: printf(―Nhập một số nguyên: ―); 
11: scanf(―%d", &so); 
12: ket_qua = lap_phuong(so); 
13: /* Chú ý: %ld là format dùng cho các số nguyên */ 
14: /* kiểu long */ 
15: printf(―\n\nLập phương của %ld bằng %ld.‖, so,ket_qua); 
16:  
17: 
18: long lap_phuong(long x) 
19:  
20: long x_lap_phuong; 
21: 
22: x_lap_phuong = x * x * x; 
23: return x_lap_phuong; 
24:  
Dòng 4 chứa nguyên mẫu của hàm (function prototype), nó cho chương trình dịch biết 
trước rằng sẽ có một hàm có tên như vậy được viết trong chương trình này. Một nguyên 
mẫu hàm gồm tên của hàm, một danh sách các biến cần phải được truyền cho hàm, và kiểu 
của biến mà hàm sẽ đưa trở lại cho chương trình, nếu có. Tên của hàm là lap_phuong, nó 
cần một biến có kiểu long, và nó sẽ đưa trở lại cho chương trình một giá trị có kiểu long. 
Danh sách các biến cần được truyền cho hàm được gọi là các đối số (argument), chúng 
được đặt trong hai dấu ngoặc và đứng ngay sau tên hàm. Trong ví dụ này, đối số của hàm là 
long x. Từ giành riêng đứng trước tên hàm dùng để chỉ kiểu của biến mà hàm sẽ đưa trở 
lại. Trong ví dụ này, một giá trị của một biến kiểu long sẽ được đưa trở lại cho chương 
trình gọi hàm. 
Dòng 12 gọi hàm lap_phuong và truyền biến số làm đối số cho hàm. Giá trị quay trở lại 
của hàm này được gán cho biến ket_qua. Chú ý là, cả hai biến so và ket_qua đều 
được khai báo tại dòng 6 với kiểu long. 
Bản thân hàm thì được gọi là định nghĩa hàm. Trong ví dụ, hàm này được đặt tên là 
lap_phuong và được viết tại các dòng 18-24. Giống như nguyên mẫu, định nghĩa hàm 
cũng có một vài bộ phận. Hàm bắt đầu bằng một đầu hàm trên dòng 18, nó cho tên của 
hàm (lap_phuong), kiểu của biến quay trở lại của hàm và khai báo các đối số. Chú ý là, 
đầu hàm trùng với nguyên mẫu hàm, ngoại trừ dấu chấm phẩy. 
Thân hàm, các dòng 19-24, được đóng trong hai dấu ngoặc nhọn. Các câu lệnh chứa trong 
thân hàm sẽ được thực hiện khi hàm được gọi. Dòng 20 là một khai báo biến giống y như 
các khai báo biến khác trong chương trình, nhưng có một điều khác là nó chỉ có tính cục bộ 
(local). Các biến cục bộ là những biến được khai báo trong thân một hàm. Cuối cùng, hàm 
kết thúc bằng một câu lệnh return trên dòng 23. Câu lệnh này báo hiệu đã hết hàm và có 
thể truyền trở lại một giá trị cho chương trình gọi nó. Trong ví dụ, giá trị của biến 
x_lap_phuong được truyền trở lại. 
Cấu trúc của hàm lap_phuong cũng giống như của hàm main(). Các hàm khác được sử 
dụng trong chương trình là printf()và scanf(). Mặc dù các hàm này là hàm thư viện 
nhưng chúng cũng hoạt động y như một hàm do người sử dụng tạo lập, tức là chúng có thể 
lấy các đối số và đưa trở lại các giá trị cho chương trình gọi chúng. 
Cách thức làm việc của một hàm 
Một chương trình C không thực hiện các câu lệnh trong một hàm cho đến khi hàm này được 
gọi bởi một câu lệnh khác của chương trình. Khi một hàm được gọi thì chương trình có thể 
truyền cho hàm các thông tin dưới dạng một hoặc một vài đối số. Một đối số là một dữ liệu 
nào đó mà hàm cần có để thực hiện nhiệm vụ của nó. Sau đó, các câu lệnh của hàm được 
chạy để thực hiện những công việc đã định. Khi các câu lệnh này kết thúc, điều khiển được 
chuyển trở lại đúng vị trí mà tại đó đã gọi hàm. Các hàm có thể truyền thông tin trở lại cho 
chương trình dưới dạng một giá trị trở lại. 
Hình 5.1 chỉ ra một chương trình với ba hàm, mỗi hàm được gọi một lần. Mỗi khi một hàm 
được gọi, điều khiển chuyển vào hàm đó. Khi hàm kết thúc, điều khiển lại chuyển trở lại vị 
trí mà từ đó hàm được gọi. Một hàm có thể được gọi nhiều lần và theo một thứ tự tùy ý. 
Hình 5.1. Khi một chương trình gọi một hàm, điều khiển chuyển vào hàm và sau đó 
 quay trở lại chương trình gọi. 
Cú pháp của hàm 
Nguyên mẫu hàm 
kiểu_quay_lại tên_hàm(kiểu tên_1,..., kiểu tên_n); 
Định nghĩa hàm 
kiểu_quay_lại tên_hàm(kiểu tên_1,..., kiểu tên_n) 
 
 các câu lệnh; 
 
Nguyên mẫu hàm cung cấp cho chương trình dịch mô tả của một hàm sẽ được định nghĩa 
sau trong chương trình. Trong nguyên mẫu hàm có thể có kiểu của biến mà hàm sẽ đưa trở 
lại giá trị khi điều khiển quay trở lại chương trình. Tiếp theo là tên hàm. Nên đặt tên hàm sao 
cho tự bản thân tên này có thể mô tả được chức năng của nó. Sau tên hàm là một cặp dấu 
ngoặc đơn. Trong đó chứa kiểu của các tham số và có thể cả bản thân các tham số sẽ được 
truyền cho hàm (nếu hàm có tham số). Một nguyên mẫu hàm bao giờ cũng kết thúc bằng 
một dấu chấm phẩy. 
Một định nghĩa hàm chính là bản thân hàm đó. Nó chứa các câu lệnh sẽ được thực hiện. 
Dòng đầu tiên của một định nghĩa hàm được gọi là đầu hàm. Đầu hàm nên trùng khớp với 
nguyên mẫu nhưng không có dấu chấm phẩy ở cuối. Dù rằng tên các biến tham số có thể 
không xuất hiện trong nguyên mẫu, nhưng chúng phải có mặt trong đầu hàm. Sau đầu hàm là 
main() 
{ 
 gọi ham1() 
 . . . 
 gọi ham2() 
 . . . 
 gọi ham3() 
 . . . 
} 
ham1() 
{ 
 ... 
} 
ham2() 
{ 
 ... 
} 
ham3() 
{ 
 ... 
} 
thân hàm chứa các câu lệnh mà hàm sẽ thực hiện. Thân hàm phải mở đầu bằng một dấu 
ngoặc nhọn mở và kết thúc bằng một dấu ngoặc nhọn đóng. Nếu kiểu của biến quay trở lại 
khác void (kiểu void tức là hàm không đưa trở lại một giá trị nào cả) thì phải có một câu 
lệnh return để đưa trở lại một giá trị có kiểu của kiểu biến quay trở lại đã được khai báo 
trong nguyên mẫu và trong đầu hàm. 
Các hàm và lập trình có cấu trúc 
Sử dụng các hàm trong một chương trình chính là sử dụng kỹ thuật lập trình có cấu trúc, 
trong đó các nhiệm vụ riêng biệt của chương trình được thực hiện bởi từng bộ phận độc lập. 
Các hàm và lập trình có cấu trúc có quan hệ mật thiết với nhau. 
Những ưu điểm của lập trình có cấu trúc 
 Một chương trình có cấu trúc dễ viết hơn bởi vì các vấn đề của một chương trình 
phức tạp có thể được chia ra thành các nhiệm vụ nhỏ hơn, đơn giản hơn. Mỗi nhiệm 
vụ được thực hiện bởi một hàm, trong đó các câu lệnh và các biến được cô lập đối 
với phần còn lại của chương trình. 
 Dễ dàng sửa chữa một chương trình có cấu trúc. Nếu chương trình có lỗi, thì với 
một thiết kế có cấu trúc, có thể dễ dàng tìm ra lỗi và cô lập bộ phận nào của 
chương trình có thể sinh lỗi. 
 Tiết kiệm thời gian lập trình. Nếu viết một hàm để thực hiện một công việc nhất định 
nào đó trong một chương trình, thì có thể nhanh chóng và dễ dàng sử dụng nó trong 
một chương trình khác mà cũng cần phải thực hiện một công việc đúng như vậy. 
Ngay cả khi nếu trong chương trình mới, công việc này cần phải sửa đôi chút thì việc 
sửa lại một hàm đã được tạo lập vẫn còn nhanh hơn rất nhiều so với việc viết mới. 
Thiết kế một chương trình có cấu trúc 
Cách tiếp cận trên xuống (Top-Down) 
Bằng kỹ thuật lập trình có cấu trúc, người lập trình đã sử dụng cách tiếp cận từ trên xuống. 
Hình 5.2 mô tả cấu trúc của chương trình như một hình cây dựng ngược. Mỗi hàm là một 
nhánh con của cây. 
Hình 5.2. Một chương trình có cấu trúc được tổ chức theo dạng phân cấp. 
Nhiều chương trình C chỉ có rất ít câu lệnh tại hàm main(). Các công việc chính của 
chương trình phần lớn nằm trong các hàm. Trong main(), thường chỉ có một vài câu lệnh 
điều khiển việc thực hiện của các hàm. Thông thường, một thực đơn được hiện ra để người 
sử dụng chọn lựa một nhánh nào đó của chương trình. 
Viết một hàm 
main() 
Sửa Nhập Sắp xếp In 
Sửa Ghi Đọc 
Bước đầu tiên trong việc viết một hàm là phải xác định được hàm này làm gì. Một khi đã 
biết điều đó, thì việc viết hàm không còn khó khăn gì mấy nữa. 
Đầu hàm 
Dòng đầu tiên của một hàm là đầu hàm, với ba thành phần sau: 
kiểu_biến_quay_lại tên_hàm(tham_số_1, tham_số_2,. . .) 
Kiểu biến quay lại của hàm 
Kiểu biến quay lại của hàm xác định kiểu của dữ liệu mà hàm sẽ đưa trở lại cho chương trình 
gọi nó. Kiểu ở đây có thể là bất kỳ kiểu dữ liệu nào của C: char, int, long, 
float, hoặc double. Cũng có thể định nghĩa một hàm không đưa trở lại một giá trị nào 
cả, trong trường hợp này kiểu quay trở lại là void. 
Tên hàm 
Tên hàm có thể đặt là gì cũng được, nhưng phải tuân theo các qui tắc như đối với tên biến. 
Tên hàm phải duy nhất (tức là không được trùng với một hàm hoặc một biến khác). Ngoài 
ra, tên hàm nên đặt sao cho có thể nói lên được nhiệm vụ của nó. 
Danh sách tham số 
Có rất nhiều hàm sử dụng các đối số. Các đối số là các giá trị được truyền cho hàm khi nó 
được gọi. Một hàm cần phải biết loại đối số nào mà nó cần. Ta có thể truyền cho một hàm 
bất kỳ kiểu dữ liệu nào của C. Kiểu của đối số được cho trong danh sách tham số của đầu 
hàm. 
Trong danh sách tham số phải có mặt mọi đối số mà chương trình sẽ phải truyền cho hàm. 
Đối với mỗi đối số đều có hai thành phần: kiểu dữ liệu và tên của tham số. Nếu có nhiều đối 
số thì các đối số được đặt cách nhau bằng một dấu phẩy. Ví dụ, đầu hàm 
void ham1(int x, float y, char z) 
định nghĩa một hàm với ba đối số: một kiểu nguyên có tên là x, một kiểu float có tên y, 
và một kiểu char có tên z. Một số hàm không có đối số, trong trường hợp này danh sách 
tham số được đọc là void: 
void ham2(void) 
Lưu ý, sau đầu hàm không được viết dấu chấm phẩy. 
Đôi khi có người nhầm lẫn giữa một tham số và một đối số. Một tham số tham gia trong 
thân hàm như là “người giữ chỗ” cho một đối số. Các tham số của một hàm là cố định, 
chúng không hề thay đổi trong quá trình thực hiện của hàm. Một đối số là một giá trị thực sự 
được truyền cho hàm bởi chương trình gọi hàm. Mỗi lần hàm được gọi, nó có thể được 
truyền các đối số khác nhau. Số đối số được truyền phải bằng đúng số tham số. Đồng thời 
kiểu của đối số phải trùng với kiểu của tham số tương ứng. Trong hàm, các đối số được 
thâm nhập thông qua tên của tham số tương ứng. 
Thân hàm 
Thân hàm được đóng trong hai dấu ngoặc nhọn và đứng ngay sau đầu hàm. Tại đây các 
công việc của hàm được thực hiện. Khi một hàm được gọi, điều khiển bắt đầu tại câu lệnh 
đầu tiên của thân hàm và kết thúc (quay trở lại chương trình gọi nó) khi gặp một câu lệnh 
return hoặc khi đã đến dấu ngoặc đóng. 
Các biến cục bộ (local) 
Có thể khai báo các biến trong thân của một hàm. Các biến được khai báo trong thân hàm 
gọi là các biến cục bộ. Thuật ngữ cục bộ có nghĩa là các biến này là sở hữu cá nhân của 
riêng hàm này và khác biệt với các biến khác có cùng tên nhưng được khai báo tại những 
nơi khác trong chương trình. Ngược lại với cục bộ là tổng thể. Một biến được gọi là tổng 
thể nếu nó được khai báo trong chương trình nhưng không nằm trong bất cứ một thân hàm 
nào. 
Một biến cục bộ cũng được khai báo y như các biến khác. Nó cũng có thể được khởi tạo 
ngay khi khai báo. Trong một hàm, có thể khai báo mọi kiểu biến của C. 
Khi khai báo và sử dụng một biến trong một hàm nào đó thì có nghĩa là biến này hoàn toàn 
độc lập và khác biệt với mọi biến khác được khai báo ở đâu đó trong chương trình. 
Chương trình 5.2. Minh hoạ các biến cục bộ 
1: /* Minh hoạ các biến cục bộ */ 
2: 
3: #include 
4: 
5: int x = 1, y = 2; 
6: 
7: void demo(void); 
8: 
9: main() 
10: { 
11: printf(―\nTrước khi gọi demo(), x=%d và y=%d.‖, x,y); 
12: demo(); 
13: printf(―\nSau khi gọi demo(), x=%d và y=%d.‖, x,y); 
14: } 
15: 
16: void demo(void) 
17: { 
18: /* Khai báo và khởi tạo hai biến cục bộ.*/ 
19: 
20: int x=88, y=99; 
21: 
22: /* Hiện các giá trị của chúng. */ 
23: 
24: printf(―\nTrong hàm demo(), x=%d và y=%d.‖, x, y); 
25: } 
Có ba quy tắc chi phối cách sử dụng các biến trong một hàm 
 Để dùng một biến trong một hàm, phải khai báo nó trong đầu hàm hoặc trong thân 
hàm (trừ những biến tổng thể ). 
 Một hàm muốn thu nhận một giá trị từ chương trình gọi nó thì giá trị này phải được 
truyền cho hàm như là một đối số. 
 Để một chương trình gọi hàm có thể thu nhận được một giá trị từ một hàm, thì giá trị 
này phải được cho trở lại từ hàm. 
Giữ cho các biến của hàm tách biệt với các biến khác của chương trình là một trong những 
cách tốt nhất để cho hàm có tính độc lập. Một hàm có thể thực hiện mọi thao tác dữ liệu 
trên tập các biến cục bộ của nó mà không hề có một ảnh hưởng nào đến các phần khác của 
chương trình. 
Các câu lệnh của hàm 
Về nguyên tắc, không hề có một hạn chế nào về các câu lệnh có thể viết trong một hàm. 
Điều duy nhất không được phép là trong một hàm không được định nghĩa một hàm khác. 
Trong một hàm, có thể gọi các hàm thư viện và các hàm tự định nghĩa khác. 
Về độ dài của hàm thì sao? C không hạn chế về độ dài của các hàm, nhưng theo kinh 
nghiệm thực tế thì nên viết các hàm có độ dài vừa phải thôi. Trong lập trình có cấu trúc, mỗi 
hàm thường được thiết kế để thực hiện một nhiệm vụ tương đối đơn giản. 
Cho trở lại một giá trị 
Để cho trở lại một giá trị từ một hàm, phải dùng từ khóa return và theo sau là một biểu 
thức C. Khi thực hiện đến câu lệnh return, biểu thức này được đánh giá, sau đó giá trị 
này và điều khiển được truyền trở lại cho chương trình gọi hàm. Giá trị quay lại của hàm 
chính là giá trị của biểu thức đứng sau return. 
Một hàm có thể chứa nhiều câu lệnh return, nhưng chỉ có một câu lệnh được thực hiện 
bởi vì sau câu lệnh này là hàm kết thúc và sự thực hiện được chuyển trở lại cho chương 
trình. Dùng nhiều câu lệnh return trong một hàm là một cách hữu hiệu để cho trở lại các giá 
trị khác nhau từ một hàm. 
Chương trình 5.3. Minh hoạ cách dùng nhiều câu lệnh return trong một hàm 
1: /* Minh hoa cach dung nhiuu lenh return trong 1 ham */ 
2: 
3: #ịnclude 
4: 
5: int x, y, z; 
6: 
7: int lager(int a, int b); 
8: 
9: main() 
10: { 
11: puts(―Nhap hai so nguyen khac nhau: ―); 
12: scanf(―%d%d‖, &x, &y); 
13: 
14: z = lager(x,y); 
15: 
16: printf(―\nSo lon hon là %d.‖, z); 
17: } 
18: 
19: int lager(int a, int b) 
20: { 
21: if (a > b) 
22: return (a); 
23: else 
24: return (b); 
25: } 
Nguyên mẫu hàm 
Một chương trình phải chứa một nguyên mẫu cho mọi hàm mà nó sử dụng. Nguyên mẫu là 
gì và tại sao lại phải cần đến nó? 
Nguyên mẫu của một hàm giống hệt như đầu hàm cộng thêm dấu chấm phẩy ở cuối. Giống 
như đầu hàm, nguyên mẫu hàm chứa thông tin về kiểu biến được cho trở lại, tên hàm và các 
tham số. 
Nói một cách chặt chẽ thì một nguyên mẫu hàm không cần thiết phải đồng nhất với đầu hàm. 
Tên của các tham số có thể khác nhau miễn sao chúng có cùng kiểu, cùng một số lượng, và 
theo cùng một thứ tự. Nhưng không có một lý do nào mà nguyên mẫu lại không thể viết 
giống y như đầu hàm; khi chúng như nhau thì dễ viết chương trình hơn và việc đọc chương 
trình cũng sẽ dễ hiểu hơn. 
Truyền các tham số cho một hàm 
Để truyền các đối số cho một hàm, chỉ cần viết các đối số này vào sau tên hàm và đóng 
chúng bằng các dấu ngoặc tròn. Số đối số, kiểu của từng đối số phải trùng khớp với các 
tham số trong đầu hàm (cũng như trong nguyên mẫu hàm). Nếu hàm có nhiều đối số, thì các 
đối số đã được cho trong lời gọi hàm được gán cho các tham số của hàm theo thứ tự: đối số 
thứ nhất ứng với tham số thứ nhất, đối số thứ hai ứng với tham số thứ hai, và cứ thế tiếp tục. 
Mỗi đối số có thể là một biểu thức C bất kỳ: một hằng, một biến, một biểu thức toán học 
hoặc logic, hoặc thậm chí là một hàm khác (một hàm có cho trở lại một giá trị). 
Gọi các hàm 
Có hai cách gọi một hàm: 
 Viết tên hàm và danh sách đối số trong một câu lệnh riêng biệt. Nếu hàm này cho trở lại 
một giá trị thì giá trị này không được sử dụng. Ví dụ 
doi(15); 
 Viết tên hàm và danh sách đối số trong một biểu thức nào đó. Cách này chỉ được sử 
dụng cho những hàm có cho trở lại một giá trị. Vì các hàm loại này có cho trở lại một giá 
trị nên chúng là các biểu thức hợp lệ của C và do vậy có thể được sử dụng ở bất cứ chỗ 
nào mà một biểu thức có thể được sử dụng. Ví dụ: 
printf(―Một nửa của %d là %d.‖, x, mot_nua_cua(x)); 
 Trong đó, mot_nua_cua() là một lời gọi hàm và được sử dụng như một đối số của 
hàm printf(). Hoặc 
y = mot_nua_cua(x) + mot_nua_cua(z); 
 Trong đó, hàm mot_nua_cua() được gọi hai lần với các đối số khác nhau. 
Đệ qui 
Thuật ngữ đệ qui dùng để chỉ một tình huống trong đó một hàm lại gọi trực tiếp hoặc gián 
tiếp chính nó. Đệ qui gián tiếp xuất hiện khi một hàm gọi một hàm khác mà hàm này lại gọi 
hàm thứ nhất. C cho phép sử dụng các hàm đệ qui, và trong một số trường hợp chúng rất 
hữu ích. 
Ví dụ, đệ qui có thể được dùng để tính giai thừa của một số. Giai thừa của x được viết là x! 
và được tính theo công thức: 
x! = x * (x-1) * (x-2) * . . . * (2) * 1 
Tuy nhiên, cũng có thể tính x! như sau: 
x! = x * (x-1)! 
Đi tiếp một bước nữa, lại có thể tính (x-1)! theo cách tương tự: 
(x-1)! = (x-1) * (x-2)! 
Cứ tiếp tục phép tính đệ qui này cho đến khi số cần tính giai thừa giảm xuống đến 1, đó là 
lúc kết thúc. Chương trình 5.4 dùng một hàm đệ qui để tính giai thừa. Vì giai thừa của 9 đã 
lớn hơn giá trị lớn nhất có thể chứa được của kiểu nguyên không dấu nên giá trị tối đa của 
đối số chỉ là 8. 
Chương trình 5.3. Dùng hàm đệ qui để tính các giai thừa. 
1: /* Minh hoạ cách dùng hàm đệ qui để */ 
2: /* tính giai thừa của một số */ 
3: 
4: #include 
5: 
6: unsigned int f, x; 
7: unsigned int giai_thua(unsigned int a); 
8: 
9: main() 
10:  
11: puts(―Nhập một số nguyên nằm giữa 1 và 8: ―); 
12: scanf(―%d‖, &x); 
13: 
14: if( x > 8 || x < 1) 
15: { 
16: printf(―Chỉ các giá trị từ 1 đến 8 được chấp nhận!‖); 
17:  
18: else 
19:  
20: f = giai_thua(x); 
21: printf(―%u giai thừa bằng %u‖, x, f); 
22:  
23:  
24: 
25: unsigned int giai_thua(unsigned int a) 
26:  
27: if (a==1) 
28: return 1; 
29: else 
30:  
31: a *= giai_thua(a-1); 
32: return a; 
33:  
34:  
Kết luận 
Bài này giới thiệu về các hàm, một bộ phận quan trọng của lập trình C. Các hàm là các phần 
độc lập thực hiện các công việc nhất định. Khi chương trình cần thực hiện một công việc 
nào đó, nó sẽ gọi hàm thực hiện công việc này. Việc sử dụng các hàm chính là việc lập trình 
có cấu trúc - một phương pháp thiết kế chương trình theo kiểu trên xuống. Lập trình có cấu 
trúc tạo ra các chương trình có hiệu qủa hơn và cũng đơn giản hơn khi lập trình. 
Một định nghĩa hàm chứa một đầu hàm và một thân hàm. Đầu hàm chứa các thông tin về 
kiểu của giá trị quay lại, tên hàm, và các tham số. Thân hàm chứa các khai báo biến cục bộ 
và các câu lệnh C sẽ được thực hiện khi hàm được gọi. 
Câu hỏi 
1. Dòng đầu tiên của một định nghĩa hàm phải là gì, và nó chứa những thông tin nào? 
2. Một hàm có thể cho quay lại bao nhiêu giá trị? 
3. Nếu một hàm không cho quay lại một giá trị, thì phải khai báo nó như thế nào? 
4. Sự khác nhau giữa một định nghĩa hàm và một nguyên mẫu là gì? 
5. Một biến cục bộ là gì? 
6. Các biến cục bộ đặc biệt như thế nào? 
Bài tập 
1. Trong chương trình sau đây có gì sai không? 
#include 
void in_thong_bao(void); 
main() 
 
 in_thong_bao( ―Đây là một thông báo cần in ra‖); 
 
void in_thong_bao(void) 
 
 puts(―Đây là một thông báo cần in ra‖); 
 return 0; 
 
2. Có gì sai trong định nghĩa hàm sau đây? 
int hai_lan (int y ); 
 
 return ( 2 * y); 
 
3. Hãy viết lại Chương trình 5.3 với chỉ một câu lệnh return. 
4. Hãy viết một hàm nhận hai số làm đối số và cho quay trở lại tích của chúng. 
5. Viết một hàm nhận hai số làm đối số. Hàm này chia số thứ nhất cho số thứ hai. Không 
chia nếu số thứ hai bằng 0. 
6. Viết một hàm gọi các hàm trong các bài tập bốn và năm. 
7. Viết một chương trình có sử dụng một hàm tìm giá trị trung bình của năm giá trị kiểu float 
được nhập vào từ bàn phím. 
8. Viết một hàm đệ qui tính 3 lũy thừa lên một số lần bất kỳ. 
Bài 6. Điều khiển chương trình 
Trong Bài 4 đã nói đến câu lệnh if. Câu lệnh này cho phép điều khiển quá trình thực hiện 
của chương trình. Tuy nhiên, khả năng tạo ra các điều kiện đúng sai chưa đáp ứng được đầy 
đủ các nhu cầu về việc điều khiển chương trình. Bài này sẽ giới thiệu ba cách mới để điều 
khiển luồng thực hiện của chương trình: 
 Sử dụng các mảng đơn như thế nào. 
 Các chu trình for, while, và do ... while để thực hiện nhiều lần một nhóm 
các câu lệnh. 
 Các câu lệnh điều khiển lồng nhau. 
Cơ sở về các mảng 
Trước khi học câu lệnh for, cần biết qua về khái niệm mảng (array). Câu lệnh for và các 
mảng có liên quan chặt chẽ với nhau trong C, vì vậy rất khó định nghĩa khái niệm này mà 
không giải thích gì về khái niệm kia. 
Một mảng là một tập hợp các vị trí lưu trữ dữ liệu mang cùng một tên và phân biệt với nhau 
qua các chỉ số. Giống như các biến khác của C, các mảng phải được khai báo trước khi 
đem ra sử dụng. Một khai báo mảng bao gồm kiểu dữ liệu, tên mảng và cỡ của mảng. Ví 
dụ, câu lệnh 
int data1000; 
khai báo một mảng có tên là data, kiểu int và có 1000 phần tử. Các phần tử riêng biệt 
của mảng được đánh số từ data0 đến data999. Chú ý rằng phần tử đầu tiên là 
data0 chứ không phải là data1 như các ngôn ngữ khác. 
Mỗi phần tử của mảng này tương đương với một biến nguyên thông thường và có thể được 
sử dụng như nhau. Chỉ số của một mảng có thể là một biểu thức C, như trong ví dụ sau: 
int data1000; 
int a; 
a = 100; 
dataa = 10; /* tương đương data100=10; */ 
Điều khiển sự thực hiện của chương trình 
Thứ tự thực hiện trong một chương trình C được ngầm định là từ trên xuống. Nó khởi đầu 
từ câu lệnh đầu tiên của hàm main(), rồi đến câu lệnh tiếp theo và cứ thế tiếp tục cho đến 
câu lệnh cuối cùng của main(). Tuy nhiên, thứ tự này hiếm khi xảy ra trong một chương 
trình C thực sự. Ngôn ngữ C có nhiều câu lệnh điều khiển chương trình cho phép điều khiển 
thứ tự thực hiện của các câu lệnh. Trong Bài 4 đã trình bầy về câu lệnh if, bài này sẽ nói 
về một số câu lệnh điều khiển khác. 
Câu lệnh for 
Câu lệnh for là một cấu trúc của C, cho phép thực hiện một khối các câu lệnh một số lần 
nhất định. Đôi khi còn được gọi là vòng lặp for. 
Một câu lệnh for có cấu trúc như sau: 
for (khởi_tạo; điều_kiện; tăng) 
 câu_lệnh 
Trong đó, khởi_tạo, điều_kiện, tăng đều là các biểu thức C và câu_lệnh là một câu lệnh 
đơn hoặc phức hợp. Khi gặp một câu lệnh for thì các sự kiện sau sẽ xẩy ra: 
1. Biểu thức khởi_tạo được tính. Thông thường, khởi_tạo là một biểu thức gán, nó gán 
một giá trị nào đó cho một biến. 
2. Biểu thức điều_kiện được đánh giá. Thông thường, điều_kiện là một biểu thức quan 
hệ. 
3. Nếu biểu thức điều_kiện được đánh giá là false, thì câu lệnh for kết thúc và việc thực 
hiện sẽ chuyển đến câu lệnh đầu tiên đứng sau câu_lệnh. 
4. Nếu biểu thức điều_kiện được đánh giá là true, các câu lệnh trong câu_lệnh được 
thực hiện. 
5. Biểu thức tăng được tính, và quay trở lại bước hai. 
Sau đây là một ví dụ đơn giản. Chương trình 6.1 dùng một câu lệnh for để in các số từ 1 
đến 20. 
Chương trình 6.1. Minh họa một câu lệnh for đơn giản 
1: /* Minh họa một câu lệnh for đơn giản */ 
2: 
3: #include 
4: 
5: int dem; 
6: 
7: main() 
8:  
9: /* In các số từ 1 đến 20 */ 
10: 
11: for(dem = 1; dem <=20; dem++) 
12: printf("\n%d‖, dem); 
13:  
Hình 6.1 là sơ đồ khối minh họa hoạt động của vòng lặp for trong Chương trình 6.1. 
Câu lệnh for thường được sử dụng để tăng giá trị của một con đếm, hay còn gọi là đếm 
tiến. Còn có thể sử dụng nó để đếm lùi. Ví dụ, 
for (dem =100; dem >0; dem--) 
Ngoài ra, mỗi lần đếm không chỉ tăng 1 mà còn có thể tăng thêm một giá trị tuỳ ý, ví dụ: 
for (dem = 0; dem < 1000; dem +=5) 
Câu lệnh for rất mềm dẻo, ví dụ, có thể bỏ qua biểu thức khởi tạo nếu biến kiểm tra đã 
được khởi tạo trước đó trong chương trình. Nhưng vẫn phải dùng dấu chấm phẩy để ngăn 
cách: 
dem = 1; 
for ( ; dem < 1000; dem ++) 
Hình 6.1. Cách thức hoạt động của vòng lặp for trong chương trình 6.1. 
Cũng có thể bỏ qua biểu thức tăng, nếu như biểu thức điều kiện được cập nhật trong thân 
của câu lệnh for. Tất nhiên, vẫn phải có dấu chấm phẩy để làm dấu ngăn cách. Ví dụ, để in 
các số từ 0 đến 99, có thể viết như sau: 
for (dem = 0; dem <100; ) 
 printf ("%d ", dem ++); 
Biểu thức điều kiện dùng để điều khiển sự kết thúc của vòng lặp có thể là một biểu thức C 
bất kỳ. Chừng nào nó còn được đánh giá là true (khác 0), câu lệnh for vẫn tiếp tục thực 
hiện. Có thể dùng các phép logic của C để kiến tạo các biểu thức điều kiện phức tạp. Ví dụ 
sau đây dùng để in các phần tử của một mảng có tên mang(), và sẽ kết thúc khi tất cả các 
phần tử của mảng đã được in ra hoặc khi gặp phải một phần tử nào đó của mảng có giá trị 
bằng 0. 
for (dem = 0; dem <1000 && mangdem != 0; dem ++) 
 printf ("%d", mangdem); 
Có thể viết sau câu lệnh for một câu lệnh rỗng để đảm bảo rằng tất cả công việc đã được 
thực hiện trong bản thân câu lệnh for. Một lệnh rỗng là một dấu chấm phẩy duy nhất trên 
một dòng. Ví dụ, để khởi tạo cho tất cả các phần tử của một mảng gồm 1000 phần tử một 
giá trị chung là 30, có thể viết như sau: 
for (dem = 0; dem < 1000; mangdem ++ = 30) 
; 
Đúng 
Khởi 
đầu 
Gán 1 
cho dem 
dem 
<=20? 
Tăng dem 
thêm 1 
Thực hiện 
printf() 
Kết thúc 
for(dem=1;dem<=20;dem++) 
 printf(“\n%d”, dem); 
Sai 
Trong Bài 4, đã nói đến phép dấu phẩy. Bằng phép này có thể tạo lập một biểu thức bằng 
cách ghép hai biểu thức con liền nhau và phân cách chúng bằng một dấu phẩy. Hai biểu thức 
con này được tính theo thứ tự từ_trái_sang_phải, và biểu thức tổng thể được gán giá trị 
của biểu thức con bên phải dấu phẩy. Bằng cách dùng phép dấu phẩy, có thể thực hiện 
nhiều lần từng bộ phận của một câu lệnh for. 
Giả sử, có hai mảng 1000 phần tử, a  và b, và cần copy nội dung của a sang b theo 
thứ tự ngược, sao cho sau thao tác copy, b0 = a999, b1 = a998, và cứ thế tiếp 
tục. Câu lệnh for sau đây sẽ làm việc này: 
for (i = 0, j =999; i <1000 ; i++ , j--) 
 bj = a i; 
Các câu lệnh for lồng nhau 
Một câu lệnh for có thể được thực hiện trong một câu lệnh for khác. Các câu lệnh này 
được gọi là lồng nhau. Bằng cách lồng các câu lệnh for lại với nhau, rất nhiều kỹ thuật lập 
trình có thể giải quyết được. Chương trình 6.2 tuy không phải là phức tạp, nhưng nó minh 
họa khá tốt cách sử dụng hai lệnh for lồng nhau. 
Chương trình 6.2. Minh họa các câu lệnh for lồng nhau. 
1: /* Minh hoạ hai câu lệnh for lồng nhau */ 
2: 
3: #include 
4: 
5: void ve_hop(int dong, int cot); 
6: 
7: main() 
8:  
9: ve_hop(8, 35); 
10:  
11: 
12: void ve_hop(int dong, int cot) 
13:  
14: int cot1; 
15: for (;dong > 0; dong--) 
16:  
17: for(cot1 = cot; cot1 > 0; cot1--) 
18: printf("X"); 
19: 
20: printf("\n"; 
21:  
22:  
Câu lệnh while 
Câu lệnh while, hay còn gọi là vòng lặp while, thực hiện một khối các câu lệnh chừng 
nào một điều kiện đã xác định vẫn còn đúng. Nó có dạng sau: 
while (điều_kiện) 
 câu_lệnh 
điều_kiện ở đây là một biểu thức C, còn câu_lệnh là một câu lệnh đơn hoặc phức hợp. Khi 
sự thực hiện chương trình gặp một câu lệnh while, các sự kiện sau sẽ sảy ra: 
1. Biểu thức điều_kiện được đánh giá. 
2. Nếu điều_kiện có giá trị false (nghĩa là bằng không), câu lệnh while kết thúc, và 
thực hiện chuyển đến câu lệnh đầu tiên sau câu_lệnh. 
3. Nếu điều_kiện có giá trị true (nghĩa là khác không), câu_lệnh được thực hiện. 
4. Thực hiện chuyển lên bước 1. 
Hoạt động của while được minh họa trong Hình 6.2 
Hình 6.2. Sơ đồ khối minh họa họat động của câu lệnh while. 
Chương trình 6.3 là một ví dụ đơn giản về cách sử dụng một câu lệnh while để in các số 
từ 1 đến 20. 
Chương trình 6.3. Minh họa cách dùnh câu lệnh while 
1: /* Minh họa cách dùng câu lệnh while */ 
2: 
3: #include 
4: 
5: int dem; 
6: 
7: main() 
8:  
9: /* In các số từ 1 đến 20 */ 
10: 
Đúng 
Khởi 
đầu 
đánh 
giá 
điều 
kiện 
Thực hiện 
các câu lệnh 
Kết thúc 
while (điều kiện) 
 các câu lệnh; 
Sai 
11: dem = 1; 
12: 
13: while (dem <= 20) 
14:  
15: printf(―\n%d, dem); 
16: dem++; 
17:  
18:  
Câu lệnh while về cơ bản tương đối giống với một câu lệnh for không có các thành phần 
khởi tạo và tăng. Nghĩa là, 
for (; điều_kiện ; ) 
tương đương với 
while (điều_kiện) 
Vì sự tương đương này mà bất cứ điều gì có thể làm được bằng một câu lệnh for thì cũng 
có thể làm được bằng một câu lệnh while. Khi sử dụng một câu lệnh while, trước hết 
cần phải tiến hành việc khởi tạo trong một câu lệnh riêng, và việc cập nhật cũng phải được 
thực hiện bởi một câu lệnh trong thành phần của vòng lặp while. 
Khi mà việc khởi tạo và cập nhật là cần thiết, thì hầu hết những người lập trình có kinh 
nghiệm đều chọn câu lệnh for vì các biểu thức khởi tạo, kiểm tra, và tăng đều được viết 
cùng với nhau ngay sau từ for, điều đó giúp cho việc tìm, đọc, và sửa chữa dễ dàng hơn. 
Với câu lệnh while, các biểu thức khởi tạo và cập nhật được viết riêng tại những chỗ khác 
và như vậy dễ bị bỏ quên và khó theo dõi. 
Các câu lệnh while lồng nhau 
Giống như for và if, các câu lệnh while cũng có thể được lồng nhau. Chương trình 6.4 
chỉ ra một ví dụ về kiểu này. 
Chương trình 6.4. Minh họa các câu lệnh while lồng nhau. 
1: /* Minh họa các câu lệnh while lồng nhau */ 
2: 
3: #include 
4: 
5: int mang5; 
6: 
7: main() 
8:  
9: int ctr = 0, 
10: nbr = 0; 
11: 
12: printf(―Chương trình này nhắc bạn nhập 5 số\n‖); 
13: printf(―Mỗi số đều nằm giữa 1 và 10\n‖); 
14: 
15: while (ctr < 5) 
16:  
17: nbr = 0; 
18: while (nbr 10) 
19:  
20: printf(―\nNhập số thứ %d trong 5 số: ―,ctr+1); 
21: scanf( ―%d‖, &nbr); 
22:  
23: 
24: mangctr = nbr; 
25: ctr++; 
26:  
27: 
28: for( ctr=0; ctr<5; ctr++ ) 
29: printf( ―\nGiá trị thứ %d là %d‖, ctr+1,mangctr); 
30:  
Vòng lặp do ... while 
Cấu trúc lặp thứ ba của C là vòng lặp do...while. Vòng lặp này thực hiện một khối các câu 
lệnh khi mà một điều kiện đã cho vẫn còn đúng. Nó thử điều kiện này ở cuối vòng lặp chứ 
không làm ở đầu như các vòng lặp for và while. 
Cấu trúc của vòn
            Các file đính kèm theo tài liệu này:
 01200051_0378_1983580.pdf 01200051_0378_1983580.pdf