Skip to content

Làm cùng lớp · Tuần 2

Tuần 2 — Làm cùng lớp: Chương trình C++ đầu tiên và bốn loại lỗi ​

Làm ngay trong giờ lý thuyết. Tuần 2 chưa có buổi thực hành. Giảng viên chiếu trang này và làm từng bài cùng cả lớp; bạn gõ theo trên máy mình. Không cần cài gì: mọi bài chạy được trên trình biên dịch online. Không chấm, không nộp.

Mang theo ​

Laptop. Không có laptop thì dùng điện thoại cũng được, chỉ là gõ code chậm hơn nhiều. Máy chỉ cần trình duyệt và mạng.

Mục tiêu ​

  • Viết, chạy và sửa một chương trình C++ ngay trên trình duyệt.
  • Thấy một file .cpp đi qua chuỗi công cụ: tiền xử lý, biên dịch, hợp dịch, liên kết.
  • Tự tay gây đủ bốn loại lỗi, đọc được g++ báo gì và biết mỗi lỗi bị chặn ở bước nào.
  • Quen dùng giáo trình online: từ tuần 3, mọi bài lab đều nằm trên trang này.

Trước khi bắt đầu ​

  1. Mở trang này trên máy bạn: vào ooplab.koomaru.com, bấm ô Tuần 2 trong khối "Làm cùng lớp" ở trang đầu.
  2. Mở OnlineGDB ở một tab khác và thiết lập một lần, như bước 1 của Phụ lục E.7: ô Language chọn C++ 17; nút bánh răng → Extra Compiler Flags → gõ -Wall → Ok → Done.
  3. Bấm Run với code mẫu có sẵn. Khung phía dưới hiện Hello World là xong.
  4. Làm quen trang giáo trình, thử xong việc nào thì tick việc đó:
0/4 việc

Lý thuyết cần nắm ​

Tóm tắt phần slide tuần 2 mà các bài bên dưới dùng tới. Trong giờ học, giảng viên giảng theo slide; sau buổi, đọc lại ở đây. Mọi kết quả đều lấy từ lần chạy thật.

1. Bộ khung của một chương trình C++ ​

Chương trình đầu tiên trong slide:

cpp
#include <iostream>
using namespace std;

int main()
{
    // Display Welcome to C++ to the console
    cout << "Welcome to C++!" << endl;
    return 0;
}
text
Welcome to C++!
DòngLàm gì
#include <iostream>Chép phần khai báo của thư viện vào/ra vào chương trình, nhờ vậy mới dùng được cout.
using namespace std;Cho phép viết cout, endl thay cho std::cout, std::endl.
int main()Hàm chính. Chương trình nào cũng bắt đầu chạy từ đây, và phải có đúng một hàm main.
{ … }Bao một khối lệnh, ở đây là thân của hàm main.
// ...Chú thích, chỉ để người đọc. Bị bỏ đi trước khi dịch.
cout << "..." << endl;In chuỗi nằm trong dấu nháy ra màn hình; endl xuống dòng.
return 0;Báo với hệ điều hành rằng chương trình kết thúc bình thường.

Câu lệnh nào cũng kết thúc bằng dấu ;. Dòng bắt đầu bằng #, dòng int main() và các dấu ngoặc nhọn thì không có ; phía sau.

2. Chuỗi công cụ: từ main.cpp tới chương trình chạy được ​

Bấm Run, hay gõ g++ main.cpp -o main.exe, thật ra là chạy liền bốn bước. Trên máy có g++, tách được từng bước bằng bốn lệnh dưới đây. OnlineGDB không cho gõ lệnh, nên phần này giảng viên làm mẫu:

sh
g++ -std=c++17 -E main.cpp -o main.ii
g++ -std=c++17 -S main.cpp -o main.s
g++ -std=c++17 -c main.cpp -o main.o
g++ main.o -o main.exe
BướcCờLàm gìVới chương trình ở mục 1
1. Tiền xử lý-EChép nội dung file được #include vào đúng chỗ dòng đó, bỏ chú thích9 dòng thành 35 988 dòng (main.ii, khoảng 1 MB)
2. Biên dịch-SDịch sang hợp ngữ (assembly)main.s, 48 dòng
3. Hợp dịch-cĐổi hợp ngữ thành mã máymain.o, 1 647 byte
4. Liên kếtGhép mã máy của bạn với mã của thư viện, lấy hàm main làm điểm bắt đầumain.exe, khoảng 131 KB

Ba lệnh đầu đều chạy lại từ đầu rồi dừng sau bước của mình: -E dừng sau tiền xử lý, -S dừng sau biên dịch, -c dừng sau hợp dịch. Lệnh thứ tư chỉ làm mỗi việc liên kết, vì đầu vào đã là mã máy.

