Giáo trình Lập Trình C, giáo trình ngôn ngữ lập trình C căn bản, dành cho các bạn học sinh, sinh viên ngành công nghệ thông tin, một ngôn ngữ căn bản, một tài liệu cần thiết cho các học viên trong các trường đại học
Trang 1LËp tr×nh C
Biªn so¹n
TS Phan §¨ng CÇu
Hµ néi, th¸ng 01/ 2004
Trang 2lời nói đầu
Tài liệu này chúng tôi biên soạn nhằm mục đích hỗ trợ các bạn học viêntrong quá trình tiếp thu bài giảng môn học "Lập trình C" Thực ra có thể gọichính xác hơn là “Lập trình C trong môi trờng C++”, vì môi trờng chúng ta
sử dụng là C++, và các lệnh cũng không hoàn toàn là của C mà đã có sửdụng cú pháp của C++ khi cần thiết; thậm chí chơng 13 nói về tính đónggói, thừa kế và đa hình là của C++ (chúng tôi viết chơng này chủ yếu đểcác bạn tham khảo và tự tìm hiểu) Chúng tôi muốn nhấn mạnh rằng tài liệunày chỉ hỗ trợ các bạn, chứ không thay thế bài giảng vì các vấn đề đợc trìnhbày một cách tóm tắt, còn nhiều điều cha đợc giải thích đầy đủ, nếu chỉ
đọc mà không nghe giảng thì rất khó hiểu Ví dụ khi trình bày cú pháp cáclệnh thì dấu [ ] hàm ý là những gì nằm giữa hai dấu này là không bắtbuộc, còn những gì nằm giữa 2 dấu < > lại là bắt buộc phải có Tuy nhiêndấu [] lại đợc dùng để bao các chỉ số trong một mảng Ví dụ a[i][j] là phần
tử ở hàng i, cột j của một mảng a 2 chiều… Trong quá trình giảng dạy chúngtôi sẽ dựa vào sự tiếp thu của các bạn để đa ra cách giải thích và các ví dụminh họa phù hợp
Chúng tôi luôn sẵn sàng lắng nghe các ý kiến đóng góp của các bạn về tàiliệu cũng nh bài giảng, để sự hợp tác giữa giáo viên và học viên ngày cànghiệu quả hơn
Hà nội, tháng 01/2004
`
TS Phan Đăng Cầu
Trang 3Mục lục
Chơng 1 Tổng quan về C và C++ 1
1.1 Ngôn ngữ lập trình và thuật toán 1
1.1.1 Ngôn ngữ lập trình 1
1.1.2 Thuật toán (Algorithm) 2
1.1.3 Sự ra đơi và phát triển của ngôn ngữ C 2
1.2 Các phần tử cơ bản trong ngôn ngữ C 3
1.2.1 Bộ ký tự (character set) 3
1.2.2 Các từ khóa (key words, reserved words) 3
1.2.3 Tên và cách đặt tên (identifier) 3
1.2.4 Lời giải thích (comment) 3
1.2.5 Một vài chơng trình C đơn giản 3
1.3 Cấu trúc chơng trình trong C 4
1.3.1 Các thành phần của một chơng trình C 4
1.3.2 Soạn thảo và chạy một chơng trình C 4
Chơng 2 Hằng, biến và mảng 7
2.1 Các kiểu dữ liệu cơ sở 7
2.2 Hằng 8
2.3 Kiểu enum 9
2.4 Biến 9
2.5 Mảng và chuỗi 11
2.6 Định nghĩa kiểu bằng typedef 11
2.7 Khối lệnh 12
2.8 Vài nét về hàm và chơng trình 12
2.9 Biến, mảng tự động 12
2.10 Biến, mảng ngoài 12
2.11 Toán tử sizeof 14
2.12 Biến tĩnh, mảng tĩnh 14
Chơng 3 Biểu thức 15
3.1 Khái niệm biểu thức(expression) 15
3.2 Phép toán số học 15
3.3 Các phép thao tác bit 15
3.4 Các phép toán quan hệ và logic 15
3.5 Phép chuyển đổi kiểu giá trị 15
3.6 Phép toán tăng giảm 15
3.7 Câu lệnh gán và biểu thức 15
3.8 Biểu thức điều kiện 15
3.9 Thứ tự u tiên các phép toán 16
Chơng 4 Vào ra 17
4.1 Hàm printf 17
4.2 Hàm scanf 18
4.3 Đa ra máy in- hàm fprintf 19
4.4 Dòng vào stdin và các hàm nhập dữ liệu 19
4.5 Nhập /xuất số liệu cho chuỗi và ký tự 21
4.6 Một số hàm xử lý chuỗi 21
4.7 Sự khác biệt giữa mảng và chuỗi 21
4.8 Các hàm vào ra và dịch chuyển vị trí trên màn hình 23
4.9 Các luồng nhập xuất cin,cout 23
Chơng 5 Các toán tử điều khiển 24
5.1 Nhắc lại khái niệm câu lệnh và khối lệnh 24
Trang 45.2 Toán tử if 24
5.3 else if 24
5.4 Toán tử switch 24
5.5 Toán tử goto và nhãn 24
5.6 Toán tử for 24
5.6 Toán tử while 25
5.7 do while 25
5.8 Lệnh break và lệnh continue 25
Chơng 6 Hàm và cấu trúc chơng trình 27
6.1 Khai báo và định nghĩa hàm 27
6.2 Các hàm void 28
6.3 Lời gọi hàm 29
6.4 Sử dụng hàm assert() để kiểm tra điều kiện trớc 29
6.5 Các đối số mặc định 29
6.6 Truyền tham số cho hàm 30
6.7 Hàm chồng(Overloaded function) 33
6.8 Các mẫu (template) 33
6.9 Hàm với các tham số là mảng 34
Chơng 7 Con trỏ, cấp phát động và hàm 36
7.1 Con trỏ và địa chỉ 36
7.2 Con trỏ và mảng một chiều 38
7.3 Con trỏ tới hàm 39
7.4 Khả năng đệ quy của hàm 40
7.5 Hàm main có đối (tham khảo tài liệu [2], trang 198) 40
Chơng 8 Cấu trúc 41
8.1 Kiểu cấu trúc 41
8.2 Truy nhập đến thành phần cấu trúc 42
8.3 Phép gán cấu trúc 42
8.4 Cấu trúc trong C++ 42
8.5 Thành phần kiểu fields 43
8.6 Union 43
Chơng 9 Thao tác trên tệp 44
Chơng 10 Quản lý màn hình văn bản 49
10.1 Chọn kiểu màn hình văn bản 49
10.3 Sự khác biệt giữa các hàm nhập xuất dữ liệu 50
10.4 Một số hàm thao tác màn hình khác 50
10.5 Ví dụ ứng dụng: Tạo thực đơn trên cửa sổ 50
Chơng 11 Đồ họa 54
11.1 Khái niệm về đồ họa 54
11.2 Độ phân giải màn hình đồ họa 55
11.3 Vẽ hình trong màn hình đồ họa 56
11.4 Đặt màu nền, màu vẽ, màu tô 56
11.5 Vẽ một số đờng và hình cơ bản 57
11.6 Cửa sổ (viewport) 59
11.7 Xử lý văn bản trên màn hình đồ họa 61
11.8 Tạo hình ảnh chuyển động 64
Chơng 12 Âm thanh, âm nhạc 66
Chơng 13 Tính đóng gói, thừa kế và đa hình 68
13.1 Mở đầu 68
Câu hỏi và bài tập ôn tập chuẩn bị cho thi hết môn 72
Phụ lục Các chơng trình mẫu 80
2
Trang 501 Tìm ớc số chung lớn nhất của 2 số nguyên 85
02 Chuyển đổi một số thập phân sang dạng nhị phân 85
03 Chuyển đổi một số thập phân sang số có cơ số bất kỳ 85
04 Chuyển đổi một số thập phân sang số có cơ số 11 86
05 Nhập và sắp xếp danh sách các số thực 87
06 Đảo lại một chuỗi ký tự 88
07 Một số chong trình đệ quy: tính giai thừa của một số nguyên không âm, tính số Fibonacci, tìm ớc số chung lớn nhất của hai số nguyên không âm, bài toán tháp Hà nội 88
08 Sàng Erastothen liệt kê các số nguyên tố không vợt quá một số tự nhiên n cho trớc 89
09 Tìm số lớn nhất trong dãy các số thực 90
10 Tìm xem một số thực x có xuất hiện trong dãy số thực hay không 91
11 Tính giá trị đa thức bậc n theo phơng pháp Horner 92
12 Tìm tổ hợp của các loại giấy bạc 92
13 Loại trừ các dấu cách thừa trong chuỗi tên (chỉ để lại một dấu cách) 93
14 Đếm số chữ trong chuỗi ký tự 93
15 Tính số theo công thức: 94
16 Nhập và in danh sách sinh viên (phiên bản 1) 96
17 Nhập và in danh sách sinh viên (phiên bản 2) 98
18 Nhập, sắp xếp và in danh sách sinh viên (phiên bản 3) 117
19 Nhập, sắp xếp, tìm kiếm và in danh sách sinh viên (phiên bản 4) 122
20 Nhập, sắp xếp, tìm kiếm và in danh sách sinh viên (phiên bản 5) 131
21 Nhập, sắp xếp, tìm kiếm và in danh sách sinh viên (phiên bản 6) 143
22 Danh sách cài đặt bằng danh sách liên kết (phiên bản 1) 145
23 Danh sách cài đặt bằng danh sách liên kết (phiên bản 2) 148
24 Danh sách cài đặt trên tệp (phiên bản 1) 151
25 Danh sách cài đặt trên tệp (phiên bản 2) 154
Tài liệu tham khảo 158
3
Trang 6Chơng 1 Tổng quan về C và C++
1.1 Ngôn ngữ lập trình và thuật toán
1.1.1 Ngôn ngữ lập trình
Trong phần “Nhập môn tin học” chúng ta đã tìm hiểu Winword và Excel,
là các phần mềm ứng dụng trong công việc soạn thảo văn bản và làm các bảngtính toán đợc Đặc điểm của các phần mềm ứng dụng là luôn định rõ phạm
vi ứng dụng và cung cấp càng nhiều càng tốt các công cụ để hoàn thành chứcnăng đó Tuy nhiên ngời sử dụng cũng hầu nh bị bó buộc trong phạm vi quy
định của chơng trình Chẳng hạn ta khó có thể dùng Excel để giải một bàitoán gồm nhiều bớc tính toán nh tính nghiệm gần đúng một phơng trình viphân hay giải một hệ phơng trình tuyến tính Mặc dầu các phần mềm ứngdụng ngày càng nhiều và thuộc đủ các lĩnh vực nh xây dựng, thiết kế, hộihọa, âm nhạc nhng không thể bao trùm hết các vấn đề nẩy sinh trong thực
tế vô cùng phong phú Rõ ràng không chỉ những chuyên gia tin học mà ngaycả những ngời sử dụng, nhất là các cán bộ kỹ thuật, rất cần đến những phầnmềm uyển chuyển và mềm dẻo hơn, có khả năng thực hiện đợc nhiều hơncác chỉ thị của ngời sử dụng để giúp họ giải quyết những công việc đadạng bằng máy tính Phần mềm có tính chất nh thế đợc gọi là ngôn ngữ lập
trình Chính xác hơn ngôn ngữ lập trình là một ngôn ngữ nhân tạo bao
gồm một tập các từ vựng (mà ta sẽ gọi là từ khóa để phân biệt với ngôn
ngữ thông thờng) và một tập các quy tắc (gọi là Syntax - cú pháp) mà ta có
thể sử dụng để biên soạn các lệnh cho máy tính thực hiện
Nh ta đã biết, các ô nhớ của máy tính chỉ có thể biểu diễn các số 0 và 1 Vìvậy ngôn ngữ mà máy có thể hiểu trực tiếp là ngôn ngữ trong đó các lệnh làcác dãy số nhị phân và do đó đợc gọi là ngôn ngữ máy (machine language) Mọi ngôn ngữ khác đều phải thông dịch hoặc biên dịch sang ngôn ngữ máy(Interpreter - thông dịch và cho chạy từng lệnh Compiler - biên dịch thành 1chơng trình ngôn ngữ máy hoàn chỉnh, do vậy chạy nhanh hơn thông dịch)
Có nhiều loại ngôn ngữ lập trình, và hầu hết các nhà khoa học về máy tính
đều cho rằng không có một ngôn ngữ độc nhất nào có đủ khả năng phục vụcho các yêu cầu của tất cả các lập trình viên Theo truyền thống, các ngônngữ lập trình đợc phân làm 2 loại: các ngôn ngữ bậc thấp và ngôn ngữ bậccao
Ngôn ngữ lập trình bậc thấp (low-level programming language):
Ngôn ngữ máy, hợp ngữ (asembler: chơng trình dịch hợp ngữ, assemblylanguage: ngôn ngữ hợp ngữ) Hợp ngữ là ngôn ngữ một bậc từ ngôn ngữ máy
Nó chỉ khác với ngôn ngữ máy trong việc sử dụng các mã biểu thị các chứcnăng chính mà máy thực hiện
Lập trình bằng hợp ngữ rất phiền toái: có đến vài tá dòng mã cần thiết chỉ
để thực hiện phép cộng 2 con số Các chơng trình hợp ngữ rất khó viết;chúng không có cấu trúc hoặc modun hóa rõ ràng Chơng trình hợp ngữ cũngkhông dễ chuyển từ loại máy tính này sang loại máy tính khác Các chơngtrình này đợc viết theo các tập lệnh đặc thù của loại bộ vi xử lý nhất định.Lập trình bằng hợp ngữ thì mã gọn và chạy nhanh Do đó hầu hết các chơngtrình điều hành hệ thống đều đợc viết bằng hợp ngữ Tuy nhiên do sự phứctạp của công việc lập trình nên các hãng sản xuất phần mềm chuyên dụng
Trang 7thích viết chơng trình bằng ngôn ngữ C (do Bell Laboratories của hãng AT&Txây dựng) là loại ngôn ngữ kết hợp đợc cấu trúc của ngôn ngữ bậc cao hiện
đại với tốc độ và tính hiệu quả của hợp ngữ bằng cách cho phép nhúng cáclệnh hợp ngữ vào chơng trình
Ngôn ngữ lập trình bậc cao:
Các ngôn ngữ lập trình bậc cao nh Basic, Pascal, C, C++ cho phép các lậptrình viên có thể diễn đạt chơng trình bằng các từ khóa và các câu lệnh gầngiống với ngôn ngữ tự nhiên Các ngôn ngữ này dợc gọi là “bậc cao” vì chúnggiải phóng các lập trình viên khỏi những quan tâm về từng lệnh sẽ đợc máytính tiến hành nh thế nào, bộ phận thông dịch hoặc biên dịch của chơngtrình sẽ giải quyết các chi tiết này khi mã nguồn đợc biến đổi thành ngônngữ máy Một câu lệnh trong ngôn ngữ bậc cao tơng ứng với một số lệnhngôn ngữ máy, cho nên bạn có thể thảo chơng theo ngôn ngữ bậc cao nhanhhơn so với bậc thấp Tuy nhiên bạn cũng phải trả giá cho việc này Chơng trìnhngôn ngữ máy đợc dịch ra từ mã nguồn đợc viết bằng ngôn ngữ bậc cao chứarất nhiều chi tiết thừa, do đó tốc độ chạy sẽ chậm hơn nhiều so với chơngtrình viết bằng hợp ngữ Thông thờng một trình biên dịch đặc trng thờngsinh ra số lệnh mã máy gấp 2 lần hay nhiều hơn số lệnh cần thiết nếu viếtbằng mã máy
Ngôn ngữ khai báo: Ngôn ngữ sẽ định nghĩa một loạt các yếu tố và các quan
hệ, đồng thời cho phép bạn có thể tiến hành xếp hàng đối với những kết quảxác định Thí dụ: Prolog, SQL (Structured Query Language)
Điều then chốt trong việc lập trình chuyên dụng là môdun hóa ngôn ngữ - đó
là sự phát triển sao cho nhiệm vụ lập trình có thể phân phối đợc cho cácthành viên của một nhóm lập trình, và kết quả đạt đợc là các bộ phận khácnhau sẽ hoạt động phù hợp với nhau khi nhiệm vụ của từng ngời hoàn thành.Ngôn ngữ lập trình môdun, nh Module-2 hoặc ngôn ngữ hớng đối tợng nh C++, sẽ cho phép từng lập trình viên có thể tập trung vào việc lập mã, biên dịch
và gỡ rối các module chơng trình riêng biệt, đồng thời có thể cho chạy (kiểmtra thử) riêng từng module của mình Khi từng module riêng đã chạy tốt chúng
sẽ đợc liên kết với nhau mà không gây trục trặc nào
1.1.2 Thuật toán (Algorithm)
Thuật ngữ Algorithm đợc dịch ra tiếng Việt là thuật toán, thuật giải hoặc giảithuật ở đây chúng tôi dùng từ thuật toán là cách gọi quen thuộc với nhiều ng-ời
Thuật toán là một dãy hữu hạn các bớc, mỗi bớc mô tả chính xác các phép toán hoặc hành động cần thực hiện, để giải quyết một vấn đề.
Để hiểu đầy đủ ý nghĩa của khái niệm thuật toán, chúng ta nêu ra 6 đặc
tr-ng sau đây của thuật toán:
1 Input Mỗi thuật toán thờng có một số dữ liệu vào
2 Ouput Mỗi thuật toán thờng có một số dữ liệu ra.
Trang 83 Tính xác định (Definiteness) Mỗi bớc đợc mô tả chính xác, chỉ có một
cách hiểu duy nhất và đủ đơn giản để có thể thực hiện đợc
4 Tính dừng (Finiteness) Thuật toán phải dừng sau một số hữu hạn bớc thực hiện
5 Tính hiệu quả (Effectiveness) Các phép toán trong các bớc phải đủ đơn
giản để có thể thực hiện đợc
6 Tính tổng quát (Generalness) Thuật toán phải có tính tổng quát, có thể
áp dụng cho một lớp đối tợng
Ngợc lại (r>0) m:=n; n:=r và quay lại bớc 1
1.1.3 Sự ra đơi và phát triển của ngôn ngữ C
Năm 1970 Ken Thompson sáng tạo ra ngôn ngữ B dùng trong môi trờng hệ
điều hành UNIX trên máy điện toán DEC PD-7 B là ký tự đầu của cụm chữviết tắt BCPL (Basic Combined Programming Language) do Martin Richardsviết Năm 1972 Dennis Ritchie của hãng Bell Laboratories (và Ken Thompson)sáng tạo nên ngôn ngữ C nhằm tăng hiệu quả cho ngôn ngữ B Lúc đầu ngônngữ C không đợc mọi ngời a dùng Nhng sau khi D.Ritchie cho xuất bản cuốn
"The C Programming Language" thì ngôn ngữ C đợc chú ý và đợc sửdụng rộng rãi Ngời ta đã dùng C để viết hệ điều hành đa nhiệm UNIX, O/S
2 và ngôn ngữ Dbase C đã đợc cải tiến qua nhiều phiên bản: trình biêndịch Turbo C từ phiên bản 1 đến phiên bản 5, Microsoft C từ phiên bản 1
đến phiên bản 6 Hiện nay, C lại đợc phát triển để thành C++ với 3 trìnhbiên dịch: Borland C++ (dùng trong DOS và trong Windows), Visual C++(dùng trong DOS và trong Windows) và Turbo C++ (dùng trong DOS) Có thểnói rằng hiện nay hầu hết các phần mềm ứng dụng đều đợc viết trực tiếphoặc gián tiếp bằng C hoặc C++
Trong tài liệu này chúng tôi sẽ giới thiệu C và một số lệnh của C++
1.2 Các phần tử cơ bản trong ngôn ngữ C
1.2.1 Bộ ký tự (character set)
1.2.2 Các từ khóa (key words, reserved words)
Các từ khóa thông dụng trong C
continue extern int sizeof union volatile
1.2.3 Tên và cách đặt tên (identifier)
Đối với C, mọi tên cần phải khai báo trớc khi sử dụng Tên ta dãy các ký tự liềnnhau, bắt đầu bằng ký tự hoặc dấu gạch dới, tiếp theo là các ký tự, các số
Trang 9hoặc dấu gạch dới
Chú ý: Tên không chứa dấu cách C phân biệt chữ hoa và chữ thờng
1.2.4 Lời giải thích (comment)
Lời giải thích từ một vi trí đến cuối dòng: //
Lời giải thích trên một hoặc nhiều dòng: /* */
Hiện câu chào
//002CHAO1.cpp Hien cau chao tren man hinh
Thông thờng một chơng trình C đầy đủ có các phần sau:
Chỉ dẫn tiền xử lý (preprocessor directive)
- Các bao hàm (#include)
- Các vĩ lệnh (#define)
Khai báo toàn cục
- Khai báo các nguyên mẫu cho hàm (function prototype)
- Khai báo các biến toàn cục
Trang 10 Hàm main()
Định nghĩa chi tiết các hàm do ngời sử dụng vừa khai báo trên
Phần "Định nghĩa chi tiết các hàm " có thể đặt ở ngay sau phần khai báotoàn cục
iomanip.h setw,setprecision, (cout<<oct<<a<<hex<<b;)
1.3.2 Soạn thảo và chạy một chơng trình C
a Soạn thảo chơng trình
Mỗi câu lệnh của C có thể viết trên một dòng hay nhiều dòng nhng phải kết
thúc bằng dấu ; Tuy nhiên khi nhập một chuỗi ký tự mà muốn chuyển sang dòng khác ta phải thêm dấu \ trớc khi xuống dòng
b Dịch và chạy chơng trình
Khi nhấn F9 thì đầu tiên chơng trình đợc dịch sang tệp có đuôi là *.obj,sau đó liên kết các tệp và dịch sang tệp có đuôi *.exe có thể chạy đợctrong môi trờng DOS
Khi dịch chơng trình có thể xuất hiện 3 loại lỗi sau đây:
Lỗi đ ợc thông báo bởi từ khóa error (lỗi cú pháp) :
Lỗi này thờng xảy ra do khi ta soạn thảo chơng trình không tuân theo đúngnhững quy tắc của C, thí dụ int thì ta gõ thành Int; hay ta gõ thiếu ngoặc
đơn, ngoặc kép chẳng hạn
Sau đây là một số thông báo lỗi thờng gặp loại này
Unknown preprocessor directive
Chỉ thị tiền xử lý không đúng Trong trờng hợp này bạn phải xem lại các lệnh
#include xem bạn có viết sai không
Declaration terminated incorrectly
Khai báo kết thúc không đúng Ví dụ bạn đánh dấu ; sau hàm main nh sau:
void main();
chẳng hạn Nên lu ý là sau tên hàm không đợc đánh dấu ; nh trên đây Sau
tên hàm phải là dấu { và kết thúc hàm là dấu }.
Unexpected }
Trang 11Thừa dấu } Mỗi lần đánh dấu { thì bạn nên đánh dấu } rồi sau đó gõ các lệnh vào đoạn giữa, nh vậy tránh đợc tình trạng thừa thiếu dấu { hoặc }.
Compound statement missing }
Thiếu dấu }.
Declaration syntax error
Khai báo sai Ví dụ bạn viết
int a,b
printf("Chao");
thì máy báo lỗi ở dòng thứ 2 Sở dĩ nh vậy là vì khi đọc qua dòng thứ nhấtkhông có dấu ; máy cho rằng lệnh cha kết thúc và còn chuyển tiếp sang dòngthứ 2 Tuy nhiên sang dòng thứ 2 thì máy lại thấy lệnh không phù hợp nên báolỗi ở dòng này Cách viết trên đây tơng đơng với cách viết:
int a,b printf("Chao");
Và máy thấy rằng đây là một lệnh không đúng Còn nếu ta sử lại các lệnhtrên là
int a,b
;printf("Chao");
thì máy không còn báo lỗi nữa vì nó chuyển xuống dòng thứ 2 gặp dấu ;
và biết là lệnh int a,b; đợc khai báo đúng.
Undefined symbol
Bạn đã sử dụng một biến nào đó mà cha khai báo Ví dụ bạn cha khai báo biến
n nhng lại sử dụng trong lệnh:
printf("%d",n);
chẳng hạn thì máy báo là Undefined symbol 'n'
Function should have a prototype
Ví dụ trong lệnh trên bạn viết sai là
prinf("%d",n);
thì máy báo là Function 'prinf' should have a prototype Nghĩa của câu nàylà: hàm prinf cần phải có nguyên mẫu
Lỗi đ ợc thông báo bởi từ khóa Warning (lỗi cảnh báo) :
Lỗi này thờng xảy ra do khi ta khai báo biến nhng không sử dụng tới
Ví dụ
is assigned a value that is never used
Khai báo và đã gán giá trị cho biến nhng không sử dụng
Ví dụ bạn viết các lệnh
int n; n=10;
nhng trong các phần tiếp theo không sử dụng n (để hiện ra màn hìnhchẳng hạn, hay dùng để tính giá trị của biến khác ) thì máy báo là
'n' is assigned a value that is never used
tuy nhiên đây chỉ là thông báo (warning) Khi bạn nhấn F9 để dịch chơngtrình thì máy vẫn báo là success
Hai loại lỗi trên đây đợc thông báo ngay khi dịch chơng trình thành file *.obj
Loại lỗi thứ 3 có thể xảy ra trong quá trình liên kết:
Lỗi này thờng xảy thí dụ khi có lời gọi hàm nhng hàm chỉ mới có nguyên mẫu
Trang 12mµ cha cã khai b¸o chi tiÕt.
Trang 13Chơng 2 Hằng, biến và mảng
2.1 Các kiểu dữ liệu cơ sở
C có một số kiểu dữ liệu cơ sở nh sau:
Mã ASCII 0 1 127 128 129 254 255
Giá trị biến kiểu char 0 1 127 -128 -127 -2 -1
Giá trị biến kiểu
Phân loại ký tự:
Các ký tự có thể phân làm 3 nhóm: 0-31,32-126, 127-254; trong đó các ký tự
từ 0 đến 32 là các ký tự điều khiển, không thể nhìn thấy trên màn hình
Chú ý:Trong chơng trình một ký tự phải đợc bao trong dấu nháy đơn Thí dụchar ch='A'; ch='1' sẽ cho ta ch=49, còn ch=1 lại cho ta ch biểu diễn ký tự coamã ASCII là 1
Trang 143 Kiểu dấu phẩy động
float chiếm 4 byte, double chiếm 8 byte
Chú ý Khi hiện một số nguyên dới dạng bát phân bằng lệnh printf() ta dùng
khuôn dạng % và chữ 0, còn với dạng thập lục phân thì dùng % và ký tự x(in thờng hoặc in hoa) Ví dụ
printf("%o",9) hoặc printf("%x",17) cho kết quả trên màn hình là 11
printf("%o",011) hoặc printf("%x",0x11) cho kết quả trên màn hình là 11
3 float và double (số thực và số thực có độ chính xác gấp đôi)
Là một dãy ký tự bất kỳ đặt trong 2 dấu "", thí dụ "Ha noi"
Chú ý: 'a' là hằng ký tự gồm một byte, còn "a" là hằng xâu ký tự gồm 2 byte,
1 byte chứa ký tự a, byte cuối cùng chứa ký tự '\0' là ký tự báo kết thúc
xâu.
Chú ý:
Khi ta dùng hàm strlen(st) để xác định độ dài của chuỗi ký tự thì ký tự '\0'
Trang 15không đợc tính vào độ dài này Thí dụ 3 biến st1,st2,st3 sau đây đều có
Giả sử ta khai báo:
#define str "Ha Noi"
const float a=10.123;
const char str2[]="Hai Phong";
const char *str3="Tay Ho";
Không nên dùng const char str2[5]= "Hai Phong" chẳng hạn, vì có thể độ dàicủa hằng vợt quá độ dài định nghĩa
Chú ý: Hằng có thể là các biến chiếm dụng bộ nhớ thí dụ int m =10; nhng cũng
có thể là những giá trị tạm thời, không đợc phân phối bộ nhớ thí dụ M_PI, M_E,printf("\n%s","Ha Noi");
Trang 16Tuy nhiên theo cách này thì các con số 2, 3, không cho ta ý nghĩa gợi nhớ
đó là các ngày trong tuần C cho ta ta định nghĩa biến kiểu liệt kê phục vụcho mục đích này Ta có thể làm nh sau
enum ngay_tt {hai, ba, tu, nam, sau, bay, chunhat};
ngay_tt i;
for(i=hai;i<=chunhat;i++) {các lệnh}
Giả sử các lệnh là printf("%d ",i); thì ta có kết quả là 0 1 2 3 4 5 6
Vì biến enum thực chất là biến nguyên, do đó các dòng lệnh trên có thể viết
đơn giản hơn nh sau:
enum {hai, ba, tu, nam, sau, bay, chunhat};
int i;
for(i=hai;i<=chunhat;i++) [lệnh]
Ta có thể khởi gán các giá trị cho biến enum Ví dụ nếu ta viết:
enum {hai=2, ba, tu, nam, sau, bay, chunhat};
thì khi đó ta sẽ có hai=2,ba=3,
2.4 Biến
Mọi biến phải đợc khai báo trớc khi sử dụng Việc khai báo đợc thực hiện theomẫu sau:
<Kiểu biến> <tên biến 1>,<tên biến 2>, ,<tên biến n>;
Có thể khai báo ngay khi sử dụng, thí dụ
for(int i=0;i<n;i++) [các lệnh]
1 Vị trí của các khai báo
Trong C++ thì biến có thể khai báo tại vị trí bất kỳ trong chơng trình
2 Khởi đầu cho các biến
float a=10.123;
char str2[]="Hai Phong";
char *str3="Tay Ho";
int x[] = {1,2,3};
float y[4] = {1.2, 3.2, 4.3, 6.5};
Nhng không thể viết:
float *py = {1.2, 3.2, 4.3, 6.5};//Viết nh thế này là sai.
Nhng với chuỗi thì lại đợc:
char st1[]="123";
char st2[7]="123";
char *st="123";
3 Lấy địa chỉ của biến
Mỗi biến đợc cấp phát một vùng nhớ gồm một số byte liên tiếp Địa chỉ củabyte đầu tiên là địa chỉ của biến Địa chỉ của biến có thể nhận đợc bằngphép toán
Trang 17p=&(k+1);//Dòng lệnh này sai
Ta chỉ có thể gán một con trỏ bằng địa chỉ của một biến cùng kiểu với nó Ví
dụ phép gán sau là sai:
int *p;
const int m=10;
p=&m;//Dòng lệnh này sai
Phải sửa lại là
Để có thể lu trữ và thực hiện các phép toán trên các giá trị địa chỉ, C địnhnghĩa một kiểu biến nguyên đặc biệt mà giá trị của chúng là địa chỉ củacác biến và gọi biến này là con trỏ Khi định nghĩa con trỏ ta phải chỉ rõ đó
là con trỏ của biến loại gì Thí dụ để khai báo một con trỏ kiểu nguyên taviết
int *p;
Và khi đó ta có thể gán p = &x; trong đó x là biến nguyên nào đó
4 Lấy giá trị của một biến thông qua địa chỉ
Giả sử ta p là một con trỏ Khi đó *p sẽ là giá trị đợc chứa trong vùng bộ nhớ
mà p trỏ tới
Nh vậy với biến x bất kỳ thì ta có *&x chính là giá trị x
Trang 182.5 Mảng và chuỗi
Trong phần trên ta hiểu biến là một vùng bộ nhớ đợc đặt tên và có thể lu trữmột kiểu giá trị nào đó Để biểu diễn nhiều giá trị ta phải dùng nhiều biến.Tuy nhiên nhiều khi số biến lại phụ thuộc một tham số nào đó Thí dụ sốthành phần của một vectơ phụ thuộc vào một số n, còn số các phần tử củamột ma trận lại phụ thuộc số hàng và số cột Trong những trờng hợp này ngời tadùng một kiểu dữ liệu có cấu trúc là mảng để biểu diễn số liệu Mảng là mộttập hợp nhiều phần tử có cùng một kiểu giá trị và có chung một tên Về mặtnào đó có thể coi mảng cũng là biến, nhng là biến có cấu trúc phức tạp hơn
Định nghĩa mảng
Khai báo int a[10] sẽ dành 10 vị trí liên tiếp trong bộ nhớ, mỗi vị trí gồm 2byte có thể lu trữ đợc một số nguyên và các vị trí này tơng ứng là a[0],a[1], , a[9]
Khai báo int a[8][10] sẽ dành 80 vị trí liên tiếp trong bộ nhớ, mỗi vị trí gồm
2 byte có thể lu trữ đợc một số nguyên và các vị trí này tơng ứng là a[0][0],a[0][1], , a[0][9], a[1][0], a[1][1], , a[1][9], , a[7][0], a[7][1], , a[7][9]
Chú ý: Khi ta khai báo một mảng có kích cỡ định trớc, thí dụ
int a[10]; thì a là một hằng địa chỉ, do đó ta không thể gán a = <địachỉ> Tuy nhiên nếu ta định nghĩa một biến con trỏ, thí dụ int *pn; thì ta
có thể dùng phép gán cho pn Với một chuỗi
char *s="123"; chẳng hạn, ta có s là địa chỉ của biến đầu tiên của chuỗi,tức là s[0], do đó *s chính là giá trị của s[0], còn *(s+1) là giá trị củas[1],
2.6 Định nghĩa kiểu bằng typedef
Các khối lệnh lồng nhau, phạm vi hoạt động của các biến:
Khối lệnh là tập hợp các lệnh đợc bao trong hai dấu { } Khối lệnh có thể
Trang 19gồm nhiều khối lệnh khác Một biến đợc khai báo trong khối lệnh thì miền tácdụng của nó là khối lệnh trong cùng chứa khai báo của biến đó Nếu có cácbiến cùng tên thì biến đợc khai báo sau cùng sẽ là biến tích cực, tức là thực sựtham gia trong mọi phép toán có tên biến chung
2 Phạm vi hoạt động và thời gian tồn tại
Biến tự động chỉ tồn tại trong khối lệnh mà nó đợc định nghĩa
3.Khởi đầu cho biến và mảng tự động
Biến ngoài (toàn cục) khi khai báo thì đợc tự động gán bằng 0 hoặc rỗng.Nhng với biến tự động khi khai báo xong mà không gán giá trị khởi đầu hoặcgán giá trị thì giá trị không xác định, là một giá trị vô nghĩa nào đó
Khác với C, trong C++ có thể khởi đầu cho mảng hoặc biến, thí dụ
Trang 202 Phạm vi hoạt động và thời gian tồn tại
Biến ngoài tồn tại từ vị trí đợc khai báo cho đến hết chơng trình
đó thì biến luôn đợc hiểu là biến địa phơng
Ví dụ sau minh họa cho điều này:
Trang 213 Khởi đầu cho biến và mảng ngoài
Biến ngoài (toàn cục) khi khai báo thì đợc tự động gán bằng 0 hoặc rỗng.Quy tắc khai báo và khởi đầu cũng giống nh biến tự động Tuy nhiên với biến
tự động thì sau khi khai báo biến có thể nhận một giá trị vu vơ nào đó
Trang 22Chơng 3 Biểu thức
3.1 Khái niệm biểu thức(expression)
Biểu thức là một giá trị đợc tạo nên do sự kết hợp các phép toán với các hằng,biến, phần tử mảng, hàm Nh vậy bản thân các hằng, biến, phần tử mảng,hàm cũng là các biểu thức
| : Phép tuyển các bit, ví dụ 3 | 5 =7
^ : Phép tuyển có loại trừ, ví dụ 3 ^ 5 = 6
<< :Phép dịch trái (dịch dãy bit k vị trí về phía trái dấu phẩy, tức là nhân
số dịch chuyển với 2k ), ví dụ 11 << 2 = 44 (1011 -> 101100)
m<<k = m* 2k
>> :Phép dịch phải (dịch dãy bit k vị trí về phía phải dấu phẩy, tức là chia
số dịch chuyển cho 2k), ví dụ 11 >> 2 = 2 (1011 -> 10)
m>>k = m/ 2k
~ : Phép lấy phần bù (trong chuỗi bit 0 -> 1 và ngợc lại)
3.4 Các phép toán quan hệ và logic
Trang 233.8 BiÓu thøc ®iÒu kiÖn
Trang 24Chơng 4 Vào ra
4.1 Hàm printf
int printf(const char *dk, [danh sách các đối]);
Đối dk là con trỏ kiểu char chứa địa chỉ của chuỗi điều khiển
1 Chuỗi điều khiển gồm 3 loại ký tự:
- Các ký tự điều khiển nh: \n Xuống dòng, \t Dấu tab
- Các đặc tả chuyển dạng và tạo khuôn (gọi tắt là đặc tả)
- Các ký tự để hiển thị ra màn hình Đối với một số ký tự đặc biệt nh dấu ', '',
b Dấu trừ (-): Nếu có dấu - thì dồn trái, không có thì dồn phải
c pp chỉ nên dùng cho số thực pp là số chữ số sau dấu chấm thập phân,nếu không có thì đợc hiểu là pp=6
d Ký tự chuyển dạng: quy định dạng in ra của đối tợng, thí dụ d là sốnguyên, f là số thực, o là số bát phân, x là số thập lục phân
Bảng các ký tự chuyển dạng
Ký tự chuyển
Trang 25Trong đó giá trị 19.25 là giá trị của biến thực ns, ta viết:
printf("\nNang suat tang %5.2f%",ns);
Giả sử ta cần in ra
Năng suất tăng 19.25% trong năm 2000
Trong đó giá trị 19.25 là giá trị của biến thực ns, 2000 là giá trị của biếnnguyên nam có thể thay đổi tuỳ thuộc từng trờng hợp, nếu ta viết:
printf("\nNang suat tang %5.2f% trong nam %4d",ns,nam);
thì về mặt cú pháp là đúng nhng lệnh không in ra điều ta mong muốn.Trong trờng hợp này ta nên tách làm 2 lệnh:
printf("\nNang suat tang %5.2f%",ns);
printf(" trong nam %4d",nam);
2 Danh sách các đối
Các đối cần đợc phân cách nhau bởi dấu phẩy Đối có thể là hằng, biến, hàmhay là kết hợp của chúng bằng các phép toán Vậy các đối chính là các biểuthức Nói chung có bao nhiêu đặc tả thì có bấy nhiêu đối Nếu số đối nhiềuhơn số đặc tả thì các đối không có đặc tả tơng ứng sẽ bị bỏ qua Còn nếu
số đối ít hơn số đặc tả thì máy sẽ bị rối và có thể dẫn đến những kết quảsai
4.2 Hàm scanf
int scanf(const char *dk, [danh sách các đối]);
1 Danh sách các đối
Các đối cần đợc phân cách nhau bởi dấu phẩy Đối phải là một con trỏ chứa
địa chỉ của một biến nào đó Vì tên của chuỗi ký tự là địa chỉ nên khôngcần dấu lấy địa chỉ đứng trớc tên chuỗi
2 Chuỗi điều khiển
Gồm các ký tự đặc tả chuyển dạng Mỗi đặc tả thờng có một đối tơng ứng
phần đặc tả có dạng tổng quát nh sau:
Trang 26Nói chung đặc tả chỉ gồm ký tự % và đặc tả chuyển dạng đợc thống kêtrong bảng sau:
Bảng các ký tự chuyển dạng
Ký tự chuyển
f hoặc e float Số dấu phẩy động
lf hoặc le double Số dấu phẩy động
g (Không có) Không có dạng này
Chú ý: Ký tự chuyển dạng của số nguyên không âm hệ 8 là chữ o, số nguyên không âm hệ 16 là ox; nhng biểu diễn số nguyên không âm hệ 8 bắt đầu bằng số 0, còn số hệ 16 thì bắt đầu bằng số 0 và chữ x.
Các dòng nghiêng và đậm chính là sự khác biệt giữa hàm scanf và printf
Ta có thể nhập các trờng vào liên tiếp nhau, sao cho mỗi trờng cách nhau ítnhất một khoảng trắng Trong đó khoảtng trắng đợc hiểu là dấu cách, dấutab hoặc dấu xuống dòng Nh vậy giữa các trờng vào có thể chứa một sốkhoảng trắng tuỳ ý Nh vậy ta không thể nhập một chuỗi ký tự có chứakhoảng trắng
Ví dụ về hàm scanf:
Trang 27int n;float a;double b;char st[5]
scanf("%d%f%lf%s",&n,&a,&b,st);
Nh vậy có thể thấy sự khác biệt của chuỗi điều khiển trong hàm printf và hàm scanf là: chuỗi điều khiển trong hàm printf có thể coá 3 thành phần: các ký tự điều khiển, các ký tự đặc tả và các ký tự hiện trên màn hình; còn trong lệnh scanf thì chỉ có các ký tự đặc tả.
4.3 Đa ra máy in- hàm fprintf
int fprintf(stdprn,const char *dk, [danh sách các đối]);
Tất cả những điều đã nói ở lệnh printf đều đúng Chỉ có một điểm khácbiệt: trong lệnh printf thiết bị ra là màn hình còn với lệnh fprintf thì thiết bị
ra là máy in
4.4 Dòng vào stdin và các hàm nhập dữ liệu
Trớc hết ta hãy chạy thử đoạn chơng trình đơn giản sau:
Nếu ta gõ a thì lệnh scanf thứ 2 bị bỏ qua
Lệnh printf tiếp theo chỉ hiện lên màn hình ký tự
Predefined streams automatically opened when the program is started.
Name Meaning stdin
stdout stderr stdaux stdprn
Standard input device Standard output device Standard error output device Standard auxiliary device Standard printer
Khi gặp một lệnh nhập số liệu và ta bắt đầu gõ từ bàn phím thì các ký tự
đợc gửi vào stdin Chơng trình sẽ chờ cho đến khi ta gõ phím báo hiệukết thúc lệnh nhập số liệu thì chơng trình bắt đầu đọc các ký tự từ stdin
để gán cho các trờng nhập Nếu số liệu trong stdin cha đủ thì chơng trìnhlại dừng để ta nhập tiếp Còn nếu số liệu có nhiều hơn thì chơng trình chỉlấy vừa đủ để gán cho các trờng vào, phần còn lại vẫn còn lại trong stdin Khigặp lệnh nhập số liệu tiếp theo thì trớc hết chơng trình xem trong stdin có
Trang 28số liệu không, nếu có đủ số liệu thì chơng trình lấy ngay các số liệu đó đểgán cho trờng vào mới và lệnh nhập số liệu đó bị bỏ qua Ta có cảm giác làlệnh không đợc thực hiện nhng thực ra đã đợc thực hiện nhng có thể không
nh ý ta muốn
Trở lại đoạn chơng trình trên đây Khi nhập số liệu cho ca ta đã gõ a vànhấn thì cả ký tự a và ký tự (\n, LF có mã là 10) đều đợc đa vàostdin Lệnh scanf đọc ký tự a từ stdin nhng không loại ký tự \n ra khỏi stdin
Do vậy khi tới lệnh scanf tiếp theo thì chơng trình lấy ngay giá trị \n để gáncho cb mà không cần chờ ngời sử dụng nhập số liệu mới Khi hiện ra mànhình thì ký tự \n không nhìn thấy do đó ta chỉ thấy chữa a mà thôi Nếubây giờ ta sửa lại lệnh printf thành
ta nhập tiếp dữ liệu cho lệnh scanf tiếp theo
Trang 29int getc(FILE *stream);
int putc(int c, FILE *stream);
int getch(void);//Nhận ký tự trực tiếp từ bàn phím, ký tự không hiện trên màn hình
int getche(void); //Nhận ký tự trực tiếp từ bàn phím, ký tự hiện trên màn hình
int putch(int ch); //Màu tuỳ thuộc vào lệnh textcolor
int getchar(void);//Nhận ký tự từ stdin
int putchar(int c); //Luôn hiện màu đen trắng
char *gets(char *s);
int puts(const char *s);
Các hàm scanf, cin chỉ nhập đợc chuỗi không có dấu cách.
Hàm gets nhập đợc chuỗi chứa dấu cách: Nhận dãy ký tự từ stdin cho đến
khi gặp ký tự '\n' Ký tự '\n' bị loại khỏi stdin nhng không đợc đặt vào cuối chuỗi Chuỗi đợc bổ sung ký tự kết thúc '\0'
Các hàm printf, cout và puts có thể hiện đợc các chuỗi có chứa dấu cách
4.6 Một số hàm xử lý chuỗi
strcmp(st1,st2)>0 nghĩa là st1>st2, phân biệt chữ hoa chữ thờng
strcmpi(st1,st2)>0 nghĩa là st1>st2, không phân biệt chữ hoa chữ thờngstrcat(st1,st2) ghép st2 vào st1
char* strchr(char *s, int kt);
Tìm lần xuất hiện đầu tiên của kt trong s Nếu tìm thấy trả về địa chỉ của
ký tự tìm đợc, nếu không trả về NULL
strcpy(st1,st2); thục chất là gán st1=st2;
strlen(st); cho độ dài chuỗi st.strlwr(st) chuyển thành chữ thờng (giốngtolower(ch) đối với ký tự)
strupr(st);
4.7 Sự khác biệt giữa mảng và chuỗi
Trớc hết ta chạy thử đoạn chơng trình sau đây
Trang 30Đây là điểm khác biệt giữa chuỗi và mảng Nếu với mảng ta chỉ có thểnhập số liệu và cho hiện từng phần tử của mảng thì nếu ta cũng đòi hỏi
nh vậy với chuỗi thì sẽ làm cho công việc lập trình rất rắc rối Chuỗi có thểxem nh mảng, nhng đồng thời phải đợc truy xuất nh một phần tử Khôngthể thao tác với tên ngời, tên địa danh mà coi nh hợp thành của nhiều phần
tử, mỗi phần tử là một ký tự đợc Chính vì vậy các nhà sáng tạo ngôn ngữ
C đã chọn một trong 2 ký hiệu &b và b một ký hiệu dùng để thao táctoàn bộ chuỗi Vì vậy khi dùng lệnh
scanf("%s",b); ta có thể nhập toàn bộ chuỗi
còn lệnh
printf("%s",b); sẽ in toàn bộ chuỗi
Nếu b là mảng thì các lệnh trên đây không thực hiện đợc và máy sẽ báolỗi
Ta có thể khởi đầu các giá trị cho chuỗi ký tự nh ví dụ sau
Trang 314.8 Các hàm vào ra và dịch chuyển vị trí trên màn hình
Hàm gotoxy(cm,rn); sẽ đa con trỏ đến vị trí ở cột thứ cm và hàng thứ rntrên màn hình
Hàm kbhit() nhận giá trị 1 nếu có phím đợc bấm
4.9 Các luồng nhập xuất cin,cout
C++ cung cấp các dòng (stream đợc chứa trong iostream.h) cin và cout đểnhập xuất số liệu Sau đây là vài ví dụ:
Trang 32Ch¬ng 5 C¸c to¸n tö ®iÒu khiÓn
5.1 Nh¾c l¹i kh¸i niÖm c©u lÖnh vµ khèi lÖnh
Trang 331 Nếu có [các lệnh A] thì thực hiện các lệnh này Nếu phần này gồmnhiều lệnh thì các lệnh cách nhau bằng dấu , (chứ không phải là dấu ;), ví
dụ các lệnh A có thể là int i=1, int j=2; Sau khi thực hiện các lệnh A thìchuyển sang bớc thứ 2 sau đây
2 Xác định [biểu thức B], nếu biểu thức này sai thì kết thúc lệnh for
tức là chuyển sang bớc 4, nếu B đúng thì thực hiện [lệnh D] sau đóchuyển sang bớc 3 sau đây
3 Thực hiện các lệnh C, (thông thờng các lệnh này làm thay đổi giá trị biểuthức B), trở lại bớc 2 Ví dụ có thể là i++, hoặc i++, j++
4 Kết thúc lệnh for, tức là chuyển sang thực hiện [lệnh E] ngay sau lệnh for
Nh vậy nếu [biểu thức B] luôn luôn đúng thì vòng lặp sẽ vô hạn Trong trờnghợp [biểu thức B] vắng mặt chẳng hạn, thì đợc hiểu là biểu thức B luônluôn đúng Trong phần B có thể không phải là một biểu thức duy nhất, mà cóthể có các biểu thức cách nhau bằng dấu , ví dụ i<10, j<20 Lúc này biểuthức B đợc hiểu là i<10 && j<20
Trang 35Chơng 6 Hàm và cấu trúc chơng trình 6.1 Khai báo và định nghĩa hàm
Tơng tự nh một biến, một hàm phải khai báo trớc khi nó đợc gọi Một khai báohàm có 3 phần: Kiểu cho ra, tên và danh sách tham số
Hàm có vai trò ngang nhau, C++ không cho phép xây dựng một hàm bên trong hàm khác
Ví dụ: Khai báo hàm:
double power(double x, int n);
Trong đó double là kiểu cho ra, power là tên của hàm và (double x,int n) làdanh sách các tham số Hàm này có 2 tham số là x và n Các tham số này còn
đợc gọi là các tham số hình thức Danh sách các tham số có thể trỗng, nhngcác dấu ngoặc đơn vẫn cần thiết Một khai báo hàm còn đợc gọi là cácnguyên mẫu hàm (prototype) Phần này chứa các thông tin tối thiểu mà bộ biêndịch cần để biên dịch các lệnh gọi đến hàm Vì vậy trong phần khai báo cóthể bỏ bớt tên các tham số chẳng hạn nh:
double max(double,double);
Tuy nhiên nếu biến là mảng thì không thể bỏ tên biến đợc Ta chỉ có thểviết một cách đơn giản hơn, ví dụ
int x[],float y[][10]
chẳng hạn Lu ý là với mảng 2 chiều phải có thông tin về số cột
Phần khai báo phải ở vị trí trớc lời gọi hàm trong chơng trình Tuy nhiên
nh ta có thể thấy, phần khai báo cha đủ để xác định một hàm Hàm còn cóphần định nghĩa nằm ở vị trí bất kỳ trong chơng trình (tất nhiên là khôngphải bên trong một hàm khác)
Nếu định nghĩa hàm nằm trớc lời gọi nó thì không cần khai báo mẫu nữa
Trong thân hàm có thể khai báo các biến cục bộ Các biến này không đợc
trùng tên với các tham số Trong thân hàm ta có thể sử dụng các tham số mà
không khai báo lại Nếu ta khai báo lại thì chơng trình sẽ báo lỗi
Trong phần định nghĩa hàm không đợc bỏ tên các tham số nh trong phầnkhai báo
Nếu hàm có kiểu cho ra thì cuối thân hàm phải có lệnh return <biểuthức>; trong đó <biểu thức> có cùng kiểu giá trị với hàm Trong thân hàm
có thể có một hoặc nhiều lệnh return <biểu thức>; Khi chơng trình gặp
Lập trình C và C++ - Chơng 5 Các toán tử điều khiển
Trang 36lệnh này thì giá trị hàm đợc trả về và chơng trình thoát khỏi hàm.
Tên hàm cũng là một identifier, do đó không đợc đặt tên hàm trùng với tênbiến hoặc ngợc lại
Khi ta khai báo tham số là mảng một chiều hay con trỏ thì khi gọi có thểgọi tham số thực là con trỏ hoặc mảng Thí dụ khai báo char st[]; vàchar *st; là hoàn toàn tơng đơng Tuy nhiên với mảng 2 chiều thì không
còn đúng nữa Thí dụ đoạn chơng trình sau đây là không hợp lệ:
ng lệnh này nếu có thì chỉ có ở dạng đơn giản là return; khi chơng trìnhgặp lệnh này thì chơng trình thoát khỏi hàm Nếu không có lệnh returnthì chơng trình chạy đến hết thân hàm rồi mới thoát khỏi hàm
Ví dụ:
Trang 37Bây giờ ta xét một ví dụ là in ra ngày trong tuần từ thứ 2 đến thứ 7 bằngtiếng Anh khi cho ngày bằng số, thí dụ 2 là thứ 2, 3 là thứ 3, và viết hàmbằng 2 cách: có kiểu cho ra và kiểu cho ra là void.
Cách 1:
char* weekday(int n)
{switch(n)
{case 2: return "Monday";
case 3: return "Tuesday";
case 4: return "Wednesday";
case 5: return "Thursday";
case 6: return "Friday";
case 7: return "Saturday";
case 3: printf("\n%s","Tuesday"); break;
case 4: printf("\n%s","Wednesday"); break;
case 5: printf("\n%s","Thursday"); break;
case 6: printf("\n%s","Friday"); break;
case 7: printf("\n%s","Saturday"); break;
t=max(a,b);
6.4 Sử dụng hàm assert() để kiểm tra điều kiện trớc
Hầu hết các hàm không thực thi tốt trên tất cả các giá trị của các tham số Thí
dụ hàm double power(double x,int n) mà chúng ta sẽ xét tới sau đây chỉ cónghĩa với x>0 Đối với các hàm có trả về giá trị khác với kiểu void thì đôi khirất khó để tìm một kiểu ra tơng ứng với các trờng hợp không thích hợp Trongcác trờng hợp này ta có thể dùng hàm assert() đợc định nghĩa trong assert.h
để kiểm tra trớc các điều kiện Khi điều kiện đó không thỏa mãn thì chơngtrình dừng thực hiện hàm và kèm lời thông báo
Trang 38Ví dụ:
double power(double x,int n)
{assert(x>0);
double y=1.0;int m=abs(n);//Nếu là double thì dùng fabs()
for(int i=0;i<m;i++) y*=x;
if(n<0) y=1/y;
return y;
}
6.5 Các đối số mặc định
Nếu ta sửa lại hàm nh sau
double power(double x=1.0,int n=2)
{assert(x>0);
double y=1.0;int m=abs(n);//Nếu là double thì dùng fabs()
for(int i=0;i<m;i++) y*=x;
6.6 Truyền tham số cho hàm
1 Truyền bằng tham trị (value parameter)
(Chú ý rằng tên mảng là con trỏ do đó hàm có tham số là mảng luôn đợc tự
động truyền theo địa chỉ Thậm chí nếu ta viết void vv(double &a[2][3])chẳng hạn thì máy sẽ báo lỗi Tuy nhiên nếu ta đã định nghĩa trớc typedefdouble kmatran[2][3]; thì lại có thể viết
void vv(kmtran &a);)
Trang 39x lại đợc hiểu là biến trớc khi gọi hàm.
Truyền bằng tham trị hằng (const value parameter):
Nếu hàm f1 đợc sửa lại nh sau:
void f1(const int x)
Nghĩa là sau khi ra khỏi chơng trình thì x lấy giá trị đã thay đổi trong
ch-ơng trình con Đây là cách truyền tham số theo địa chỉ Qua cách truyềnnày thì chơng trình con làm việc trực tiếp với giá trị mà tham số địa chỉtrỏ tới, tức là giá trị thực sự của biến Do vậy nếu trong hàm giá trị biến bịthay đổi thì khi thoát khỏi hàm những sự thay đổi đó vẫn đợc giữ lại Khitruyền tham số theo địa chỉ ta cần chú ý tới những điểm sau:
Mảng chỉ có thể truyền cho hàm qua địa chỉ, do đó đối với dữ liệu kiểu
Trang 40mảng thì mọi sự thay đổi trong hàm vẫn còn tác dụng khi ra khỏi hàm.
Có thể dùng 2 cách khai báo biến <kiểu dữ liệu>* <tên biến con trỏ>hoặc <kiểu dữ liệu> *<tên biến con trỏ> (thí dụ int* pi hoặc int *pi,trong đó pi là con trỏ kiểu int) Cả 2 cách này hoàn toàn tơng đơng Cáchthứ nhất thờng đợc dùng khi khai báo nguyên mẫu hàm, vì khi đó ta chỉkhai báo kiểu của biến, cha cần khai náo tên
Con trỏ không cần phải khởi tạo khi khai báo nhng phải khởi tạo trớc khi sửdụng
Không đợc gán giá trị khác kiểu cho con trỏ Thí dụ trong hàm trên đây takhông thể gọi f1(2); hoặc f1(&(x+1));
3 Truyền bằng tham chiếu (reference parameter):
Trong C++ ngời ta còn đa thêm khái niệm biến tham chiếu Có thể xem biếntham chiếu là một bớc tiến của con trỏ Ta hãy xét đoạn chơng trình sau:
nh biến i Do đó mọi sự thay đổi của i đều tơng đơng với thay đổi trên j
và ngợc lại Vậy ta có thể hình dung một biến tham chiếu giống nh bí danhcủa biến khác Ta có mấy điều chú ý sau đây về biến tham chiếu:
Biến tham chiếu cũng chứa địa chỉ của một hực thể nh các biến khác
Biến tham chiếu cần phải đợc khởi tạo khi khai báo Thí dụ nếu ta khai báoint& i; thì máy sẽ báo lỗi Điều này cũng dễ hiễu, vì biến tham chiếu làbiến "ăn theo", do đó phải đợc khởi tạo khi khai báo Tốt nhất là ta khởi tạobiến tham chiếu theo một biến khác đã có Còn nếu ta khởi tạo bằng mộthằng, thí dụ int &m=2; thì máy không báo lỗi nhng có dòng cảnh báoTemporary used to initialize 'm'
Không cấp phát bộ nhớ động cho biến tham chiếu
Không dùng các phép toán nh tăng hoặc giảm địa chỉ đối với biến thamchiếu
Mặc dù bản chất của biến tham chiếu là con trỏ, nhng ta có thể sử dụng tênbiến tham chiếu để truy nhập đến giá trị của nó Đây là điểm khác biệtgiữa biến thuộc kiểu tham chiếu và biến kiểu con trỏ Nếu trong đoạn chơngtrình trên đay ta sửa lại là
const int i=10;
int& j=i;
j=20;
printf("\n%d",i);