C++ toàn tập

Đây là ngôn ngữ rất phổ biến được nhiều người lựa chọn và nó có cơ hội việc làm thu nhập rất cao nếu bạn thông thạo nó. Vì vậy Blog đã viết một loạt bài về C++ với mục đích giúp các bạn tiện tra cứu và học hỏi.

Cơ bản:

Nhập, xuất dữ liệu từ file trong C++

Tất cả các chương trình mà ta đã gặp trong cuốn sách này đều lấy dữ liệu vào từ bàn phím và in ra màn hình. Nếu chỉ dùng bàn phím và màn hình là các thiết bị vào ra dữ liệu thì chương trình của ta khó có thể xử lý được khối lượng lớn dữ liệu, và kết quả chạy chương trình sẽ bị mất ngay khi ta đóng cửa sổ màn hình output hoặc tắt máy. Để cải thiện tình trạng này, ta có thể lưu dữ liệu  tại  các thiết bị lưu trữ thứ cấp mà thông dụng nhất thường là ổ đĩa cứng. Khi đó dữ liệu tạo bởi một chương trình có thể được lưu lại để sau này được sử dụng bởi chính nó hoặc các chương trình khác. Dữ liệu lưu trữ như vậy được đóng gói tại các thiết bị lưu trữ thành các cấu trúc dữ liệu gọi là tp (file).

Chương này sẽ giới thiệu về cách viết các chương trình lấy dữ liệu vào (từ bàn phím hoặc từ một tệp) và ghi dữ liệu ra (ra màn hình hoặc một  tệp).

Khái niệm dòng dữ liệu

Trong một số ngôn ngữ lập trình như C++ và Java, dữ liệu vào ra từ tệp, cũng  như từ bàn phím và màn hình, đều được vận hành thông qua các dòng dliu (stream). Ta có thể coi dòng dữ liệu là một kênh hoặc mạch dẫn  mà  dữ  liệu được truyền qua đó để chuyển từ nơi gửi đến nơi nhận.

Dữ liệu được truyền từ chương trình ra ngoài theo một dòng ra (output stream). Đó có thể là dòng ra chuẩn nối và đưa dữ liệu ra màn hình, hoặc dòng ra nối với một tệp và đẩy dữ liệu ra tệp đó.

Chương trình nhận dữ liệu vào qua một dòng vào (input stream). Dòng vào thì có thể là dòng vào chuẩn nối và đưa dữ liệu vào từ màn hình, hoặc dòng vào nối với một tệp và nhận dữ liệu vào từ tệp đó.

Dữ liệu vào và ra có thể là các kí tự, số, hoặc các byte chứa các chữ số nhị  phân.

Trong C++, các dòng vào ra được cài đặt bằng các đối tượng của các lớp dòng vào ra đặc biệt. Ví dụ, cout mà ta vẫn dùng để ghi ra màn hình chính là dòng ra chuẩn, còn cin là dòng vào chuẩn nối với bàn phím. Cả hai đều là các đối tượng dòng dữ liệu (khái niệm “đối tượng” này có liên quan đến tính năng hướng đối tượng của C++, khi nói về các dòng vào/ra của C++, ta sẽ phải đề cập nhiều đến tính năng này).

Tp văn bn và tp nhphân

Về bản chất, tất cả dữ liệu trong các tệp đều được lưu trữ dưới dạng một chuỗi các bit nhị phân 0 và 1. Tuy nhiên, trong một số hoàn cảnh, ta không  coi nội dung của một tệp là một chuỗi 0 và 1 mà coi tệp đó là một chuỗi các kí tự. Một   số tệp được xem như là các chuỗi kí tự và được xử lý bằng các dòng và hàm cho phép chương trình và hệ soạn thảo văn bản của bạn nhìn các chuỗi nhị phân như là các chuỗi kí tự. Chúng được gọi là các tp văn bn (text file). Những tệp không phải tệp văn bản là tp nhphân (binary file). Mỗi loại tệp được xử lý   bởi các dòng và hàm riêng.

Chương trình C++ của bạn được lưu trữ trong tệp văn bản. Các tệp ảnh và nhạc  là các tệp nhị phân. Do tệp văn bản là chuỗi kí tự, chúng thường trông giống nhau tại các máy khác nhau, nên ta có thể chép chúng  từ  máy này sang  máy khác mà không gặp hoặc gặp phải rất ít rắc rối. Nội dung của các tệp nhị phân thường lấy cơ sở là các giá trị số, nên việc sao chép chúng giữa các máy có thể gặp rắc rối do các máy khác nhau có thể dùng các quy cách lưu trữ số không giống nhau. Cấu trúc của một số dạng tệp nhị phân đã được chuẩn hóa để chúng có thể được sử dụng thống nhất tại các platform khác nhau. Nhiều dạng tệp ảnh và âm thanh thuộc diện này.

Mỗi kí tự trong một tệp văn bản được biểu diễn bằng 1 hoặc 2 byte, tùy theo đó  là kí tự ASCII hay Unicode. Khi một chương trình viết một giá trị vào một tệp văn bản, các kí tự được ghi ra tệp giống hệt như khi chúng được ghi ra màn hình bằng cách sử dụng cout. Ví dụ, hành động viết số 1 vào một tệp sẽ dẫn đến kết quả là 1 kí tự được ghi vào tệp, còn với số 1039582 là 7 kí tự được ghi vào  tệp.

Các tệp nhị phân lưu tất cả các giá trị thuộc một kiểu dữ liệu cơ bản theo cùng một cách, giống như cách dữ liệu được lưu trong bộ nhớ máy tính. Ví dụ, mỗi  giá trị int bất kì, 1 hay 1039582 đều chiếm một chuỗi 4 byte.

 Vào ra tệp

C++ cung cấp các lớp sau để thực hiện nhập và xuất dữ liệu đối với  tệp:

  • ofstream: lớp dành cho các dòng ghi dữ liệu ra tệp
  • ifstream: lớp dành cho các dòng đọc dữ liệu từ tệp
  • fstream: lớp dành cho các dòng vừa đọc vừa ghi dữ liệu ra tệp.