Cuối file main.ii vẫn là code của bạn, chỉ riêng dòng chú thích đã biến mất. Phía trên nó là gần 36 nghìn dòng của <iostream> (trích):

text
using namespace std;

int main()
{

    cout << "Welcome to C++!" << endl;
    return 0;
}

Trong main.s vẫn nhận ra chuỗi của bạn: nạp địa chỉ chuỗi, nạp cout, rồi gọi toán tử <<. Cái tên dài ngoằng ở dòng call là tên của toán tử << sau khi g++ mã hoá (trích):

text
.LC0:
	.ascii "Welcome to C++!\0"
...
	leaq	.LC0(%rip), %rdx
	movq	.refptr._ZSt4cout(%rip), %rax
	movq	%rax, %rcx
	call	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc

Số dòng và kích thước file tuỳ phiên bản g++; các con số trên đo bằng g++ 15.2 trên Windows.

3. Bốn loại lỗi, mỗi loại bị chặn ở một chỗ ​

Slide tuần 2 chia lỗi thành ba loại: lỗi cú pháp, lỗi lúc chạy, lỗi logic. Chạy thử các ví dụ thì gặp thêm loại thứ tư, lỗi liên kết, ngay từ ví dụ int man() cuối slide. Giáo trình giải thích kỹ cả bốn loại ở mục 1.6.

Loại lỗiBị chặn ởCó chương trình để chạy khôngDấu hiệu
Lỗi cú pháp, kể cả gõ sai tên hay sai tên filebước 1 hoặc 2: tiền xử lý, biên dịchKhôngerror: kèm tên file, số dòng, số cột
Lỗi liên kếtbước 4: liên kếtKhôngundefined reference, ld, collect2; không có số dòng nào trong file của bạn
Lỗi lúc chạylúc chạyCó, nhưng chết giữa chừngg++ không báo gì; chương trình dừng sớm, mã thoát khác 0
Lỗi logickhông bước nào chặnCó, chạy tới cuốiChạy êm nhưng in sai; chỉ lộ ra khi so với kết quả đúng

Loại càng về sau càng khó tìm: lỗi cú pháp thì g++ chỉ tận dòng, còn lỗi logic thì không ai báo cho bạn cả.


Bài 2.1 — Hello world (8 phút) ​

Mục tiêu 8 phút08:00Chưa bắt đầu

Giảng viên giảng mục 1. Bạn:

  1. Trong OnlineGDB, xoá hết code mẫu rồi gõ lại chương trình Welcome ở mục 1. Gõ tay, đừng chép: gõ một lần mới nhớ dấu ; cuối mỗi câu lệnh và cặp ngoặc { }. Bấm Run, phải ra:

    text
    Welcome to C++!
  2. Sửa thành chương trình in ba dòng như slide:

    cpp
    #include <iostream>
    using namespace std;
    
    int main()
    {
        cout << "Programming is fun!" << endl;
        cout << "Fundamentals First" << endl;
        cout << "Problem Driven" << endl;
        return 0;
    }
    text
    Programming is fun!
    Fundamentals First
    Problem Driven
  3. Thử: bỏ << endl ở dòng in đầu tiên, chỉ còn cout << "Programming is fun!";. Đoán trước kết quả, rồi chạy:

    text
    Programming is fun!Fundamentals First
    Problem Driven

    endl là thứ đưa con trỏ xuống dòng. Không có nó, chữ in sau dính ngay vào chữ in trước. Trả lại << endl rồi mới sang bài 2.2: bài 2.3 dùng lại đúng chương trình ba dòng này.

Hướng dẫn từng bước — mở khi đã tự thử mà vẫn bí

Nếu bấm Run mà khung dưới chỉ ghi Compilation failed due to following error(s).: bấm thẻ stderr trong khung đó để đọc lỗi.

Nếu g++ báo lỗi ngay dòng 1: xem lại chữ iostream. Lỗi 1 của bài 2.3 chính là lỗi này.

Nếu ba dòng in ra dính vào nhau: dòng in nào đó thiếu << endl ở cuối.


Bài 2.2 — Tính toán với số (7 phút) ​

Mục tiêu 7 phút07:00Chưa bắt đầu
  1. Chương trình tính biểu thức trong slide:

    cpp
    #include <iostream>
    using namespace std;
    
    int main()
    {
        cout << "(10.5 + 2 * 3) / (45 - 3.5) = ";
        cout << (10.5 + 2 * 3) / (45 - 3.5) << endl;
        return 0;
    }
    text
    (10.5 + 2 * 3) / (45 - 3.5) = 0.39759

    Câu hỏi: cùng một biểu thức, vì sao lần đầu in ra nguyên văn, lần sau in ra một con số?

  2. Bài tập nhanh cuối slide: diện tích hình tròn bán kính 20, radius và area kiểu double. Tự viết trước khi xem lời giải trên slide. Chạy đúng thì ra:

    text
    The area is 1256.64

    Slide lần theo từng bước ghi 1256.636, còn máy in 1256.64: cout mặc định chỉ in 6 chữ số có nghĩa.

  3. Thử: trong main, thay các dòng cout đang có bằng ba dòng dưới đây. Đoán trước từng dòng rồi mới chạy:

    cpp
        cout << 9 / 5 << endl;
        cout << 9.0 / 5 << endl;
        cout << 9 % 5 << endl;
    text
    1
    1.8
    4

    Hai số nguyên chia nhau thì bỏ phần lẻ, nên 9 / 5 là 1; % lấy phần dư. Nhớ dòng đầu tiên: bài 2.3 gặp lại nó.

