Giáo trình Ngôn ngữ lập trình C++
Mục lục
Giới thiệu.6
Chương 1. Mở đầu .8
1.1. Chương trình là gì?.8
1.2. Lập trình là gì? .8
1.2.1. Mức cao độc lập với máy tính .8
1.2.2. Mức thấp phụthuộc vào máy tính .10
1.3. Ngôn ngữlập trình và chương trình dịch .10
1.4. Môi trường lập trình bậc cao.11
1.5. Lỗi và tìm lỗi.13
1.6. Lịch sửC và C++ .14
1.7. Chương trình C++ đầu tiên .15
Bài tập .19
Chương 2. Biến, kiểu dữliệu và các phép toán .20
2.1. Kiểu dữliệu.22
2.1.1. Kiểu dữliệu cơbản .22
2.1.2. Kiểu dữliệu dẫn xuất .24
2.2. Khai báo và sửdụng biến .24
2.2.1. Định danh và cách đặt tên biến .24
2.2.2. Khai báo biến .25
2.3. Hằng.25
2.4. Các phép toán cơbản.26
2.4.1. Phép gán.26
2.4.2. Các phép toán sốhọc.26
2.4.3. Các phép toán quan hệ.27
2
2.4.4. Các phép toán lô-gic.28
2.4.5. Độ ưu tiên của các phép toán.28
2.4.6. Tương thích giữa các kiểu .29
Bài tập .30
Chương 3. Các cấu trúc điều khiển .32
3.1. Luồng điều khiển.32
3.2. Các cấu trúc rẽnhánh .33
3.2.1. Lệnh if-else .33
3.2.2. Lệnh switch .39
3.3. Các cấu trúc lặp .43
3.3.1. Vòng while .43
3.3.2. Vòng do-while .46
3.3.3. Vòng for .49
3.4. Các lệnh breakvà continue .54
3.5. Biểu thức điều kiện trong các cấu trúc điều khiển.57
Bài tập .59
Chương 4. Hàm.61
4.1. Các hàm có sẵn.62
4.2. Cấu trúc chung của hàm .63
4.3. Cách sửdụng hàm .64
4.4. Biến toàn cục và biến địa phương .65
4.4.1. Phạm vi của biến .65
4.4.2. Thời gian sống của biến.67
4.5. Tham số, đối số, và cơchếtruyền tham sốcho hàm .68
4.5.1. Truyền giá trị .68
3
4.5.2. Truyền tham chiếu.69
4.5.3. Tham sốmặc định .72
4.6. Hàm trùng tên.74
4.7. Hàm đệquy .76
Bài tập .78
Chương 5. Mảng .80
5.1. Mảng một chiều.80
5.1.1. Khai báo và khởi tạo mảng .81
5.1.2. Ứng dụng của mảng .83
5.1.3. Trách nhiệm kiểm soát tính hợp lệcủa chỉsốmảng.85
5.1.4. Mảng làm tham sốcho hàm.85
5.2. Mảng nhiều chiều .86
5.3. Mảng và xâu kí tự .88
5.3.1. Khởi tạo giá trịcho xâu kí tự .90
5.3.2. Thưviện xửlý xâu kí tự .90
5.4. Tìm kiếm và sắp xếp dữliệu trong mảng .91
5.4.1. Tìm kiếm tuyến tính .91
5.4.2. Tìm kiếm nhịphân .92
5.4.3. Sắp xếp chọn .94
Bài tập .96
Chương 6. Con trỏvà bộnhớ .99
6.1. Bộnhớmáy tính .99
6.2. Biến và địa chỉcủa biến.99
6.3. Biến con trỏ .100
6.4. Mảng và con trỏ.105
4
6.5. Bộnhớ động .107
6.5.1. Cấp phát bộnhớ động.107
6.5.2. Giải phóng bộnhớ động .108
6.6. Mảng động và con trỏ .109
6.7. Truyền tham sốlà con trỏ .111
Bài tập .115
Chương 7. Các kiểu dữliệu trừu tượng .118
7.1. Định nghĩa kiểu dữliệu trừu tượng bằng cấu trúc struct .118
7.2. Định nghĩa kiểu dữliệu trừu tượng bằng cấu trúc class .124
7.2.1. Quyền truy nhập .127
7.2.2. Toán tửphạm vi và định nghĩa các hàm thành viên .128
7.2.3. Hàm khởi tạo và hàm hủy.129
7.3. Lợi ích của lập trình hướng đối tượng.132
7.4. Biên dịch riêng rẽ .133
Bài tập .137
Chương 8. Vào ra dữliệu.140
8.1. Khái niệm dòng dữliệu .140
8.2. Tệp văn bản và tệp nhịphân .141
8.3. Vào ra tệp .141
8.3.1. Mởtệp.142
8.3.2. Đóng tệp.143
8.3.3. Xửlý tệp văn bản .144
8.3.4. Xửlý tệp nhịphân .147
Bài tập .151
Phụlục A. Phong cách lập trình .153
5
Phụlục B. Dịch chương trình C++ bằng GNU C++.157
Phụlục C. Xửlý xâu bằng thưviện cstring.160
Tài liệu tham khảo .162
biến Tên biến nên dễ đọc, và gợi nhớ đến công dụng của biến hay kiểu dữ liệu mà biến sẽ lưu trữ. Đối với những biến gồm nhiều từ, thì các từ nên viết liền nhau và chữ cái đầu tiên của các từ phía sau nên được viết hoa. Ví dụ numberStudents, savingAccount Các biến hằng nên viết hoa. Nếu biến hằng gồm nhiều từ, thì các từ nên chia cách bằng dấu gạch chân. Ví dụ 155 PI, NUMBER_ITERATION A.5. Cách đặt tên hàm Tên hàm nên dễ đọc, và gợi nhớ đến mục đích của hàm. Tên hàm nên bắt đầu bằng một động từ. Đối với những hàm gồm nhiều từ, thì các từ nên viết liền nhau và chữ cái đầu tiên của các từ phía sau nên được viết hoa. Ví dụ getSum, getMin, calculateScores A.6. Biểu thức Các biểu thức cần viết đơn giản, gắn gọn và dễ hiểu. Các biểu thức dài có thể tách nhỏ ra sử dụng các biến trung gian. Nên sử dụng các cặp dấu ngoặc để trách sự nhập nhằng và nhầm lẫn về thứ tự thực hiện các phép toán trong biểu thức. Ví dụ: sum = a * b + a * b * c – d/e Có thể viết thành ab = a * b; sum = ab * (c + 1) – (d/e); Lưu ý: Biểu thức điều kiện cũng nên tuân thủ theo nguyên tắc trên. A.7. Vòng lặp Không nên sử dụng nhiều các lệnh nhảy như break, hay continue để thoát ra khỏi vòng lặp. Với mỗi vòng lặp, nên xác định rõ ràng điều kiện để vòng lặp kết thúc. A.8. Khối chương trình Các đoạn chương trình cần được trình bày theo thành từng khối (sử dụng các dấu cách). Việc trình bày theo khối sẽ giúp chúng ta dễ dàng hiểu được cấu trúc và thứ tự thực hiện các lệnh. Ví dụ: 156 while (!done) { doSomething (); done = check (); } A.9. Lớp Mỗi lớp (class) nên tách ra thành hai tệp riêng biệt. Tệp header (.h) chứa khai báo về lớp, còn tệp nguồn (.cpp) chứa định nghĩa về các phương thức của lớp. Việc tách này sẽ dễ dàng cho chúng ta trong việc theo dõi, tìm hiểu và phát triển chương trình. Ví dụ: time.h, time.cpp 157 Phụ lục B. Dịch chương trình C++ bằng GNU C++ B.1. Dịch và liên kết Quá trình tạo tệp thực thi được từ các tệp mã nguồn có hai bước. Bước 1: biên dịch các tệp mã nguồn thành các tệp mã object. Các tệp có mở rộng .cpp hoặc .cc được dịch thành các tệp có mở rộng .o. Bước 2: liên kết các tệp mã object thành một tệp thực thi được. Các tệp có mở rộng .o được liên kết với nhau thành một tập thực thi được (đuôi .exe trong môi trường DOS/Windows). Nếu chương trình chỉ gồm một tệp mã nguồn, ví dụ count.cpp, ta chỉ cần chạy lệnh g++ -o count.exe count.cpp Kết quả là một tệp thực thi được có tên count.exe. Hoặc ta chỉ dùng lệnh g++ count.cpp Kết quả là một tệp thực thi được có tên mặc định (a.out trong môi trường Unix/Linux). Nếu chương trình bao gồm nhiều tệp mã nguồn, chẳng hạn time_test.cpp (chứa hàm main), time.cpp, time.h, ta dịch từng tệp có đuôi cpp hoặc cc bằng lệnh sau g++ -c file.cpp Sau đó liên kết lại bằng lệnh g++ time_test.o time.o -o time_test.exe B.2. Tiện ích make của GNU Ta có biên dịch bằng cách mỗi lần lại gõ từng lệnh như hướng dẫn ở trên. Tuy nhiên, với những chương trình lớn bao gồm nhiều tệp mã nguồn, dùng đến nhiều thư viện, việc gõ từng lệnh bằng tay mỗi khi cần dịch chương trình không phải là cách làm hiệu quả. Tiện ích make trong bộ công cụ GNU cho phép tự động hóa quy trình dịch và liên kết chương trình. make là một hệ thống được thiết kế để xây dựng các chương trình từ các cây mã nguồn lớn. Từ đặc tả của lập trình viên về cây mã nguồn, tiện ích make sẽ gọi trình biên dịch và liên kết 158 một cách hiệu quả nhất để xây dựng chương trình thực thi được. Mục này là một hướng dẫn tối giản cho việc sử dụng tiện ích make. Để dùng tiện ích make cho một chương trình, lập trình viên phải tạo một tệp có tên Makefile nằm trong cùng thư mục với các tệp mã nguồn. Tệp này chứa các lệnh hướng dẫn make làm gì và làm thế nào để xây dựng được chương trình. Makefile gồm các bộ đặc tả, mỗi bộ quản lý việc cập nhật một tệp. Mỗi bộ đặc tả của một tệp Makefile gồm 3 phần: đích (target), các quan hệ phụ thuộc (dependency), và các chỉ thị (instruction). Công thức như sau: TargetFile: DependencyFile1 DependencyFile2 ... DependencyFilen trong đó, TargetFile là tệp cần cập nhật và mỗi DependencyFilei là một tệp mà TargetFile phụ thuộc vào nó. Dòng thứ hai của bộ đặc tả là một lệnh dịch TargetFile, lệnh này phải có một kí tự TAB đứng trước và kết thúc bằng kí tự xuống dòng. Ví dụ, bộ đặc tả đầu tiên trong Makefile của chúng ta như sau: time_test.exe: time.o time_test.o g++ time.o time_test.o -o time_test.exe trong đó dòng đầu chỉ ra rằng time_test.exe phụ thuộc hai tệp time.o và time_test.o, dòng thứ hai là lệnh dùng g++ dịch ra tệp time_test.exe. Tiếp theo, time.o không có sẵn ngay khi biên dịch lần đầu, cho nên ta phải viết bộ đặc tả cho tệp này: time.o: time.cc time.h g++ -c time.cc Tương tự là bộ đặc tả cho time_test.o time_test.o: time.cc time.h g++ -c time_test.cc Do đó, Makefile sẽ có dạng: time_test: time.o time_test.o g++ time.o time_test.o -o time_test time.o: time.cc time.h g++ -c time.cc time_test.o: time.cc time.h g++ -c time_test.cc Và khi ta gõ từ dấu nhắc dòng lệnh 159 make chương trình make sẽ đọc Makefile và thực hiện các công việc sau: 1. thấy rằng time_test.exe phụ thuộc vào time.o, và: a) Kiểm tra time.o, thấy nó phụ thuộc time.cc và time.h; b) Xác định xem time.o đã lỗi thời chưa (cũ hơn bản mới nhất của các tệp time.cc và time.h) c) nếu đã lỗi thời thì chạy lệnh g++ -c time.cc để tạo time.o 2. thấy rằng time_test.exe cũng phụ thuộc vào time_test.o, và a) Kiểm tra time_test.o, thấy nó phụ thuộc time_test.cc và time.h; b) Xác định xem time_test.o đã lỗi thời chưa (chưa được dịch từ bản mới nhất của các tệp time_test.cc và time.h) c) nếu đã lỗi thời thì chạy lệnh g++ -c time_test.cc để tạo time_test.o 3. thấy rằng tất cả các tệp mà time_test.exe phụ thuộc đều đã được cập nhật, nên nó chạy lệnh g++ time.o time_test.o -o time_test để tạo chương trình time_test.exe. 160 Phụ lục C. Xử lý xâu bằng thư viện cstring C.1. Một số hàm xử lý xâu thông dụng char *strcpy( char *d, const char *s ); Sao chép xâu s vào mảng char d. Trả về địa chỉ của xâu d. char *strncpy( char *d, const char *s, size_t n ); Chép tối đa n kí tự từ xâu s vào mảng char d. Trả về địa chỉ mảng d. char *strcat( char *d, const char *s ); Chép xâu s vào cuối xâu d. Kí tự đầu tiên của xâu s sẽ đè lên kí tự null cuối xâu d. Trả về địa chỉ của xâu d. char *strncat( char *d, const char *s, size_t n ); Chép tối đa n kí tự của xâu s vào cuối xâu d. Kí tự đầu tiên của xâu s sẽ đè lên kí tự null cuối xâu d. Trả về địa chỉ của xâu d. int strcmp( const char *s1, const char *s2 ); So sánh xâu s1 và xâu s2. Trả về một giá trị bằng 0 nếu s1 bằng s2, nhỏ hơn 0 (thường là -1) nếu s1 nhỏ hơn s2, hoặc lớn hơn 0 (thường là 1) nếu s1 lớn hơn s2 theo thứ tự từ điển. int strncmp( const char *s1, const char *s2, size_t n ); Tương tự hàm strcmp() nhưng chỉ so sánh n kí tự đầu tiên của xâu s1 với n kí tự đầu tiên của xâu s2. char *strtok( char *s1, const char *s2 ); Một chuỗi các lời gọi hàm strtok có tác dụng tác xâu s1 thành các mảnh ("token"), chẳng hạn các từ trong một dòng văn bản. Xâu kí tự được tách dựa theo các kí tự chứa trong xâu s2. Ví dụ, nếu ta cần tách xâu "Hello, how are you?" thành các từ, với ' ', '?', ',' là các kí tự ngăn cách giữa các từ, kết quả sẽ là các token "Hello", "how", "are", "you". Tuy nhiên, mỗi lần gọi hàm strtok chỉ tách được một token và trả về địa chỉ của token đó. Khi không còn tìm thấy token nào, hàm sẽ trả về giá trị NULL. Lần gọi đầu tiên trong quá trình phân tách xâu s1 sẽ cần tham số đầu tiên là s1, các lần gọi sau sẽ tiếp tục tách s1 nếu tham số đầu tiên là NULL. size_t strlen( const char *s ); Trả về độ dài của xâu s – là số kí tự đứng trước kí tự null. 161 C.2. Khuyến cáo về việc sử dụng thư viện cstring Một số hàm trong thư viện cstring nếu sử dụng không cẩn thận có thể dẫn đến việc truy nhập ra ngoài không gian đã được khai báo của mảng hoặc tình trạng xâu kí tự không được kết thúc đúng cách bằng kí tự null. Đối với những hàm này, lập trình viên phải sử dụng một cách cẩn trọng để đảm bảo không gây ra lỗi dữ liệu. Mục này đưa ra một số khuyến cáo về việc sử dụng các hàm này. Các hàm strcpy và strncpy sao chép toàn bộ xâu kí tự là tham số thứ hai vào mảng char đích mà không quan tâm không gian khai báo của mảng đích có đủ chỗ chứa hay không. Lập trình viên phải tự đảm bảo rằng mảng đích có đủ chỗ cho xâu nguồn cũng như kí tự null kết thúc xâu đó. Một lời khuyên là nên dùng strncpy thay cho strcpy, vì strncpy cho phép kiểm soát được số lượng kí tự sẽ được chép vào mảng đích. Tương tự, các hàm strcat và strncat chép xâu kí tự là tham số thứ hai vào cuối xâu kí tự là tham số thứ nhất mà cũng không quan tâm không gian khai báo của mảng đích có đủ chỗ chứa cả hai xâu hay không. Lập trình viên phải tự đảm bảo rằng mảng đích có đủ chỗ cho cả hai xâu cũng như kí tự null kết thúc xâu thứ hai. strncat cũng được khuyên dùng hơn vì nó cho phép kiểm soát số lượng kí tự sẽ được chép vào mảng đích. Khi sử dụng strncpy, nếu số kí tự cần chép (tham số thứ ba) không lớn hơn độ dài của xâu nguồn (tham số thứ hai), thì kí tự null sẽ không được chép vào mảng đích. strncpy cũng không tự chèn thêm một kí tự null vào cuối đoạn vừa chép. Trong trường hợp này, nếu lập trình viên không tự gắn kí tự null đánh dấu kết thúc xâu kết quả, xâu này có thể trở thành xâu có độ dài không xác định với nguy cơ dẫn đến lỗi nghiêm trọng khi chạy chương trình. Tình trạng tương tự cũng xảy ra đối với hàm strncat. 162 Tài liệu tham khảo [1]. Bjarne Stroustrup, The C++ Programming Language, 3rd edition, Addison-Wesley, 1997. [2]. Deitel & Deitel, C++ How to Program, 5th edition, Prentice Hall, 2005. [3]. ISO/IEC JTC1/SC22/WG21. Stable release, ISO/IEC 14882:2003 (2003) [4]. Juan Soulie, C++ Language Tutorial,
File đính kèm:
- Giáo trình Ngôn ngữ lập trình C++.pdf