Đối tượng thuộc các lớp này do quan hệ thừa kế nên cách sử dụng chúng khá giống với cin và cout – các đối tượng thuộc lớp istream và ostream – mà chúng ta đã dùng. Khác biệt chỉ là ở chỗ ta phải nối các dòng đó với các  tệp.

c-p-yeulaptrinh.pw

Hình 8.1: Các thao tác cơ bn vi tp văn bn.

Chương trình trong Hình 8.1 tạo một tệp có tên hello.txt và ghi vào đó một câu “Hello!” theo cách mà ta thường làm đối với cout, chỉ khác ở chỗ thay cout bằng đối tượng dòng myfile đã được nối với một tệp. Sau đây là các bước thao tác với tệp.

  • Mở tệp

Việc đầu tiên là nối đối tượng dòng với một tệp, hay nói cách khác là mở một  tệp. Kết quả là đối tượng dòng sẽ đại diện cho tệp, bất kì hoạt động đọc và ghi  đối với đối tượng đó sẽ được thực hiện đối với tệp mà nó đại diện. Để mở một  tệp từ một đối tượng dòng, ta dùng hàm open của nó:

open (fileName, mode);


Trong đó, fileName là một xâu kí tự thuộc loại const char * với kết thúc là kí tự null (hằng xâu kí tự cũng thuộc dạng này), là tên của tệp cần mở, và mode là tham số không bắt buộc và là một tổ hợp của các cờ  sau:

ios::in     mở để đọc

ios::out    mở để ghi

ios::binary  mở ở dạng tệp nhị  phân

ios::ate    đặt ví trí bắt đầu đọc/ghi tại cuối tệp. Nếu cờ này không được đặt giá trị gì, vị trí khởi đầu sẽ là đầu tệp.

ios::app    mở để ghi tiếp vào cuối tệp. Cờ này chỉ được dùng  cho dòng mở tệp chỉ để ghi.

ios::trunc   nếu tệp được mở để ghi đã có từ trước, nội dung cũ sẽ bị  xóa

để ghi nội dung mới.
Các cờ trên có thể được kết hợp với nhau bằng toán tử bit OR (|). Ví dụ, nếu ta muốn mở tệp people.dat theo dạng nhị phân để ghi bổ sung dữ liệu vào cuối tệp, ta dùng lời gọi hàm sau:

ofstream myfile;
myfile.open ("people.dat",
   ios::out | ios::app | ios::binary);
Trong trường hợp lời gọi hàm open không cung cấp tham số mode, chẳng hạn Hình 8.1, chế độ mặc định cho dòng loại ostream là ios::out, cho dòng loại istream là ios::in, và cho dòng loại fstream là ios::in | ios::out.

Cách thứ hai để nối một dòng với một tệp là khai báo tên tệp và kiểu mở tệp  ngay khi khai báo dòng, hàm open sẽ được gọi với các đối số tương ứng. Ví dụ:

ofstream myfile ("hello.txt",
   ios::out | ios::app | ios::binary);

Để kiểm tra xem một tệp có được mở thành công hay không, ta dùng hàm thành viên is_open(), hàm này không yêu cầu đối số và trả về một giá trị kiểu bool bằng true nếu thành công và bằng false nếu xảy ra trường hợp ngược lại


if (myfile.is_open()) { /* file now open and ready */ }
  • Đóng tệp

Khi ta hoàn thành các công việc đọc dữ liệu và ghi kết quả, ta cần đóng tệp để

tài nguyên của nó trở về trạng thái sẵn sàng được sử dụng. Hàm thành viên này

 

không có tham số, công việc của nó là xả các vùng bộ nhớ có liên quan và đóng tệp:

 

Sau khi tệp được đóng, ta lại có thể dùng dòng myfile để mở tệp khác, còn tệp vừa đóng lại có thể được mở bởi các tiến trình  khác.

Hàm close cũng được gọi tự động khi một đối tượng dòng bị hủy trong khi    nó

đang nối với một tệp.

 

  • Xlý tp văn bn

Chế độ dòng tệp văn bản được thiết lập nếu ta không dùng cờ ios::binary khi mở tệp. Các thao tác xuất và nhập dữ liệu đối với tệp văn bản được thực hiện tương tự như cách ta làm với cout và cin.

#include <iostream>
#include <fstream>
 
using namespace std; 
 
int main ()
{
  ofstream courseFile (“courses.txt); if (courseFile.is_open())
  {
    courseFile <<1 Introduction to Programming\n”; courseFile <<2 Mathematics for Computer Science\n”; courseFile.close();
  }
  else cout << “Error: Cannot open file”;
  return 0;
}

Kết quả chạy chương trình [tệp courses.txt]

  • Introduction to Programming

 

  • Mathematics for Computer Science
Hình 8.2: Ghi dliu ra tp văn bn.
#include <iostream>
#include <fstream>
#include <string>
 
using namespace std; 
 
int main ()
{
  ifstream file (“courses.txt); 
  if (file.is_open())
  {
    while (! file.eof())
    {
      string  line; getline (file,line);
      cout << line << endl;
    }
    file.close();
  }
  else cout << “Error! Cannot open file”;
  return 0;
}

Kết quả chạy chương trình

  • Introduction to Programming
  • Mathematics for Computer Science

c-p-2-yeulaptrinh.pw

Hình 8.3: Đọc dliu ttp văn bn.

 

Chương trình ví dụ trong Hình 8.2 ghi hai dòng văn bản vào một tệp. Chương trình trong Hình 8.3 đọc nội dung tệp đó và ghi ra màn hình. Để ý rằng trong chương trình thứ hai, ta dùng một vòng lặp để đọc cho đến cuối tệp. Trong đó, myfile.eof() là hàm trả về giá trị true khi chạm đến cuối tệp, giá trị true mà myfile.eof() trả về đã được dùng làm điều kiện kết thúc vòng lặp đọc tệp.