Hướng dẫn từng bước — mở khi đã tự thử mà vẫn bí

Nếu diện tích in ra The area is 1256: biến area đang là int. Gán radius * radius * 3.14159 vào một biến int thì phần lẻ bị cắt mất, và g++ không cảnh báo gì kể cả với -Wall. Đổi thành double.


Bài 2.3 — Cố tình gây bốn loại lỗi (15 phút) ​

Mục tiêu 15 phút15:00Chưa bắt đầu

Giảng viên giảng mục 2 và mục 3. Lỗi 1 tới lỗi 5 sửa ngay trên chương trình ba dòng của bài 2.1; lỗi 6 và lỗi 7 là hai chương trình riêng, chép đè cả chương trình cũ. Mỗi lần chỉ gây một lỗi: sửa đúng chỗ ghi, đoán trước g++ sẽ báo gì và bước nào chặn lại, bấm Run, đọc thông báo, rồi trả chương trình về như cũ mới sang lỗi sau.

Thông báo dưới đây chép từ OnlineGDB, và số dòng, số cột tính theo đúng chương trình ba dòng ở bài 2.1: bạn gõ khác đi thì số khác. Các lỗi biên dịch trên máy Windows có nội dung giống hệt, chỉ khác dấu nháy là ' '; riêng lỗi 5 và lỗi 6 khác hẳn, ghi rõ ở ngay chỗ đó.

Lỗi 1 — sai tên thư viện. Đổi dòng đầu thành #include <iostrem>.

Xem OnlineGDB báo gì
text
main.cpp:1:10: fatal error: iostrem: No such file or directory
    1 | #include <iostrem>
      |          ^~~~~~~~~
compilation terminated.

Bước tiền xử lý không tìm thấy file để chép vào nên dừng ngay tại đó. fatal error là lỗi không cho đi tiếp.

Lỗi 2 — thiếu dấu chấm phẩy. Xoá dấu ; ở cuối dòng in Fundamentals First.

Xem OnlineGDB báo gì
text
main.cpp: In function ‘int main()’:
main.cpp:7:41: error: expected ‘;’ before ‘cout’
    7 |     cout << "Fundamentals First" << endl
      |                                         ^
      |                                         ;
    8 |     cout << "Problem Driven" << endl;
      |     ~~~~

main.cpp:7:41 nghĩa là file main.cpp, dòng 7, cột 41. g++ chỉ đúng chỗ thiếu và gợi ý luôn ký tự cần thêm. Nó chỉ nhận ra lỗi khi đọc tới chữ cout ở dòng 8.

Lỗi 3 — thiếu dấu nháy. Xoá dấu " đóng ngay sau chữ Problem Driven.

Xem OnlineGDB báo gì
text
main.cpp:8:13: warning: missing terminating " character
    8 |     cout << "Problem Driven << endl;
      |             ^
main.cpp:8:13: error: missing terminating " character
    8 |     cout << "Problem Driven << endl;
      |             ^~~~~~~~~~~~~~~~~~~~~~~~
main.cpp: In function ‘int main()’:
main.cpp:9:5: error: expected primary-expression before ‘return’
    9 |     return 0;
      |     ^~~~~~

Một chỗ sai sinh ra hai lỗi. Lỗi thứ hai ở dòng 9 chỉ là hậu quả của lỗi thứ nhất. Luôn sửa lỗi đầu tiên rồi dịch lại: các lỗi kéo theo thường tự hết.

Lỗi 4 — gõ sai tên. Đổi chữ cout ở dòng in Fundamentals First thành cot.

Xem OnlineGDB báo gì
text
main.cpp: In function ‘int main()’:
main.cpp:7:5: error: ‘cot’ was not declared in this scope
    7 |     cot << "Fundamentals First" << endl;
      |     ^~~

was not declared in this scope nghĩa là g++ chưa từng nghe tới tên này. Gõ sai tên biến, tên hàm, hay quên #include đều ra đúng câu này.

Lỗi 5 — gõ sai tên main. Đổi int main() thành int man(), như ví dụ cuối slide.