Kiểm tra trạng thái của dòng

Bên cạnh hàm eof() có nhiệm vụ kiểm tra cuối tệp, còn có các hàm thành viên khác dùng để kiểm tra trạng thái của dòng:

bad()  trả về true nếu một thao tác đọc hoặc ghi bị thất bại. Ví dụ khi ta cố viết vào một tệp không được  mở  để ghi hoặc khi thiết bị lưu  trữ không còn chỗ trống để ghi.

fail() trả về true trong những trường hợp bad() trả về true và khi có lỗi định dạng, chẳng hạn như khi ta đang định đọc một số nguyên nhưng lại gặp phải dữ liệu là các chữ cái.

eof()   trả về true nếu chạm đến cuối tệp

good()    trả về false nếu xảy tình huống mà  một trong các hàm trên nếu được gọi thì sẽ trả về true.

Để đặt lại cờ trạng thái mà một hàm thành viên nào đó đã đánh dấu trước đó, ta dùng hàm thành viên clear.

Nhập, xuất dữ liệu trong C++

Trong bài này, chúng ta sẽ học cách nhập vào từ bàn phím (stdin standard input device) và ghi ra màn hình (stdout standard output device).

Ta cũng cần dùng đến thư viện iostream namespace std để hỗ trợ nhập xuất. Khi muốn dùng lệnh nào nằm trong namespace std, ta có 2 cách:

  • Khai báo using namespace std ở đầu chương trình, sau đó có thể dùng các lệnh này bình thường.
  • Thêm std:: vào trước lệnh ta muốn dùng. Ví dụ như std::cin, std::endl.

Từ đây trở đi, khi gặp std:: trước một lệnh, chúng ta sẽ tự hiểu lệnh sau đó nằm trong namespace std và có 2 cách như trên để viết.

std::cin

Để nhập dữ liệu cho biến, ta dùng lệnh cin

cin >> biến;

Nếu có nhiều biến cần nhập vào, ta có thể viết liên tiếp như sau

cin >> biến 1 >> biến 2 >> … >> biến n;

Khi chạy đến lệnh cin, chương trình sẽ chờ người dùng nhập dữ liệu vào các biến tương ứng. Dữ liệu nhập vào được phân cách nhau bởi dấu cách hoặc tab hoặc enter, và luôn được hiển thị ra màn hình.

Ví dụ

int a, b;
cin >> a >> b;

Lệnh cin ở dòng 2 sẽ yêu cầu người dùng nhập vào 2 giá trị tương ứng với 2 biến số nguyên a và b.

Lưu ý

Khi nhập vào, các giá trị được phân tách nhau bởi space (dấu cách), tab (dấu tab) hay enter (dấu xuống dòng). Nếu không, mặc dù trong một số trường hợp chương trình vẫn chạy nhưng rất khó kiểm soát được giá trị các biến nhập vào.
Ví dụ: đoạn mã sau

int a,b,c;  
cin>>a>>b>>c;  
hoặc
int a,b,c;
cin>>a; cin>>b; cin>>c;
khi thực hiện, ta nhập dữ liệu từ bàn phím: 3 4 5<enter> hoặc nhập:
3<enter>
4<enter>
5<enter>
Sau khi nhập xong biến a=3, b=4 và c=5.

std::cout

Để in dữ liệu ra màn hình, ta dùng lệnh cout

cout << biểu thức;

Ta cũng có thể in hàng loạt nhiều biểu thức liên tiếp nhau

cout << biểu thức 1 << biểu thức 2 << … << biểu thức n;

Biểu thức ở đây có thể hiểu là biểu thức toán học chứa biến, hằng, hay kết quả trả về của một hàm, …

Ví dụ

int a, b;
cin >> a >> b;
cout << a << ” + ” << b << ” = ” << a + b;

Lệnh cout ở dòng 3 in ra lần lượt: giá trị biến a, chuỗi ” + “, giá trị biến b, chuỗi ” = ” và giá trị biểu thức a + b.

Một số ký tự điều khiển

  • \a’ : tiếng chuông
  • \b’ : lùi lại một bước
  • \n’ : xuống dòng
  • \t’ : dấu tab
  • \\’ : dấu \
  • \?’ : dấu ?
  • \”‘ : dấu “

Lưu ý

std::endl cũng có chức năng tương tự ‘\n’ nhưng ngoài ra endl còn làm rỗng bộ đệm đầu ra.

Ví dụ

cout << ‘\t’ << “Hello World !\n” << 0;

Kết quả:

Hello World!
0

Lưu ý

cin dùng toán tử >> còn cout dùng toán tử <<. Đừng nhầm lẫn!

cout, cin nằm trong thư viện iostream

Định dạng in

Để dùng những lệnh định sau, ngoài thư viện iostream, ta còn phải dùng thư viện iomanip để định dạng. Các định dạng này cần được cout mới có tác dụng.

std::setw(n): quy định khoảng không gian cho dữ liệu được in ra màn hình là n. Nếu dữ liệu chiếm ít không gian hơn, dữ liệu sẽ được căn  lề phải khi in ra. Ngược lại , lệnh này không có ảnh hưởng, tức dữ liệu vẫn in ra như bình thường.

std::setprecision(n): quy định số chữ số được làm tròn khi in ra là n. Số chữ số được tính từ trái qua phải.

std::fixed: lệnh này đi kèm với setprecision sẽ xác định chỉ làm tròn các chữ số ở phần thập phân.

Ví dụ

cout << 12.345 << ‘ ‘;
cout << setprecision(2) << 12.345 << ‘ ‘;
cout << fixed << setprecision(2) << 12.345;

Kết quả:

12.345 12 12.35

Một số hàm khác liên quan đến nhập xuất

std::cin.get(c): nhập 1 ký tự vào biến c.

std::cin.getline(s, n): nhập tối đa n – 1 ký tự vào xâu s (ký tự thứ n là NULL).
std::getline (cin,str): đọc xâu kí tự str, str kiểu dữ liệu string.
std::cin.ignore(n): xóa n ký tự trong bộ đệm đầu vào.

fflush(stdin): xóa toàn bộ bộ đệm đầu vào.

Có thể bạn thích:
Nhập, xuất dữ liệu từ file trong C++

Bài tập mẫu

Viết chương trình nhập vào 3 số a, b, c. In ra trung bình cộng của 3 số đó với giá trị làm tròn đến chữ số thập phân thứ 5.

Code:

#include <iostream>
#include <iomanip>
 
using namespace std;
 
int main()
{
     cout << "Nhap 3 so a, b, c: ";
     float a, b, c;
     cin >> a >> b >> c;
     float avr = (a + b + c) / 3;
     cout << fixed << setprecision(5) << avr << endl;
     system("pause"); // Tạm dừng màn hình chờ nhấn Enter
     return 0;
}

Hướng dẫn cài đặt và sử dụng Dev-C++

Dev-C++ là một môi trường phát triển tích hợp tự do (IDE) được phân phối dưới hình thức giấy phép Công cộng GNU hỗ trợ việc lập trình bằng C/C++. Nó cũng nằm trong bộ trình dịch mã nguồn mở MinGW. Chương trình IDE này được viết bằng ngôn ngữ Delphi.  Dùng để soạn thảo và biên dịch chương trình viết bằng NNLT C/ C++.

dev-c-yeulaptrinh.pw

Mình khuyên các bạn nên dùng phiên bản: Dev-Cpp 5.11 TDM-GCC 4.9.2 Setup.exe

Link tải trực tiếp từ server Vietnam: Download here

Link tải từ trang chủ:  https://sourceforge.net/projects/orwelldevcpp/files/Setup%20Releases/

Link tải các phiên bản khác: http://www.bloodshed.net/dev/devcpp.html

Thực hiện chương trình

B1. Tạo tệp mới và soạn thảo mã nguồn

  • Vào File > New > Source File (Hoặc Project nếu chương trình gồm nhiều file > Chọn “Console Application” > Chọn C project hoặc C++ Project) hoặc nhấn Ctrl+N
  • Soạn thảo mã nguồn
  • Lưu: File/Save/ đặt tên (chọn đường dẫn nếu có) > OK

dev-c-1-yeulaptrinh.pw

B2. Dịch chương trình

Kích Menu Execute > Compile (Ctrl+ F9)

dev-c-2-yeulaptrinh.pw

Nếu xuất hiện lỗi cú pháp, hãy quan sát dòng lệnh được đánh dấu màu nâu trong mã nguồn và các dòng gợi ý nội dung lỗi. Ta xác định lỗi và tự sửa lại, sau đó Cltr+F9 để biên dịch lại đến khi chương trình hết lỗi.

dev-c-3-yeulaptrinh.pw

Một số lỗi hay gặp: thiếu dấu chấm phảy ;, ngoặc tròng (,), ngoặc móc {,}, các phép quan hệ, các lệnh if, for, while không đúng cú pháp …

B3. Chạy chương trình

Kích menu Execute > Run (Ctrl+ F10)

dev-c-4-yeulaptrinh.pw

Nếu không dừng màn hình để xem kết quả ta thêm vào lệnh system(“pause”) trước lệnh return 0; trong hàm main(); hoặc kích menu tools/Enviroment options/ General/ chọn Pause console programs after return

I.7.3. Debug chương trình

Để theo dõi quá trình thực hiện của máy trên từng dòng lệnh và các giá trị thay đổi của biến ta sử dụng chức năng debug của phần mềm DevC++ theo hướng dẫn sau:

Bước 1:  Giả sử ta có chương trình sau:

dev-c-5-yeulaptrinh.pw

  • Tạo Breakpoint để chỉ nơi bắt đầu theo dõi chương trình bằng cách kích chuột vào số thứ tự dòng, xuất hiện nền của dòng đó có màu đỏ, sau đó kích menu

Execute/ Debug (hoặc nhấn phím F5)

  • Để theo dõi máy thực hiện từng dòng lệnh ta nhấn phím F7 hoặc kích nút Next line (dòng có nền xanh là dòng máy đang thực hiện đến đó)
  • Để xem giá trị của biến nào, ta kích nút Add wacth, gõ tên biến vào hộp thoại mới xuất hiện/ OK … như hình dưới đây.

dev-c-6-yeulaptrinh.pw

Giải thích thêm cho hình trên:

  • Đầu tiên tạo Breakpoint tại dòng 11, nhấn F5. Nền xanh tại dòng này x=8, y=3, z là giá trị chưa xácđịnh cụ thể. Nhấn phím F7, dòng xanh nhảy xuống dòng 12. Lệnh z = ++x + y–; được thực hiện:
  • Lệnh ++x thực hiện nên x=9 đến lệnh z = x+y và z=12 sau đó đến y –, khi đó y=2 như kết quả hiện thời trên khung debug.
  • Bằng cách nhấn F7 tiếp cho đến khi kết thúc hoặc kích nút Stop Execute để ngừng debug chương trình.

Lưu ý:

  • Để soạn theo nhanh một tệp C++, tao có thể mở cửa sổ Codeblocks và kích menu File/ New/ Empty file, một file trắng xuất hiện và ta bắt đầu soạn thảo mã nguồn tại đây, lưu, dịch và thực hiện chương trình một cách bình thường và nhanh. Chỉ có tồn tại là làm theo cách này không Debug được mã nguồn. Nếu muốn debug thì phải thêm tệp này vào một project nào đó và debug như cách hướng dẫn ban đầu.
  • Hai trình biên dịch DevC++ và Codeblocks đểu có thể biên soạn, lưu trữ, dịch, debug và thực hiện tốt những chương trình soạn thảo bằng C++. Vậy ta sẽ chọn chương trình nào để thực hành các bài tập trên máy? Điều này có lẽ tùy thuộc vào người sử dụng yêu thích biên dịch nào… Nếu bạn là người bắt đầu cho cả hai loại trên, theo ý tác giả bạn nên chọn Codeblocks vì chỉ một lý do DEBUG!!! Bạn có thể tìm hiểu qua các phương tiện thông tin khác và chọn cho mình một trình biên dịch để bắt đầu ngay.