Xem OnlineGDB báo gì
text
/nix/store/...-binutils-2.44/bin/ld: /nix/store/...-glibc-2.40-66/lib/Scrt1.o: in function `_start':
(.text+0x1b): undefined reference to `main'
collect2: error: ld returned 1 exit status

Trên máy Windows dùng g++ của MSYS2, cùng lỗi này báo (trích, đường dẫn đã rút gọn):

text
ld.exe: ...libmingw32.a(lib64_libmingw32_a-crtexewin.o): in function `main':
...crtexewin.c:62:(.text.startup+0xb6): undefined reference to `WinMain'
collect2.exe: error: ld returned 1 exit status

Bước biên dịch vẫn qua, vì man là một tên hàm hợp lệ. Tới bước liên kết, ld đi tìm hàm main để làm điểm bắt đầu của chương trình và không thấy. Đây là lỗi liên kết, loại lỗi thứ tư ở mục 3, chứ không phải lỗi cú pháp. Dấu hiệu: có undefined reference, có ld và collect2, và không có số dòng nào trong main.cpp.

Lỗi 6 — chia cho 0. Thay cả chương trình bằng ví dụ lỗi lúc chạy của slide. So với slide có thêm hai dòng in, một trước và một sau phép chia:

cpp
#include <iostream>
using namespace std;

int main()
{
    int i = 4;
    int j = 0;
    cout << "Bat dau chia..." << endl;
    cout << i / j << endl;
    cout << "Chia xong." << endl;
    return 0;
}
Xem chuyện gì xảy ra

g++ không báo gì, kể cả với -Wall. Chạy trên OnlineGDB:

text
Bat dau chia...


...Program finished with exit code 136

Trên Windows, chương trình in Bat dau chia... rồi tắt ngang, không một dòng thông báo. Chỉ có mã thoát cho biết nó đã chết: trong PowerShell, $LASTEXITCODE là -1073741676 (mã C0000094: chia số nguyên cho 0).

Dòng Chia xong. không bao giờ được in: chương trình chết đúng ở phép chia. Đó là lỗi lúc chạy. Muốn biết chương trình chết ở đâu, xem dòng cuối cùng in được.

Lỗi 7 — đổi nhiệt độ sai. Thay cả chương trình bằng ví dụ lỗi logic của slide, đổi 35 độ C sang độ F:

cpp
#include <iostream>
using namespace std;

int main()
{
    cout << "Celsius 35 is Fahrenheit degree " << endl;
    cout << (9 / 5) * 35 + 32 << endl;

    return 0;
}
Xem chuyện gì xảy ra
text
Celsius 35 is Fahrenheit degree 
67

Chạy êm, không cảnh báo, nhưng sai: 35 độ C là 95 độ F. Thủ phạm là 9 / 5 ra 1, đúng như bài 2.2. Sửa thành (9.0 / 5) thì in 95. Đây là lỗi logic: không bước nào chặn được, chỉ phát hiện khi so với kết quả đúng.

Làm xong bảy lỗi, nhìn lại bảng ở mục 3 và xếp từng lỗi vào đúng loại của nó.

Hướng dẫn từng bước — mở khi đã tự thử mà vẫn bí

Nếu đã sửa lại mà vẫn còn lỗi: chương trình chưa về đúng như cũ. So từng ký tự với chương trình ba dòng ở bài 2.1, hoặc chép lại nó từ đó.

Nếu g++ báo một loạt lỗi: chỉ đọc lỗi đầu tiên, sửa, rồi dịch lại. Các lỗi sau thường chỉ là hậu quả của lỗi đầu, như ở lỗi 3.


Sau buổi ​

Tự kiểm tra:

0/5 việc

Chuẩn bị cho tuần 3. Buổi lab tuần 3 dịch chương trình bằng lệnh g++; bài chấm điểm tuần 8 và phòng thi đều không có mạng. Vì vậy trước buổi lab tuần 3, cài một công cụ trên máy theo Phụ lục E: chưa biết chọn gì thì lấy Dev-C++ (E.1); muốn dùng VS Code như slide gợi ý thì làm đủ mục E.5, vì VS Code không kèm sẵn bộ biên dịch.

Đọc thêm trong giáo trình: mục 1.5 về lệnh biên dịch, mục 1.6 về bốn loại lỗi, và Phụ lục A về cách đọc một thông báo lỗi của g++.

© 2026 Ngan V.T. Nguyen · Nội dung theo giấy phép CC BY-NC-SA 4.0 · Code ví dụ theo giấy phép MIT · Ảnh của bên thứ ba giữ giấy phép riêng, ghi ở Phụ lục E

Nội dung theo giấy phép CC BY-NC-SA 4.0 · Code ví dụ theo giấy phép MIT · Ảnh của bên thứ ba giữ giấy phép riêng, ghi ở Phụ lục E