Hướng dẫn cài đặt, thiết lập và sử dụng Code::Blocks

Cách tải về

Download trực tiếp server Vietnam tại: Download here

Download tải trang chủ Code::Blocks: http://www.codeblocks.org/downloads

Giao diện http://www.codeblocks.org/downloads sẽ như thế này. Click Chọn “Download the binary release

codeblock-1-yeulaptrinh.pw

Chọn phiên bản mình muốn download. Nếu bạn dùng Windows thì hãy chọn phiên bản “codeblocks-16.01mingw-setup.exe” như hình dưới. Click chọn “Sourceforge.net or FossHub” để tải về.

codeblock-2-yeulaptrinh.pw

Rồi ngồi đợi vài phút chờ tải xong 🙂

Cài đặt

Chạy file cài đặt vừa tải về, ở đây tôi chạy file “codeblocks-16.01mingw-setup.exe“.

Cửa sổ cài đặt xuất hiện, bạn cứ chọn “Next” là được thôi 🙂

c-1-yeulaptrinh.pw

c-3-yeulaptrinh.pw

Cài cấu hình ban đầu

Cái này quan rất quan trọng vì nếu bạn chọn cấu hình Code::Blocks sai thì sau này có thể sẽ không thể chạy được.

Nhớ chọn trình biên dịch “GNU GCC Compiler” nhé!

c-6-yeulaptrinh.pw

Sau đó thiết lập các thông số cho 2 trình Compiler (biên dịch) và Debugger (gỡ lỗi).

(For CodeBlocks 13.12 For Windows) – Mở cửa sổ CodeBlocks, kích

“Settings” menu, kích “Compiler…”. Xuất hộp thoại Compiler Settings, tại “Selected Compiler”, chọn “GNU GCC Compiler”, Chọn “Toolchain Executables” và kiểm tra các thông tin “Compiler’s Installation Directory”. Được đặt là “MinGW” tên thư mục con trong thư mục cài đặt CodeBlocks, ví dụ, thư mục của CodeBlocks là “c:\Program Files\codeblocks”, thì trình biên dịch là “c:\Program Files\codeblocks\MinGW”.

Tương tự như vậy, hãy kiểm tra đường dẫn của chương trình Debugger. Kích “Settings” menu, kích “Debugger …” và chọn “GDB / CDB debugger/ Default”. Trong “Executable path”, cung cấp tên đầy đủ đường dẫn của tệp “gdb.exe”, ví dụ, “c : \ Program Files \ CodeBlocks \ MinGW \ bin \ gdb.exe “.

Viết và chạy một chương trình C++

Kích menu File/ New/ Empty file, xuất hiện cửa sổ soạn thảo mới với tên Untitled1… Sau đó kích menu “File/ Save”, đặt tên tệp mới (ví dụ: Tong.cpp) và kích nút Save/ chọn tiếp OK, và tiến hành soạn thảo mã nguồn cho bài toán mới. Trước khi đặt tên ta có thể soạn thảo luôn, nhưng có thể không được cung cấp hỗ trợ về định dạnh, soạn thảo hoặc những rủi do mất file.

Ví dụ: Viết chương trình nhập vào hai số nguyên a và b từ bàn phím, in lên màn hình tổng giá trị của hai số a +b như sau:codeblocks-1-yeulaptrinh.pw

Nhấn Ctrl+F9 để dịch chương trình, nếu xuất hiện lỗi như hình ảnh trên. Ta quan sát dòng đánh dấu (ô vuông nhỏ màu đỏ) và các gợi ý về lỗi tại Biuld messages để xác định lỗi và sửa lại cho đúng, sau đó nhấn Ctrl+F9 để kiểm tra tiếp (đến khi hết lỗi).

Nhấn phím Ctrl+F10 để chạy chương trình và nhập vào 2 số nguyên có giá trị 4 và 5 <enter> xuất hiện như hình dưới đây:

codeblocks-2-yeulaptrinh.pw

Lưu ý:

  • Nhấn phím F9 để vừa dịch và chạy chương trình (tức ban đầu là dịch chương trình, nếu có lỗi (là lỗi cú pháp hay lỗi văn phạm), sẽ dừng lại ở vị trí gây lỗi. Nếu không lỗi, chương trình được thực thi luôn)
  • Nếu soạn thảo và thực hiện chương trình theo cách này, ta sẽ không sử dụng được chương trình Debugger (gỡ lỗi). Cách này thường được sử dụng khi chương trình không còn lỗi về văn phạm nhưng gặp phải lỗi về giải thuật – output sai. Để sử dụng tính năng Debugger ta sẽ nghiên mục tiếp theo.

Tạo file project và file CPP.

Bước 1: Tạo project (dự án) mới:

Project là một file của Codeblocks nó có thể quản lý nhiều file khác. Để tạo một Project ta lần lượt thực hiện theo các bước sau:

  • Kích menu File/ New/ Project …/ Chọn Console Application, kích nút Go/ Chọn C++/ kích Next. Xuất hiện hộp thoại sau:

codeblocks-3-yeulaptrinh.pw

  • Project title: tên tiêu đề cho project
  • Folder to create project in: Gõ hoặc chọn thư mục lưu file.  Project filename: gõ tên project cần tạo

Sau khi hoàn thành, kích nút Next.

codeblocks-4-yeulaptrinh.pw

Xuất hiện hộp thoại chọn trình biên dịch và nơi lưu trữ (thường để mặc định như hình ảnh trên). Kích Next, cửa Codeblocks: project xuất hiện, tệp main.cpp với các dòng lệnh có sẵn:

codeblocks-5-yeulaptrinh.pw

Xuất hiện cửa sổ Codeblocks với tên project (chuong1) vừa tạo và đã sẵn sàng để viết chương trình của bạn. Cần lưu ý rằng mục tiêu là Debug, mà sẽ cho phép bạn sử dụng các trình gỡ lỗi (debugger) để tìm lỗi.

Trong khung Management của màn hình (Shift-F2 để ẩn/hiện), bạn sẽ thấy các file đó là một phần của dự án trong tab Project. Để xem các tập tin nguồn, bấm vào cộng [+] ‘s để mở rộng không gian làm việc và thư mục con của nó, muốn thu nhỏ ta kích vào [-] và muốn hiện nội dung của tệp ta kích đúp vào tên tệp.

Khi tạo một project mới, nó luôn tạo sẵn file Main.cpp chỉ để in ra màn hình Console (Dos- nền đen, chữ màu trắng) dòng chữ “Hello World!” như đã giới thiệu ở phần đầu của giáo trình này.

Bước 2: Dịch và chạy chương trình (đã giới thiệu phần trước).

G/s ta đã soạn thảo xong mã nguồn tệp main.cpp như trên, tiếp theo kích menu Biuld/ chọn Biuld hoặc nhấn tổ hợp phím Ctrl+F9 để dịch chương trình để kiểm tra có lỗi cú pháp hay không. Nếu không có lỗi, tiếp theo nhấn Ctrl+F10 để chạy chương trình như hình dưới đây:

codeblocks-6-yeulaptrinh.pw

Để soạn thảo mã nguồn mới, bạn có thể sửa trực tiếp vào tệp này hoặc tạo tệp mới bằng cách:

Kích menu File/ New/ Empty file, xuất hiện thông báo:

codeblocks-7-yeulaptrinh.pw

Chọn “Yes” để thêm tệp mới vào project hiện tại và đặt tên tệp/ Save, xuuast hiện hộp thoại Multiple selection, đánh dấu vào hai mục Debugg và Release và kích OK, cửa sổ soạn thảo tệp vừa đặt tên xuất hiện. NLT bắt đầu soạn thảo mã nguồn, dịch và chạy chương trình.

Để xóa tệp khỏi cửa sổ Management: kích chuột phải vào tên tệp/ chon remove file from project hoặc kích menu Project/ chọn Remove file…/ bỏ dấu tích tại tên tệp muốn xóa/ chọn nút OK.

Nếu ta đã có sẵn tệp trên máy, để thêm vào project hiện tại dịch và chạy chương trình ta thực hiện: Kích menu Project/ chọn Add file…/ Chọn đến tệp cần thêm/ kích Open, sau đó tiến hành dịch và chạy bình thường.

Hướng dẫn Debug chương trình (trình gỡ lỗi – debugger)

Thiết lập thông số của Debug, kích menu Project/ Biuld Option/ và đảm bảo đặt dấu tích vào dòng “produce debugging symbols [-g]” như hình dưới đây và kích nút OK:

codeblocks-8-yeulaptrinh.pw

Soạn thảo tệp mới hoặc Add file có sẵn vào project hiện thời. Ví dụ tệp Tong2.cpp như hình dưới đây. Nếu trong trường hợp tồn tại hai tệp CPP trở lên thì NLT phải loại bỏ các tệp khác, chỉ giữ lại duy nhất tệp muôn debug.

Để loại bỏ tệp khỏi project hiện thời ta làm như sau:

  • Kích file lên tên tệp CPP tại khung trái – project/ chọn Remove file from project.
  • Kích phải vào nền trắng trong khung trái – project/ chọn clean workspace để đảm bảo xóa các thông tin, dữ liệu đã debug trước đó.

codeblocks-9-yeulaptrinh.pw

Trước tiên, hãy đặt trỏ văn bản tại nơi chương trình debug bắt đầu (dòng 11). Kích menu Debug/ chọn Run to cursor (hoặc nhấn phím F4), nếu xuất hiện thông báo … ta chọn Yes để tiếp tục.

Hoặc

Đánh dấu vị trí bắt đầu và kết thúc Debug: đặt trỏ văn bản vào vị trí, kích Debug/ Toggle breakpoint (phím F5).

Sau đó kích menu Debug/ Start/ Continue (phím F8) để bắt đầu quá trình Debugger.

codeblocks-10-yeulaptrinh.pw

Xuất hiện hình tam giác nhỏ màu vàng (gọi là breakpoint) tại dòng bắt đầu debug và cửa số Watches (new) để theo dõi giá trị của các biến trong chương trình khi ta đi qua từng dòng lệnh trong chương trình nguồn. Trong trường hợp cửa sổ này chưa xuất hiện thì kích menu Debug/ Dubugging Windows/ Watchs. Và màn hình Console – nền đen (có thể sẽ ẩn sau cửa sổ Codeblocks).

Bắt đầu quá trình debugging, máy tính tính thực hiện dòng lệnh hiện thời và xem giá trị các biến thay đổi ta nhấn phím F7 con trỏ và Breakpoint sẽ lần lượt nhảy xuồng các dòng tiếp theo (mỗi lần nhấn phím F7, hãy dừng lại một chút để kiểm tra việc thực hiện lệnh và giá trị các biến có tương ứng hay không trên cửa sổ Watches).

codeblocks-11-yeulaptrinh.pw

Nhấn phím F7 lần 1.Nhấn phím F7 lần 2.

Máy thực hiện dòng lệnh 12. Dòng  lệnh này xuất ra màn hình Console:

codeblocks-12-yeulaptrinh.pw

Máy thực hiện dòng lệnh 11 gồm:

++x, x=x+y, y– nên (x=9,y=2 và z=12)

Tiếp tục nhấn phím F7 đến dòng lệnh 19, ta được kết quả trên cửa sổ Watches là:

codeblocks-13-yeulaptrinh.pw

Để kết thúc debug, kích menu Debug/ Stop debugger

 

Có gì chưa hiểu rất mong nhận được sự góp ý của quý bạn!

Set trong C++

Tổng quan

  • Set là một loại associative containers để lưu trữ các phần tử không bị trùng lặp (unique elements), và các phần tử này chính là các khóa (keys).
  • Khi duyệt set theo iterator từ begin đến end, các phần tử của set sẽ tăng dần theo phép toán so sánh.
  • Mặc định của set là sử dụng phép toán less, bạn cũng có thể viết lại hàm so sánh theo ý mình.
  • Set được thực hiện giống như cây tìm kiếm nhị phân (Binary search tree).

Khai báo:

#include <set> 
set <int> s;
set <int, greater<int> > s;
Hoặc viết class so sánh theo ý mình:
struct cmp{
bool operator() (int a,int b) {return a<b;}
};
set <int,cmp > myset ;

Capacity:

  • size : trả về kích thước hiện tại của set. ĐPT O(1)
  • empty : true nếu set rỗng, và ngược lại. ĐPT O(1).

Thay đổi:

  • insert : Chèn phần tử vào set. ĐPT O(logN).
  • erase : có 2 kiểu xóa: xóa theo iterator, hoặc là xóa theo khóa. ĐPT O(logN).
  • clear : xóa tất cả set. ĐPT O(n).
  • swap : đổi 2 set cho nhau. ĐPT O(n).

Truy cập phần tử:

  • find : trả về itarator trỏ đến phần tử cần tìm kiếm. Nếu không tìm thấy itarator trỏ về “end” của set. ĐPT O(logN).
  • lower_bound : trả về iterator đến vị trí phần tử bé nhất mà không bé hơn (lớn hơn hoặc bằng) khóa (dĩ nhiên là theo phép so sánh), nếu không tìm thấy trả về vị trí “end” của set. ĐPT O(logN).
  • upper_bound: trả về iterator đến vị trí phần tử bé nhất mà lớn hơn khóa, nếu không tìm thấy trả về vị trí “end” của set.. ĐPT O(logN).
  • count : trả về số lần xuất hiện của khóa trong container. Nhưng trong set, các phần tử chỉ xuất hiện một lần, nên hàm này có ý nghĩa là sẽ return 1 nếu khóa có trong container, và 0 nếu không có. ĐPT O(logN).

 

Chương trình Demo 1:

#include <iostream>
#include <set>
using namespace std; 
main() {
      set <int> s;
      set <int> ::iterator it; s.insert(9);  // s={9}
      s.insert(5);  // s={5,9} cout << *s.begin() << endl; //In ra 5 s.insert(1);  // s={1,5,9} cout << *s.begin() << endl; // In ra 1
 
      it=s.find(5);
      if (it==s.end()) cout << "Khong co trong container" << endl; else  cout << "Co trong container" << endl;

      s.erase(it);  // s={1,9}
      s.erase(1);  // s={9}

      s.insert(3);  // s={3,9}
      s.insert(4);  // s={3,4,9}

      it=s.lower_bound(4);
      if (it==s.end()) cout << "Khong co phan tu nao trong set khong be hon 4" << endl; else cout << "Phan tu be nhat khong be hon 4 la " << *it << endl;  // In ra 4

      it=s.lower_bound(10);
      if (it==s.end()) cout << "Khong co phan tu nao trong set khong be hon 10" << endl; else cout << "Phan tu be nhat khong be hon 10 la " << *it << endl; // Khong co ptu nao           

      it=s.upper_bound(4);
      if (it==s.end()) cout << "Khong co phan tu nao trong set lon hon 4" << endl; else cout << "Phan tu be nhat lon hon 4 la " << *it << endl;  // In ra 9                

      /* Duyet set */

      for (it=s.begin();it!=s.end();it++) { cout << *it <<  " ";
      }
      // In ra 3 4 9

      cout << endl; system("pause");
}

Lưu ý: Nếu bạn muốn sử dụng hàm lower_bound hay upper_bound để tìm phần tử lớn nhất “bé hơn hoặc bằng” hoặc “bé hơn” bạn có thể thay đổi cách so sánh của set để tìm kiếm. Mời bạn xem chương trình sau để rõ hơn:

#include <iostream>
#include <set>
#include <vector> using namespace std;
main() {
      set <int, greater <int> > s;
      set <int, greater <int> > :: iterator it; // Phép toán so sánh là greater

      s.insert(1);  // s={1}
      s.insert(2);  // s={2,1}
      s.insert(4);  // s={4,2,1}
      s.insert(9);  // s={9,4,2,1}

      /* Tim phần tử lớn nhất bé hơn hoặc bằng 5 */ it=s.lower_bound(5);
      cout << *it << endl;  // In ra 4

      /* Tim phần tử lớn nhất bé hơn 4 */ it=s.upper_bound(4);
      cout << *it << endl;  // In ra 2

      system("pause");
}

Vector trong C++

Khai báo vector:

Vector có thể hiểu là một mảng có trình tự, giống như với danh sách liên kết hay một chuỗi thông thường nhưng “vector” khác với chuỗi hoăc mảng thông thường là chúng ta có thể thay đổi kích thước của nó và cũng có thể truy cập trực tiếp đến các phần tử, điều này làm cho việc sử dụng “vector” linh hoạt hơn so với “list”…

#include <vector>
...
/* Vector 1 chiều */

/* tạo vector rỗng kiểu dữ liệu int */ vector <int> first;

//tạo vector với 4 phần tử là 100 vector <int> second (4,100);

// lấy từ đầu đến cuối vector second
vector <int> third (second.begin(),second.end())

//copy từ vector third vector <int> four (third)

/*Vector 2 chiều*/

/* Tạo vector 2 chiều rỗng */ vector < vector <int> > v;

/* khai báo vector 5×10 */
vector < vector <int> > v (5, 10) ;

/* khai báo 5 vector 1 chiều rỗng  */ vector < vector <int> > v (5) ;

//khai báo vector 5*10 với các phần tử khởi tạo giá trị là 1 vector < vector <int> > v (5, vector <int> (10,1) ) ;
Các bạn chú ý 2 dấu “ngoặc” không được viết liền nhau. Ví dụ như sau là sai:
/*Khai báo vector 2 chiều SAI*/ 
vector <vector <int>> v;

Các hàm thành viên:

  • size : trả về số lượng phần tử của vector. ĐPT O(1).
  • empty : trả về true(1) nếu vector rỗng, ngược lại là false(0). ĐPT O(1).

 

Truy cập tới phần tử:

  • operator [] : trả về giá trị phần tử thứ []. ĐPT O(1).
  • at : tương tự như trên. ĐPT O(1).
  • front: trả về giá trị phần tử đầu tiên. ĐPT O(1).
  • back: trả về giá trị phần tử cuối cùng. ĐPT O(1).

 

Chỉnh sửa:

  • push_back : thêm vào ở cuối vector. ĐPT O(1).
  • pop_back : loại bỏ phần tử ở cuối vector. ĐPT O(1).
  • insert (iterator,x): chèn “x” vào trước vị trí “iterator” ( x có thể là phần tử hay iterator của 1 đoạn phần tử…). ĐPT O(n).
  • erase : xóa phần tử ở vị trí iterator. ĐPT O(n).
  • swap : đổi 2 vector cho nhau (ví dụ: first.swap(second);). ĐPT O(1).
  • clear: xóa vector. ĐPT O(n).

 

Nhận xét:

  • Sử dụng vector sẽ tốt khi:
    • Truy cập đến phần tử riêng lẻ thông qua vị trí của nó O(1)
    • Chèn hay xóa ở vị trí cuối cùng O(1).
  • Vector làm việc giống như một “mảng động”.

 

Một vài ví dụ:

Ví dụ 1: Ví dụ này chủ yếu để làm quen sử dụng các hàm chứ không có đề bài cụ thể.

#include <iostream>
#include <vector> 
using namespace std;
vector <int> v; //Khai báo vector
vector <int>::iterator it;  //Khai báo iterator
vector <int>::reverse_iterator rit; //Khai báo iterator ngược int i;
main() {
      for (i=1;i<=5;i++) v.push_back(i); // v={1,2,3,4,5} cout << v.front() << endl;  // In ra 1
      cout << v.back() << endl;  // In ra 5

      cout << v.size() << endl;  // In ra 5

      v.push_back(9);  // v={1,2,3,4,5,9}
      cout << v.size() << endl;  // In ra 6

      v.clear();  // v={}
      cout << v.empty() << endl;  // In ra 1 (vector rỗng)

      for (i=1;i<=5;i++) v.push_back(i); // v={1,2,3,4,5} v.pop_back();  // v={1,2,3,4}
      cout << v.size() << endl;  // In ra 4

      v.erase(v.begin()+1);  // Xóa ptử thứ 1 v={1,3,4} v.erase(v.begin(),v.begin()+2);  // v={4}
      v.insert(v.begin(),100);  // v={100,4}
      v.insert(v.end(),5);  // v={100,4,5}

      /*Duyệt theo chỉ số phần tử*/
      for (i=0;i<v.size();i++) cout << v[i] << " "; // 100 4 5 cout << endl;
 
      /*Chú ý: Không nên viết
      for (i=0;i<=v.size()-1;i++) ...
      Vì nếu vector v rỗng thì sẽ dẫn đến sai khi duyệt !!!
      */

      /*Duyệt theo iterator*/
      for (it=v.begin();it!=v.end();it++) cout << *it << " " ;
      //In ra giá trị ma iterator đang trỏ tới "100 4 5" cout << endl;

      /*Duyệt iterator ngược*/
      for (rit=v.rbegin();rit!=v.rend();rit++) cout << *rit << " "; // 5 4 100
      cout << endl;

      system("pause");
}
Ví dụ 2: Cho đồ thị vô hướng G có n đỉnh (các đỉnh đánh số từ 1 đến n) và m cạnh và không có khuyên (đường đi từ 1 đỉnh tới chính đỉnh đó).

Cài đặt đồ thị bằng danh sách kề và in ra các cạnh kề đối với mỗi cạnh của đồ thị. Dữ liệu vào:

  • Dòng đầu chứa n và m cách nhau bởi dấu cách
  • M dòng sau, mỗi dòng chứa u và v cho biết có đường đi từ u tới Không có cặp đỉnh u,v nào chỉ cùng 1 đường đi.

Dữ liệu ra:

  • M dòng: Dòng thứ i chứa các đỉnh kề cạnh i theo thứ tự tăng dần và cách nhau bởi dấu cách.

Giới hạn: 1 <= n,m <= 10000 Ví dụ:

INPUT OUTPUT
6 7 2 3 5 6
1 2 1 3 6
1 3 1 2 5
1 5  
2 3 1 3
2 6 1 2
3 5  
6 1  

 

Chương trình mẫu:

#include <iostream>
#include <vector> using namespace std;
vector < vector <int> > a (10001);
 
//Khai báo vector 2 chiều với 10001 vector 1 chiều rỗng int m,n,i,j,u,v;
main() {
      /*Input data*/ cin >> n >> m;
      for (i=1;i<=m;i++) { cin >> u >> v; a[u].push_back(v);
      a[v].push_back(u);
      }
      /*Sort cạnh kề*/ for (i=1;i<=m;i++)
      sort(a[i].begin(),a[i].end());
      /*Print Result*/
      for (i=1;i<=m;i++) {
      for (j=0;j<a[i].size();j++) cout << a[i][j] << " "; cout << endl;
      }
      system("pause");
}

Toán tử trong C++

Toán tử so sánh (Relational operators)

Toán tử logic trong C++ (Logical operators)
Toán tử trên bit trong C++ (Bitwise operators)
Các toán tử hỗn hợp trong C++ (Misc Operators)

Độ ưu tiên và quy tắc kết hợp toán tử trong C++