Bài tập c++ cơ bản và thực tế năm 2024

Ngôn ngữ lập trình C là một ngôn ngữ lập trình máy tính được phát triển bởi Dennis Ritchie vào những năm 1970 tại Bell Labs. C là một ngôn ngữ lập trình mạnh mẽ, có hiệu suất cao, và được sử dụng rộng rãi trong phát triển phần mềm hệ thống, phần mềm nhúng, ứng dụng máy tính cá nhân, và nhiều lĩnh vực công nghiệp khác.

Photo by Uday Awal / Unsplash

C được chú trọng vào việc quản lý bộ nhớ và cung cấp nhiều tính năng gần gũi với cấu trúc máy tính, giúp lập trình viên có kiểm soát chi tiết hơn về cách dữ liệu và mã máy được xử lý. Nó cũng có một cú pháp đơn giản và mạnh mẽ, giúp trong việc phát triển phần mềm hiệu quả và dễ bảo trì.

Ngôn ngữ C đã tạo nền tảng cho nhiều ngôn ngữ lập trình khác, bao gồm C++, C#, và nhiều ngôn ngữ khác. Ngoài ra, nó cũng là ngôn ngữ phổ biến cho việc viết hệ điều hành và phần mềm nhúng do khả năng kiểm soát phần cứng của nó.

2. Ứng dụng của ngôn ngữ lập trình C

Ngôn ngữ lập trình C có nhiều ứng dụng khác nhau trong nhiều lĩnh vực khác nhau do tính linh hoạt, hiệu suất cao và khả năng tương tác gần gũi với cấu trúc máy tính. Dưới đây là một số ứng dụng phổ biến của ngôn ngữ lập trình C:

  1. Hệ điều hành: C được sử dụng rộng rãi trong việc phát triển hệ điều hành. Hầu hết những hệ điều hành phổ biến hiện nay đều được viết bằng C như UNIX, Linux, Windows hay MacOS.
  2. Phát triển phần mềm hệ thống: C là một ngôn ngữ lý tưởng để phát triển phần mềm hệ thống, chẳng hạn như trình quản lý cơ sở dữ liệu (SQLite, MySQL, Berkeley DB), trình quản lý tập tin (ext4 trên Linux và NTFS trên Windows), và các thành phần quan trọng của hệ thống máy tính.
  3. Phát triển ứng dụng nhúng: C thường được sử dụng trong việc phát triển phần mềm nhúng, tức là phần mềm chạy trên các thiết bị như điện thoại di động, thiết bị y tế, thiết bị điều khiển công nghiệp, hệ điều khiển động cơ, trò chơi điện tử và nhiều ứng dụng khác. Ví dụ các hệ máy chơi game như PlayStation và Xbox, hay hệ điều hành Android / iOS, đều được viết bằng C.
  4. Phát triển ứng dụng máy tính cá nhân: C vẫn được sử dụng để phát triển ứng dụng máy tính cá nhân, đặc biệt là trong các lĩnh vực đòi hỏi hiệu suất cao như các trò chơi máy tính và phần mềm đồ họa. Ví dụ như các tựa game trên steam được viết bằng Unity và Unreal Engine, phần lõi của các phần mềm này đều được viết bằng C.
  5. Phân tích số liệu và tính toán khoa học: C thường được sử dụng trong các ứng dụng liên quan đến tính toán khoa học và phân tích số liệu. Ví dụ các thư viện sử dụng trong lĩnh vực khoa học dữ liệu và máy học (machine learning) như OpenCV và TensorFlow cung cấp API cho C/C++ để phát triển các ứng dụng trong lĩnh vực này.
  6. Viết thư viện và framework: C thường được sử dụng để viết thư viện và framework mà các lập trình viên có thể sử dụng để phát triển ứng dụng cho nhiều mục đích khác nhau. Một số thư viện được viết bằng C như OpenGL, OpenSSL, GTK+ (GIMP Toolkit),...

3. Tại sao cần phải học ngôn ngữ C?

  1. Nền tảng kiến thức: C là một trong những ngôn ngữ lập trình cơ bản và cổ điển nhất. Hiểu rõ về C sẽ giúp bạn nắm vững các khái niệm cơ bản trong lập trình, cú pháp, và quản lý bộ nhớ, điều này sẽ giúp bạn dễ dàng học và làm việc với các ngôn ngữ khác. Vì C là nền tảng của mọi ngôn ngữ, các ngôn ngữ lập trình khác đều được xây dựng dựa trên C.
  2. Hiệu suất cao: C là một ngôn ngữ hiệu suất cao, cho phép bạn kiểm soát trực tiếp tài nguyên máy tính. Điều này rất quan trọng khi bạn cần phát triển ứng dụng yêu cầu xử lý nhanh hoặc đòi hỏi quản lý tài nguyên hiệu quả, chẳng hạn như hệ điều hành, phần mềm nhúng, hoặc ứng dụng đòi hỏi hiệu năng cao.
  3. Học cú pháp cấu trúc: Ngôn ngữ C dạy bạn cách sử dụng cú pháp cấu trúc, một kỹ thuật lập trình quan trọng. Điều này giúp bạn viết mã sạch sẽ, dễ đọc và bảo trì.
  4. Phát triển hệ điều hành và phần mềm hệ thống: C là ngôn ngữ phù hợp để phát triển hệ điều hành và các phần mềm hệ thống quan trọng. Nếu bạn quan tâm đến lĩnh vực này, học C là cần thiết.
  5. Hiểu cách hoạt động của máy tính: C cho phép bạn hiểu rõ hơn về cách máy tính hoạt động. Điều này sẽ giúp bạn hiểu rõ luồng hoạt động của mọi dòng code bạn viết ra và từ đấy, trở thành một lập trình viên giỏi hơn. Các lập trình viên nằm ở TOP đều phải nắm rõ cách mọi thứ vận hành.
  6. Giải quyết vấn đề: Học C giúp bạn phát triển kỹ năng tư duy lập trình và giải quyết vấn đề. Giải quyết nhiều bài toán khác nhau với C giúp bạn tiến tới giải được những bài toán thực tế có độ khó lớn hơn rất nhiều.

4. Ngôn ngữ C hoạt động như thế nào?

Quá trình biên dịch trong C chuyển đổi mã mà con người có thể đọc thành định dạng mà máy có thể đọc được. Đối với C, việc này xảy ra trước khi chương trình bắt đầu thực thi để kiểm tra cú pháp và ngữ nghĩa của mã. Quá trình biên dịch trong C bao gồm 4 bước: tiền xử lý (pre-processing), biên dịch (compiling), tập hợp (assembling) và liên kết (linking), sau đó chúng ta chạy file thực thi thu được để xuất ra màn hình kết quả.

4.1 Biên dịch là gì?

Giả sử có 2 người đang nói chuyện với nhau, anh A đến từ Úc nói tiếng Anh còn anh B đến từ Việt Nam. Để 2 anh này có thể hiểu nhau nói gì thì một trong hai người phải biết ngôn ngữ của người kia, ví dụ anh A sử dụng Google Dịch để dịch tiếng Anh sang tiếng Việt, quá trình dịch này có thể hiểu là quá trình biên dịch.

Đối với máy tính cũng vậy, bản thân máy tính chỉ hiểu các kí tự 01010101000, hay còn gọi là mã máy (Machine Code). Chính vì thế cần có trình biên dịch đứng ở giữa giúp chuyển đổi những dòng code ta viết ra ở dạng con người hiểu (ABC) sang dạng nhị phân (000 0000) để máy tính hiểu, các bạn có thể tìm hiểu thêm về ASCII để hiểu rõ hơn.

Bài tập c++ cơ bản và thực tế năm 2024
Biên dịch là gì? Nguồn: Scaler Topics

4.2 Quá trình biên dịch trong C

Quá trình biên dịch trong C diễn ra qua 4 bước: tiền xử lý (pre-processing), biên dịch (compiling), tập hợp (assembling) và liên kết (linking). Giờ chúng ta sẽ cùng nhau phân tích từng bước thông qua những ví dụ cụ thể nhé.

4.2.1 Pre-processing (tiền xử lý)

Tiền xử lý trong C là bước đầu tiên của quá trình biên dịch để thực hiện các thay đổi hoặc xử lý trước khi mã nguồn được biên dịch thành mã máy. Tiền xử lý trong C đầu tiên sẽ loại bỏ các dòng comment, sau đó sử dụng các hằng số (

define), macro (

define), và các chỉ thị (

include) để thực hiện các nhiệm vụ như định nghĩa hằng số, tạo các phiên bản mã, và nhập các tệp mã nguồn khác vào tệp mã nguồn hiện tại.

Comments Removal (Loại bỏ comment)

Các dòng comment trong chương trình C sẽ được loại bỏ trong bước tiền xử lý, do nó chỉ có chức năng là giải thích đoạn code chứ không được sử dụng khi biên dịch.

/* This is a 
 multi-line comment in C */

# include
int main()
{
    // this is a single-line comment in C
    return 0;
}

Những đoạn comment sẽ bị loại bỏ. Nguồn: Scaler Topics

Macros Expansion (Định nghĩa hằng số)

Việc định nghĩa sẵn các hằng số ở đầu chương trình giúp tạo ra một danh mục dễ đọc về các giá trị hằng số được sử dụng trong chương trình. Điều này làm cho mã nguồn dễ hiểu hơn, giúp lập trình viên và đồng nghiệp khác dễ dàng theo dõi và hiểu mã nguồn của bạn.


# define PI 3.14159265

# define MAX_SIZE 100

Ở ví dụ này,

gcc -o hello hello.o

9 được sử dụng để định nghĩa hằng số PI và MAX_SIZE. Khi tiền xử lý thực hiện, mọi sự xuất hiện của PI và MAX_SIZE trong mã nguồn sẽ được thay thế bằng giá trị tương ứng.

File inclusion (Nhập mã nguồn từ thư viện)

Để sử dụng được các hàm như

gcc -S -o hello hello.c

0 hay

gcc -S -o hello hello.c

1, ta cần sử dụng mã nguồn từ các thư viện bên ngoài. Thư viện điển hình mà chúng ta hay sử dụng trong C là thư viện

gcc -S -o hello hello.c

2. Để sử dụng thư viện này trong mã nguồn của chúng ta, ta viết như sau:


# include 

4.2.2 Compiling (Biên dịch)

Giai đoạn Compilation (Biên dịch) trong quá trình biên dịch mã nguồn C là bước quan trọng để chuyển mã nguồn C của bạn thành mã máy hoặc mã nguồn trung gian. Trong giai đoạn này, trình biên dịch (compiler) sẽ phân tích mã nguồn C và tạo ra mã máy hoặc mã nguồn trung gian. Dưới đây là một ví dụ cụ thể:

Giả sử bạn có một tệp mã nguồn C đơn giản như sau (ví dụ "hello.c"):


# include 
int main() {
    printf("Hello, World!\n");
    return 0;
}

Bây giờ, để biên dịch tệp này, bạn sử dụng trình biên dịch C, chẳng hạn như GCC trên hệ thống Unix/Linux. Sử dụng lệnh sau:

gcc -o hello hello.c

Trong lệnh trên:

  • gcc -S -o hello hello.c

    3 là trình biên dịch C.
  • gcc -S -o hello hello.c

    4 chỉ định tên của tệp thực thi đầu ra sẽ là "hello".
  • gcc -S -o hello hello.c

    5 là tệp mã nguồn C bạn muốn biên dịch.

Khi bạn chạy lệnh trên, trình biên dịch sẽ thực hiện giai đoạn Compilation. Trong giai đoạn này, nó thực hiện các nhiệm vụ sau:

  1. Phân tích cú pháp (Parsing): Trình biên dịch sẽ đọc mã nguồn C từ tệp "hello.c" và phân tích cú pháp của mã nguồn để hiểu cấu trúc và ngữ pháp của chương trình.
  2. Kiểm tra lỗi (Error Checking): Trình biên dịch kiểm tra chương trình để tìm lỗi cú pháp và lỗi logic. Nếu có lỗi, trình biên dịch sẽ tạo thông báo lỗi.
  3. Tạo mã nguồn trung gian (Intermediate Code): Trình biên dịch tạo mã trung gian (intermediate code) dựa trên mã nguồn C của bạn. Mã trung gian thể hiện các hướng dẫn trung gian cho máy tính về cách thực hiện chương trình.
  4. Tối ưu hóa (Optimization): Trong giai đoạn này, trình biên dịch có thể thực hiện tối ưu hóa để cải thiện hiệu suất của chương trình. Các tối ưu hóa bao gồm loại bỏ mã không sử dụng, tối ưu hóa động và tĩnh, và nhiều tối ưu hóa khác.
  5. Tạo tệp đối tượng (Object File): Cuối cùng, trình biên dịch tạo ra một tệp đối tượng (object file) hoặc tệp mã máy dựa trên mã trung gian. Tệp đối tượng chứa mã máy hoặc mã nguồn trung gian đã tối ưu hóa.

Sau giai đoạn Compilation, bạn có thể có một tệp đối tượng (với đuôi

gcc -S -o hello hello.c

  1. hoặc tệp thực thi (với tên "hello" trong ví dụ này) tùy thuộc vào cách bạn đã thiết lập trình biên dịch.

Lưu ý rằng một số trình biên dịch có thể tạo mã nguồn trung gian (intermediate code) thay vì mã máy, và mã nguồn trung gian này có thể được thực thi trong một máy ảo hoặc môi trường chạy giả lập (sandbox).

4.2.3 Assembly (Sử dụng mã hoá)

Assembly (Sử dụng mã hóa) là bước quan trọng sau khi mã nguồn C đã được biên dịch thành mã nguồn trung gian hoặc mã nguồn tương tự C. Trong giai đoạn này, mã nguồn trung gian hoặc mã nguồn tương tự C sẽ được chuyển đổi thành mã hợp ngữ (assembly code). Mã hợp ngữ là một dạng gần gũi với mã máy nhưng được viết bằng ngôn ngữ hợp ngữ, dễ đọc hơn và dễ hiểu hơn cho con người.

Giả sử bạn đã biên dịch tệp mã nguồn C "hello.c" thành tệp đối tượng "hello.o" sử dụng trình biên dịch GCC như sau:

gcc -o hello hello.c

Sau đó, bạn có thể sử dụng trình liên kết để tạo tệp thực thi "hello" bằng cách sử dụng lệnh:

gcc -o hello hello.o

Tại giai đoạn liên kết, mã hợp ngữ được tạo ra bởi trình biên dịch. Để xem mã hợp ngữ, bạn có thể sử dụng các tùy chọn đối với trình biên dịch hoặc trình liên kết để in ra mã hợp ngữ. Ví dụ, bạn có thể sử dụng tùy chọn

gcc -S -o hello hello.c

7 với GCC để tạo tệp mã hợp ngữ:

gcc -S -o hello hello.c

Sau đó, một tệp mã hợp ngữ "hello.s" sẽ được tạo ra, và bạn có thể xem nội dung của tệp này bằng trình soạn thảo hoặc trình biên tập mã nguồn.

Ví dụ nội dung tệp mã hợp ngữ "hello.s" có thể trông giống như sau:

    .section .data
hello:
    .string "Hello, World!\n"
    .section .text
.globl _start
_start:
    movl $4, %eax
    movl $1, %ebx
    movl $hello, %ecx
    movl $13, %edx
    int $0x80
    movl $1, %eax
    int $0x80

Mã hợp ngữ trên thể hiện mã máy tương ứng cho chương trình "Hello, World!" đơn giản. Nó sử dụng các chỉ thị gán giá trị, di chuyển dữ liệu và gọi hàm hệ thống để in ra thông báo "Hello, World!" trên màn hình.

Sau giai đoạn Assembly, mã hợp ngữ này sẽ được chuyển đổi thành mã máy thực thi trên máy tính của bạn.

4.2.4 Linking (Liên kết)

Linking (Liên kết) là bước quan trọng sau khi mã nguồn đã được biên dịch và có thể thực hiện riêng lẻ. Trong giai đoạn này, các tệp đối tượng và thư viện được kết hợp để tạo một tệp thực thi (executable file) hoàn chỉnh. Giai đoạn này cũng giải quyết các tham chiếu đến các hàm và biến từ các thư viện hoặc tệp đối tượng khác.

Giả sử bạn đã biên dịch hai tệp mã nguồn C thành hai tệp đối tượng "file1.o" và "file2.o" như sau:

gcc -c -o file1.o file1.c
gcc -c -o file2.o file2.c

Sau đó, bạn muốn liên kết hai tệp đối tượng này để tạo một tệp thực thi "myprogram". Bạn có thể sử dụng lệnh sau:


# define PI 3.14159265

# define MAX_SIZE 100

0

Trong ví dụ này:

  • gcc -S -o hello hello.c

    3 là trình liên kết (linker).
  • gcc -S -o hello hello.c

    9 chỉ định tên của tệp thực thi đầu ra sẽ là "myprogram".
  • .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    

    0 và

    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    
    1 là các tệp đối tượng cần được liên kết lại với nhau.

Khi bạn chạy lệnh trên, trình liên kết sẽ thực hiện các công việc sau:

  1. Giải quyết các tham chiếu: Trình liên kết kiểm tra xem các tham chiếu đến hàm và biến trong
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    

    0 và

    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    
    1 có thể được giải quyết bằng cách sử dụng mã máy từ các thư viện hoặc tệp đối tượng khác. Nếu có tham chiếu không được giải quyết, trình liên kết sẽ tạo lỗi liên kết.
  2. Kết hợp mã máy: Trình liên kết sẽ kết hợp mã máy từ
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    

    0 và

    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    
    1 để tạo một tệp thực thi hoàn chỉnh. Điều này bao gồm gắn các đoạn mã máy từ các tệp đối tượng vào một chương trình duy nhất.
  3. Xác định điểm bắt đầu: Trình liên kết cũng xác định điểm bắt đầu của chương trình (entry point). Thông thường, điểm bắt đầu là hàm
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    
    6.

Kết quả cuối cùng là một tệp thực thi "myprogram" mà bạn có thể chạy để thực hiện chương trình. Tệp thực thi này bao gồm mã máy đã được kết hợp từ các tệp đối tượng và thư viện, và nó thực hiện toàn bộ ứng dụng C của bạn.

Xem biểu đồ bên dưới để thấy trình tự diễn ra của quy trình biên dịch này

Bài tập c++ cơ bản và thực tế năm 2024
Flow Diagram (Nguồn: Scaler Topics)

5. Các khái niệm quan trọng trong ngôn ngữ C

5.1 Biến (Variable)

Biến trong ngôn ngữ lập trình C là một định danh được sử dụng để lưu trữ giá trị dữ liệu. Mỗi biến có một kiểu dữ liệu và một tên riêng biệt để xác định nó. Dưới đây là một số khái niệm quan trọng về biến trong C:

5.1.1 Khai báo biến (Variable Declaration)

Để tạo một biến trong C, bạn phải khai báo nó. Khai báo biến bao gồm kiểu dữ liệu và tên biến. Ví dụ:


# define PI 3.14159265

# define MAX_SIZE 100

1

Vậy điều gì xảy ra ở dưới khi chúng ta khai báo một biến trong C?

  • Không gian bộ nhớ được cấp phát: Khi bạn khai báo một biến, hệ thống cấp phát một phần của bộ nhớ cho biến đó. Kích thước của phần này phụ thuộc vào kiểu dữ liệu của biến. Ví dụ, một biến kiểu
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    
    7 thường cấp phát 4 byte (32 bit) trong bộ nhớ.
  • Địa chỉ bộ nhớ được gán cho biến: Mỗi biến sẽ có một địa chỉ bộ nhớ cụ thể, và bạn sử dụng tên biến để tham chiếu đến địa chỉ này trong mã nguồn của bạn.

5.1.2 Khởi tạo biến (Variable Initialization)

Bạn có thể khai báo và khởi tạo biến cùng một lúc. Ví dụ:


# define PI 3.14159265

# define MAX_SIZE 100

2

Việc khởi tạo biến trong C là không bắt buộc, tuy nhiên nó cũng có một số lợi ích. Dưới đây là một số lý do tại sao bạn nên khởi tạo biến:

  • Tránh giá trị rác: Khi bạn khởi tạo một biến, bạn cung cấp một giá trị ban đầu cho nó. Nếu bạn không khởi tạo biến, giá trị của biến có thể chứa giá trị rác từ vùng bộ nhớ trước đó, và điều này có thể dẫn đến lỗi không xác định hoặc hành vi không mong muốn.
  • Tạo mã dễ đọc và dễ bảo trì: Khởi tạo biến giúp làm cho mã nguồn dễ đọc hơn. Khi bạn đọc mã, bạn biết giá trị ban đầu của biến là gì, điều này giúp bạn hiểu mã nguồn nhanh hơn.
  • Bảo trì tính nhất quán: Khởi tạo biến giúp đảm bảo tính nhất quán trong mã nguồn. Điều này làm cho mã dễ bảo trì hơn bởi vì bạn biết rằng biến luôn có một giá trị ban đầu đã định.
  • Tránh lỗi logic: Khởi tạo biến có thể giúp bạn phát hiện và tránh lỗi logic trong mã nguồn. Khi bạn sử dụng một biến mà bạn đã quên khởi tạo, bạn có thể gặp lỗi không mong muốn khi sử dụng giá trị của nó.

5.1.3 Tên biến (Variable Name)

Tên biến là một định danh duy nhất được sử dụng để xác định biến. Tên biến phải tuân theo các quy tắc về cú pháp và không được trùng với từ khóa trong ngôn ngữ C.

Có một số quy tắc cơ bản trong việc đặt tên biến trong C:

  • Chỉ sử dụng chữ cái, số và dấu gạch dưới: Tên biến chỉ có thể bắt đầu bằng một chữ cái (a-z hoặc A-Z), một số (0-9) hoặc một dấu gạch dưới (_). Những ký tự sau có thể bao gồm chữ cái, số và dấu gạch dưới.
  • Không sử dụng dấu cách: Tên biến không được chứa khoảng trắng hoặc dấu cách. Nếu bạn muốn kết hợp từ hoặc cụm từ, bạn có thể sử dụng dấu gạch dưới hoặc chữ hoa để phân biệt, ví dụ:
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    

    8,

    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    
    9.
  • Không sử dụng từ khóa của ngôn ngữ: Tên biến không được trùng với từ khóa hoặc các từ được định nghĩa trước trong ngôn ngữ C, chẳng hạn như
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    

    7,

    gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c

    1,

    gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c

    2,

    gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c

    3, v.v.
  • Phân biệt chữ hoa và chữ thường: Các biến trong C phân biệt chữ hoa và chữ thường. Ví dụ,
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    

    9 và

    gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c

    5 là hai biến khác nhau.
  • Chọn tên biến có ý nghĩa: Tên biến nên phản ánh mục đích hoặc nhiệm vụ của biến. Điều này làm cho mã nguồn dễ đọc và dễ bảo trì hơn. Ví dụ, thay vì sử dụng

    gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c

    6 hoặc

    gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c

    7, bạn nên sử dụng tên biến mô tả giá trị của nó, chẳng hạn

    gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c

    8,

    gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c

    9,

    `

define PI 3.14159265

define MAX_SIZE 100
00, v.v.
  • Sử dụng kiểu Camel Case hoặc Snake Case: Có hai phong cách phổ biến để đặt tên biến: Camel Case và Snake Case. Trong Camel Case, từ đầu tiên bắt đầu bằng chữ thường, sau đó mỗi từ tiếp theo bắt đầu bằng chữ in hoa, ví dụ: `
# define PI 3.14159265 # define MAX_SIZE 100

01. Trong Snake Case, các từ được phân tách bằng dấu gạch dưới, ví dụ:


# define PI 3.14159265  

# define MAX_SIZE 100  
02. Cả hai phong cách đều được chấp nhận, nhưng quan trọng là duy nhất trong mã nguồn của bạn.

  • Giới hạn độ dài tên biến: Tên biến nên ngắn gọn và dễ đọc. Tránh đặt tên biến quá dài hoặc quá ngắn.

5.1.4 Gán giá trị (Assignment)

Để gán một giá trị mới cho biến, bạn sử dụng toán tử gán (=). Ví dụ:


# define PI 3.14159265

# define MAX_SIZE 100

3

Khi bạn gán giá trị cho một biến trong ngôn ngữ lập trình C, bạn đang thực hiện thao tác gán, tức là bạn đang đặt giá trị mới cho biến. Dưới đây là một số điểm quan trọng về quá trình gán giá trị này:

  • Gán giá trị mới: Gán giá trị cho biến có nghĩa là bạn thay đổi giá trị của biến bằng giá trị mới.


# define PI 3.14159265

# define MAX_SIZE 100

4

  • Thay đổi giá trị biến: Gán giá trị mới cho biến thay đổi giá trị của nó và làm cho giá trị trước đó bị mất đi. Giá trị mới được lưu trong biến.
  • Kiểu dữ liệu phải phù hợp: Giá trị bạn gán cho biến phải phù hợp với kiểu dữ liệu của biến. Ví dụ, nếu biến là kiểu
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    
    7, bạn chỉ có thể gán giá trị nguyên cho nó.
  • Gán có thể kết hợp với các biểu thức: Bạn có thể kết hợp gán với các biểu thức, ví dụ:


# define PI 3.14159265

# define MAX_SIZE 100

5

  • Thứ tự trong biểu thức gán: Các ngôn ngữ lập trình xác định một thứ tự cụ thể cho các biểu thức gán. Điều này có thể ảnh hưởng đến kết quả của biểu thức, đặc biệt trong trường hợp của biểu thức phức tạp. Ví dụ:


# define PI 3.14159265

# define MAX_SIZE 100

6

5.1.5 Phạm vi biến (Variable Scope)

Phạm vi của một biến xác định nơi mà biến có thể được truy cập. Biến cục bộ (local variables) chỉ có thể truy cập trong phạm vi của hàm hoặc khối mã trong đó chúng được khai báo, trong khi biến toàn cục (global variables) có thể truy cập từ mọi nơi trong chương trình.

Biến Cục Bộ (Local Variable):


# define PI 3.14159265

# define MAX_SIZE 100

7

Trong ví dụ trên, biến

gcc -c -o file1.o file1.c
gcc -c -o file2.o file2.c

6 và


# define PI 3.14159265

# define MAX_SIZE 100

05 là biến cục bộ. Biến

gcc -c -o file1.o file1.c
gcc -c -o file2.o file2.c

6 được khai báo trong hàm

    .section .data
hello:
    .string "Hello, World!\n"
    .section .text
.globl _start
_start:
    movl $4, %eax
    movl $1, %ebx
    movl $hello, %ecx
    movl $13, %edx
    int $0x80
    movl $1, %eax
    int $0x80

6, và biến


# define PI 3.14159265

# define MAX_SIZE 100

05 được khai báo trong khối mã của câu lệnh

gcc -c -o file1.o file1.c
gcc -c -o file2.o file2.c

1. Biến

gcc -c -o file1.o file1.c
gcc -c -o file2.o file2.c

6 có phạm vi trong toàn bộ hàm

    .section .data
hello:
    .string "Hello, World!\n"
    .section .text
.globl _start
_start:
    movl $4, %eax
    movl $1, %ebx
    movl $hello, %ecx
    movl $13, %edx
    int $0x80
    movl $1, %eax
    int $0x80

6, trong khi biến


# define PI 3.14159265

# define MAX_SIZE 100

05 chỉ có phạm vi trong khối mã của câu lệnh

gcc -c -o file1.o file1.c
gcc -c -o file2.o file2.c

1. Vì vậy, sau khi chương trình rời khỏi khối mã

gcc -c -o file1.o file1.c
gcc -c -o file2.o file2.c

1, biến


# define PI 3.14159265

# define MAX_SIZE 100

05 không còn tồn tại.

Biến Toàn Cục (Global Variable):


# define PI 3.14159265

# define MAX_SIZE 100

8

Trong ví dụ này, biến


# define PI 3.14159265

# define MAX_SIZE 100

16 là biến toàn cục, có thể truy cập từ bất kỳ hàm nào trong chương trình. Biến

gcc -c -o file1.o file1.c
gcc -c -o file2.o file2.c

6 là biến cục bộ, chỉ có phạm vi trong hàm

    .section .data
hello:
    .string "Hello, World!\n"
    .section .text
.globl _start
_start:
    movl $4, %eax
    movl $1, %ebx
    movl $hello, %ecx
    movl $13, %edx
    int $0x80
    movl $1, %eax
    int $0x80

6. Khi bạn muốn sử dụng biến toàn cục, bạn có thể thực hiện điều đó từ bất kỳ hàm nào trong chương trình, như trong ví dụ trên.

5.2 Kiểu dữ liệu (Data Type)

C có một loạt các kiểu dữ liệu mà bạn có thể sử dụng để định nghĩa và lưu trữ dữ liệu. Dưới đây là một số kiểu dữ liệu thường gặp trong C:

  • int: Kiểu dữ liệu nguyên (integer) được sử dụng để lưu trữ các số nguyên. Ví dụ: `

define PI 3.14159265

define MAX_SIZE 100
19
  • float: Kiểu dữ liệu số thực đơn (floating-point) được sử dụng để lưu trữ các số thập phân. Ví dụ: `
# define PI 3.14159265 # define MAX_SIZE 100

20

  • double: Kiểu dữ liệu số thực kép (double-precision floating-point) tương tự như `

define PI 3.14159265

define MAX_SIZE 100
21, nhưng có độ chính xác cao hơn. Ví dụ:  

define PI 3.14159265

define MAX_SIZE 100
22
  • char: Kiểu dữ liệu ký tự (character) được sử dụng để lưu trữ một ký tự. Ví dụ: `
# define PI 3.14159265 # define MAX_SIZE 100

23

  • long int: Kiểu dữ liệu số nguyên dài (long integer) có kích thước lớn hơn so với
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    

    7. Ví dụ:

    `

define PI 3.14159265

define MAX_SIZE 100
25
  • unsigned int: Kiểu dữ liệu số nguyên không dấu (unsigned integer) để lưu trữ các số nguyên không âm (tức là không có dấu). Ví dụ: `
# define PI 3.14159265 # define MAX_SIZE 100

26

  • unsigned char: Kiểu dữ liệu ký tự không dấu (unsigned character) để lưu trữ ký tự không dấu. Ví dụ: `

define PI 3.14159265

define MAX_SIZE 100
27

### 5.3 In và đọc dữ liệu

C cung cấp sẵn cho chúng ta 2 hàm là 

gcc -S -o hello hello.c


0 và 
gcc -S -o hello hello.c

1 dùng để in và đọc dữ liệu.

#### 5.3.1 printf 

Được sử dụng để in dữ liệu ra màn hình theo một định dạng cụ thể.'

  • Cú pháp

define PI 3.14159265

define MAX_SIZE 100

9
  • Ví dụ:

include


0

#### 5.3.2 scanf

Được sử dụng để đọc dữ liệu từ bàn phím vào biến theo một định dạng cụ thể.

  • Cú pháp

include


1
  • Ví dụ:

include


2

Lưu ý rằng trong 
gcc -S -o hello hello.c

1, bạn phải sử dụng dấu 

define PI 3.14159265

define MAX_SIZE 100

31 trước tên biến để truyền địa chỉ của biến đó cho 

gcc -S -o hello hello.c


1\. Điều này là cần thiết để 
gcc -S -o hello hello.c

1 có thể ghi dữ liệu vào biến được chỉ định.

#### 5.3.3 Định dạng dữ liệu

Các kí hiệu như 

define PI 3.14159265

define MAX_SIZE 100

34 hay 

define PI 3.14159265

define MAX_SIZE 100

35 được sử dụng trong 

gcc -S -o hello hello.c


0 với 
gcc -S -o hello hello.c

1 được gọi là định dạng của dữ liệu. Để định dạng cách in hay đọc dữ liệu, C đã cung cấp sẵn cho chúng ta các loại định dạng sau:

  • %c : Ký tự đơn
  • %s : Chuỗi
  • %d : Số nguyên hệ 10 có dấu
  • %f : Số chấm động (VD 7.44 khi in sẽ ra 7.440000)
  • %e : Số chấm động (ký hiệu có số mũ)
  • %g : Số chấm động (VD 7.44 khi in sẽ in ra 7.44)
  • %x : Số nguyên hex không dấu (hệ 16)
  • %o : Số nguyên bát phân không dấu (hệ 8)
  • %p : Địa chỉ con trỏ
### 5.4 Hàm (Function) Trong ngôn ngữ lập trình C, hàm (function) là một khối mã thực hiện một tác vụ cụ thể. Hàm là một phần quan trọng của chương trình C, vì chúng giúp bạn chia nhỏ chương trình thành các phần nhỏ dễ quản lý và sử dụng lại. Dưới đây là cách định nghĩa và sử dụng hàm trong C: #### 5.4.1 Cú pháp của hàm Một hàm trong C được định nghĩa bằng cú pháp sau:

include


3
  • `
# define PI 3.14159265 # define MAX_SIZE 100
38: Đây là kiểu dữ liệu của giá trị mà hàm sẽ trả về. Nếu hàm không trả về giá trị, bạn sử dụng

# define PI 3.14159265  

# define MAX_SIZE 100  
39.

  • `

define PI 3.14159265

define MAX_SIZE 100
40: Đây là tên của hàm, bạn tự đặt tên cho hàm. Tên hàm phải tuân theo quy tắc đặt tên biến.
  • `
# define PI 3.14159265 # define MAX_SIZE 100

41: Đây là các tham số (có thể là không có tham số) mà hàm có thể nhận vào. Tham số là các giá trị mà bạn truyền cho hàm để thực hiện các công việc cụ thể.

  • `

define PI 3.14159265

define MAX_SIZE 100
42: Đây là nơi bạn đặt mã nguồn của hàm, thực hiện các công việc cụ thể.
  • `
# define PI 3.14159265 # define MAX_SIZE 100

43: Nếu hàm trả về một giá trị, bạn sử dụng lệnh

gcc -c -o file1.o file1.c  
gcc -c -o file2.o file2.c  
3 để trả giá trị đó. Nếu hàm không trả về giá trị hoặc chỉ có mục tiêu thực hiện công việc mà không cần trả về giá trị, bạn sử dụng
gcc -c -o file1.o file1.c  
gcc -c -o file2.o file2.c  
3 với

# define PI 3.14159265  

# define MAX_SIZE 100  
39.

5.4.2 Gọi hàm

Để sử dụng hàm, bạn gọi hàm bằng tên của nó cùng với các đối số (nếu có). Ví dụ:


# include 

4

5.4.3 Hàm có hoặc không có giá trị trả về

Hàm có thể có giá trị trả về hoặc không. Ví dụ:


# include 

5

5.4.4 Hàm nguyên mẫu (Function Prototype)

Trước khi sử dụng hàm trong một tệp mã nguồn khác, bạn nên định nghĩa hàm nguyên mẫu (function prototype) ở đầu tệp mã hoặc bao gồm tệp tiêu đề chứa định nghĩa hàm. Hàm nguyên mẫu chỉ ra kiểu dữ liệu trả về của hàm và kiểu dữ liệu của các tham số nó có. Điều này giúp trình biên dịch biết cách gọi hàm và kiểm tra sự phù hợp của đối số và kiểu trả về.


# include 

6

5.5 Chuỗi (String)

Trong C, chuỗi (string) thường được biểu diễn bằng một mảng các ký tự (


# define PI 3.14159265

# define MAX_SIZE 100

47). Mảng này chứa các ký tự liên tiếp kết thúc bằng một ký tự null (


# define PI 3.14159265

# define MAX_SIZE 100

  1. để xác định kết thúc của chuỗi. Dưới đây là cách bạn có thể định nghĩa, khai báo và làm việc với chuỗi trong C:

5.5.1 Định nghĩa chuỗi

Bạn có thể khai báo một mảng ký tự cố định để định nghĩa chuỗi. Độ dài của mảng cố định phải đủ lớn để chứa chuỗi và ký tự null cuối cùng. Ví dụ:


# include 

7

5.5.2 Làm việc với chuỗi

Có nhiều cách để thao tác và làm việc với chuỗi, dưới đây là một số cách:

  • In chuỗi: Để hiển thị một chuỗi trong C lên màn hình chúng ta sẽ dùng kí hiệu `

define PI 3.14159265

define MAX_SIZE 100
35

include


8
  • Sao chép chuỗi: Bạn có thể sử dụng hàm `
# define PI 3.14159265 # define MAX_SIZE 100
50 để sao chép một chuỗi vào một chuỗi khác.

# include 

9

  • Nối chuỗi: Để nối một chuỗi vào cuối chuỗi khác, bạn có thể sử dụng hàm `

define PI 3.14159265

define MAX_SIZE 100
51.

include

int main() {

printf("Hello, World!\n");
return 0;
}

0
  • So sánh chuỗi: Hàm `
# define PI 3.14159265 # define MAX_SIZE 100
52 được sử dụng để so sánh hai chuỗi.

# include 
int main() {
    printf("Hello, World!\n");
    return 0;
}

1

  • Tính độ dài chuỗi: Để tính độ dài của chuỗi, bạn có thể sử dụng hàm `

define PI 3.14159265

define MAX_SIZE 100
53.

include

int main() {

printf("Hello, World!\n");
return 0;
}

2

Lưu ý rằng chuỗi kết thúc bằng ký tự null 

define PI 3.14159265

define MAX_SIZE 100

48, nên khi bạn làm việc với chuỗi, hãy đảm bảo rằng bạn duyệt đến ký tự null để xác định kết thúc của chuỗi.

### 5.6 Mảng một chiều (Array)

Mảng (array) trong C là một tập hợp các phần tử có cùng kiểu dữ liệu được sắp xếp theo thứ tự. Mảng cho phép bạn lưu trữ nhiều giá trị cùng loại trong một biến duy nhất. 

#### 5.6.1 Định nghĩa và Khai báo Mảng

Để định nghĩa một mảng trong C, bạn cần chỉ định kiểu dữ liệu của các phần tử và kích thước của mảng (số lượng phần tử). Cú pháp tổng quan như sau:

include

int main() {

printf("Hello, World!\n");
return 0;
}

3
  • `
# define PI 3.14159265 # define MAX_SIZE 100
38: Đây là kiểu dữ liệu của các phần tử trong mảng, ví dụ
    .section .data  
hello:  
    .string "Hello, World!\n"  
    .section .text  
.globl _start  
_start:  
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
7,

# define PI 3.14159265  

# define MAX_SIZE 100  
21,

# define PI 3.14159265  

# define MAX_SIZE 100  
47, ...

  • `

define PI 3.14159265

define MAX_SIZE 100
59: Đây là tên của mảng.
  • `
# define PI 3.14159265 # define MAX_SIZE 100

60: Số lượng phần tử trong mảng.

Ví dụ:


# include 
int main() {
    printf("Hello, World!\n");
    return 0;
}

4

5.6.2 Truy cập phần tử trong Mảng

Bạn có thể truy cập các phần tử trong mảng bằng cách sử dụng chỉ mục (index) của phần tử trong dấu ngoặc vuông


# define PI 3.14159265

# define MAX_SIZE 100

61. Chỉ mục bắt đầu từ 0. Ví dụ:


# include 
int main() {
    printf("Hello, World!\n");
    return 0;
}

5

5.7 Mảng hai chiều (2D Array)

Mảng hai chiều (2D array) trong ngôn ngữ lập trình C là một cấu trúc dữ liệu cho phép bạn lưu trữ dữ liệu dưới dạng ma trận hoặc lưới có hai chiều (hàng và cột).

5.7.1 Định nghĩa và Khai báo Mảng hai chiều


# include 
int main() {
    printf("Hello, World!\n");
    return 0;
}

6

  • `

define PI 3.14159265

define MAX_SIZE 100
38: Đây là kiểu dữ liệu của các phần tử trong mảng 2D, ví dụ  
.section .data  
hello:
.string "Hello, World!\n"  
.section .text  
.globl _start

_start:

movl $4, %eax  
movl $1, %ebx  
movl $hello, %ecx  
movl $13, %edx  
int $0x80  
movl $1, %eax  
int $0x80  
` 7,

# define PI 3.14159265  

# define MAX_SIZE 100  
21,

# define PI 3.14159265  

# define MAX_SIZE 100  
47, ...

  • `

define PI 3.14159265

define MAX_SIZE 100
59: Đây là tên của mảng.
  • `
# define PI 3.14159265 # define MAX_SIZE 100

67: Số lượng hàng trong mảng.

  • `

define PI 3.14159265

define MAX_SIZE 100
68: Số lượng cột trong mảng.

Ví dụ:

include

int main() {

printf("Hello, World!\n");
return 0;
}

7

#### 5.7.2 Truy cập phần tử trong Mảng hai chiều

Bạn có thể truy cập các phần tử trong mảng 2D bằng cách sử dụng chỉ mục của hàng và cột trong dấu ngoặc vuông 

define PI 3.14159265

define MAX_SIZE 100

69\. Chỉ mục hàng và cột bắt đầu từ 0\. Ví dụ:

include

int main() {

printf("Hello, World!\n");
return 0;
}

8

Mảng hai chiều thường được sử dụng để biểu diễn ma trận, hình ảnh, dữ liệu lưới và nhiều loại dữ liệu có cấu trúc tương tự.

### 5.8 Câu lệnh điều kiện (Conditional Statement)

#### 5.8.1 Câu lệnh 

define PI 3.14159265

define MAX_SIZE 100

70

Câu lệnh 

define PI 3.14159265

define MAX_SIZE 100

70 cho phép bạn thực hiện một khối mã nguồn nếu điều kiện kiểm tra là đúng (true), và một khối mã nguồn khác nếu điều kiện là sai (false). Cú pháp tổng quan như sau:

include

int main() {

printf("Hello, World!\n");
return 0;
}

9

Ví dụ:
gcc -o hello hello.c

0

#### 5.8.2 Câu lệnh 

define PI 3.14159265

define MAX_SIZE 100

72

Câu lệnh 

define PI 3.14159265

define MAX_SIZE 100

73 cho phép bạn kiểm tra nhiều điều kiện liên tiếp và thực hiện các khối mã nguồn tương ứng với điều kiện đúng đầu tiên. Cú pháp tổng quan như sau:

gcc -o hello hello.c


1

Ví dụ: 
gcc -o hello hello.c

2

#### 5.8.3 Câu lệnh 

define PI 3.14159265

define MAX_SIZE 100

74

Câu lệnh 

define PI 3.14159265

define MAX_SIZE 100

74 cho phép bạn kiểm tra một biểu thức hoặc giá trị và thực hiện các tác vụ tương ứng với từng giá trị. Cú pháp tổng quan như sau:

gcc -o hello hello.c


3

Ví dụ: 
gcc -o hello hello.c

4

### 5.9 Vòng lặp (Loop)

Trong ngôn ngữ lập trình C, có ba loại vòng lặp chính được sử dụng để lặp qua mã nguồn hoặc thực hiện các tác vụ lặp đi lặp lại. Các loại vòng lặp này là:

#### 5.9.1 Vòng lặp for

Vòng lặp for được sử dụng để lặp qua một khối mã nguồn một số lần xác định. Cú pháp tổng quan như sau:
gcc -o hello hello.c

5

Ví dụ:
gcc -o hello hello.c

6

#### 5.9.2 Vòng lặp 

define PI 3.14159265

define MAX_SIZE 100

76

Vòng lặp 

gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c


2 được sử dụng để lặp qua một khối mã nguồn trong khi một điều kiện là đúng (true). Cú pháp tổng quan như sau:
gcc -o hello hello.c

7

Ví dụ: 
gcc -o hello hello.c

8

#### 5.9.3 Vòng lặp 

define PI 3.14159265

define MAX_SIZE 100

78: 

Vòng lặp 

define PI 3.14159265

define MAX_SIZE 100

79 tương tự với 

gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c


2, nhưng khối mã trong vòng lặp được thực hiện ít nhất một lần trước khi kiểm tra điều kiện. Cú pháp tổng quan như sau:
gcc -o hello hello.c

9

Ví dụ: 
gcc -o hello hello.c

0

#### 5.9.4 Sự khác nhau giữa 3 loại vòng lặp

Sự khác nhau chính giữa ba vòng lặp chính trong ngôn ngữ lập trình C (for, while, và do...while) là cách chúng kiểm tra và điều khiển việc lặp. Dưới đây là sự khác nhau giữa chúng và trường hợp nào nên sử dụng mỗi loại vòng lặp:

 Vòng lặp for Vòng lặp while Vòng lặp do...while Cú pháp sử dụng một biểu thức khởi tạo, một biểu thức điều kiện, và một biểu thức cập nhật sử dụng một biểu thức điều kiện sử dụng một biểu thức điều kiện Thứ tự kiểm tra điều kiện Đầu tiên kiểm tra điều kiện, sau đó thực hiện khối mã nếu điều kiện là true. Sau khi thực hiện khối mã, biểu thức cập nhật được thực hiện và sau đó kiểm tra lại điều kiện. Đầu tiên kiểm tra điều kiện, sau đó thực hiện khối mã nếu điều kiện là true. Không thực hiện nếu điều kiện là false. Thực hiện khối mã ít nhất một lần, sau đó kiểm tra điều kiện. Nếu điều kiện là true, thực hiện thêm vòng lặp. Thích hợp cho Sử dụng khi bạn biết trước số lần lặp cụ thể hoặc cần thực hiện một số lần lặp cố định. Sử dụng khi bạn không biết trước số lần lặp và muốn thực hiện lặp dựa trên điều kiện nào đó. Sử dụng khi bạn muốn đảm bảo rằng khối mã ít nhất được thực hiện một lần, sau đó kiểm tra điều kiện để quyết định liệu cần tiếp tục lặp hay không.

### 5.10 Struct (Cấu trúc)

Struct (cấu trúc) trong ngôn ngữ lập trình C là một cấu trúc dữ liệu cho phép bạn tổng hợp nhiều biến có kiểu dữ liệu khác nhau vào một đối tượng duy nhất. Cấu trúc giúp bạn tạo ra kiểu dữ liệu tùy chỉnh để biểu diễn các thực thể có tính chất phức tạp hơn. Dưới đây là cách bạn định nghĩa và làm việc với cấu trúc trong C:

#### 5.10.1 Định nghĩa cấu trúc

Để định nghĩa cấu trúc trong C, bạn sử dụng từ khóa 

define PI 3.14159265

define MAX_SIZE 100

81 theo sau là tên cấu trúc và danh sách các biến thành viên (members) trong cấu trúc. Cú pháp tổng quan như sau:

gcc -o hello hello.c


1

Ví dụ:
gcc -o hello hello.c

2

#### 5.10.2 Khai báo biến kiểu struct trong C

Việc khai báo biến với struct cũng giống như cách khai báo biến thông thường, trong đó kiểu dữ liệu là kiểu struct trong C mà bạn vừa định nghĩa.
gcc -o hello hello.c

3

#### 5.10.3 Truy xuất các thuộc tính của struct

C cung cấp cho chúng ta 2 toán tử để truy xuất các thuộc tính của struct:

  • Sử dụng `
# define PI 3.14159265 # define MAX_SIZE 100
82 => Truy xuất tới thuộc tính khi khai báo biến bình thường.

  • Sử dụng `

define PI 3.14159265

define MAX_SIZE 100
83 => Truy xuất tới thuộc tính khi biến là con trỏ.

Ví dụ chúng ta muốn truy xuất đến thuộc tính 

define PI 3.14159265

define MAX_SIZE 100

84 của Person ta làm như sau:

gcc -o hello hello.c


4

### 5.11 Enum (Liệt kê)

Enum (liệt kê) trong ngôn ngữ lập trình C là một cấu trúc dữ liệu cho phép bạn xác định một tập hợp các giá trị nguyên có tên. Thông thường, liệt kê được sử dụng để đặt tên cho các hằng số hoặc để tạo các biểu thức nguyên với ý nghĩa dễ đọc. Dưới đây là cách bạn định nghĩa và làm việc với liệt kê trong C:

#### 5.11.1 Định nghĩa enum

Để định nghĩa enum trong C, bạn sử dụng từ khóa 

define PI 3.14159265

define MAX_SIZE 100

85 sau đó là tên liệt kê và danh sách các giá trị liệt kê trong dấu ngoặc nhọn 

define PI 3.14159265

define MAX_SIZE 100

86\. Ví dụ:

gcc -o hello hello.c


5

Trong ví dụ này, 

define PI 3.14159265

define MAX_SIZE 100

87 là tên của liệt kê, và 

define PI 3.14159265

define MAX_SIZE 100

88, 

define PI 3.14159265

define MAX_SIZE 100

89, 

define PI 3.14159265

define MAX_SIZE 100

90, và 

define PI 3.14159265

define MAX_SIZE 100

91 là các giá trị liệt kê. Mặc định, giá trị của các trạng thái trong enum là các số nguyên liên tiếp, bắt đầu từ 0\. 

define PI 3.14159265

define MAX_SIZE 100

88 có giá trị là 0, 

define PI 3.14159265

define MAX_SIZE 100

89 là 1, và tiếp tục như vậy.

#### 5.11.2 Gán giá trị tùy chỉnh cho các trạng thái

Bạn có thể gán giá trị tùy chỉnh cho các trạng thái của enum bằng cách chỉ định giá trị sau mỗi trạng thái. Ví dụ:

gcc -o hello hello.c


6

Trong ví dụ này, 

define PI 3.14159265

define MAX_SIZE 100

94 có giá trị là 1, và các giá trị tiếp theo sẽ được tự động tăng thêm một đơn vị (2, 3, 4,...).

#### 5.11.3 Sử dụng enum

Sau khi bạn đã định nghĩa enum, bạn có thể sử dụng nó để khai báo biến hoặc trong các biểu thức. Ví dụ:

gcc -o hello hello.c


7

Ngoài ra bạn có thể sử dụng enum làm tham số cho hàm để biểu thị trạng thái hoặc tùy chọn.
gcc -o hello hello.c

8
Enum giúp làm cho mã nguồn dễ đọc hơn và giúp tránh sử dụng các số nguyên cứng.
### 5.12 Thư viện (Library) Trong ngôn ngữ lập trình C, thư viện (library) là một tập hợp các tệp và hàm được biên dịch sẵn (compiled) mà bạn có thể sử dụng trong các chương trình của mình. Thư viện giúp tái sử dụng mã nguồn, giảm độ phức tạp của chương trình, và tăng hiệu suất trong quá trình phát triển phần mềm. Dưới đây là một số điểm quan trọng về thư viện trong ngôn ngữ C:
  1. Thư viện tiêu chuẩn (Standard Library): C có một thư viện tiêu chuẩn chứa các hàm và tệp thư viện đã được định nghĩa trước để thực hiện các nhiệm vụ phổ biến. Ví dụ, thư viện gcc -S -o hello hello.c 2 chứa hàm liên quan đến đầu vào và đầu ra, như gcc -S -o hello hello.c 0, gcc -S -o hello hello.c 1, và `
# define PI 3.14159265 # define MAX_SIZE 100
98. Thư viện tiêu chuẩn được cung cấp bởi ngôn ngữ C và có sẵn cho mọi trình biên dịch C.

  1. Thư viện bên ngoài (External Libraries): Bên cạnh thư viện tiêu chuẩn, có nhiều thư viện bên ngoài được tạo ra bởi cộng đồng lập trình C và các nhà sản xuất trình biên dịch. Những thư viện này cung cấp các tính năng đặc biệt và phức tạp hơn cho việc phát triển ứng dụng. Để sử dụng thư viện bên ngoài, bạn cần bao gồm tiêu đề (header) thư viện trong mã nguồn của mình và liên kết (link) với tệp thư viện khi biên dịch.
  2. Tiêu đề (Header Files): Tiêu đề (header) là tệp văn bản có phần mở rộng `

define PI 3.14159265

define MAX_SIZE 100
99 chứa các khai báo hàm, cấu trúc dữ liệu và hằng số mà bạn cần sử dụng từ một thư viện. Để sử dụng thư viện, bạn cần đưa tiêu đề của nó vào mã nguồn của mình bằng cách sử dụng  

include
00\. Ví dụ:  

include
01.
  1. Liên kết (Linking): Sau khi bạn đã sử dụng các hàm từ thư viện trong mã nguồn của mình, bạn cần liên kết mã nguồn với thư viện khi biên dịch. Điều này đảm bảo rằng trình biên dịch biết nơi tìm các định nghĩa hàm cụ thể. Thông thường, việc liên kết được thực hiện bằng cách sử dụng câu lệnh liên kết hoặc trình biên dịch.
  2. Tạo thư viện tùy chỉnh (Custom Libraries): Bạn cũng có thể tạo thư viện tùy chỉnh để đóng gói và tái sử dụng mã nguồn của mình. Điều này đặc biệt hữu ích khi bạn muốn chia sẻ mã nguồn với các dự án khác hoặc đảm bảo tính riêng tư và bảo mật của mã nguồn.
    Thư viện đóng một vai trò quan trọng trong việc phát triển ứng dụng C và giúp tối ưu hóa quá trình phát triển phần mềm bằng cách sử dụng lại mã nguồn đã có sẵn.
### 5.13 Con trỏ (Pointer) Con trỏ (Pointer) trong ngôn ngữ lập trình C là một biến đặc biệt được sử dụng để lưu trữ địa chỉ bộ nhớ của một biến khác. Con trỏ cho phép bạn truy cập và thao tác trực tiếp với bộ nhớ và dữ liệu, và nó là một trong những tính năng quan trọng của C. Dưới đây là các khái niệm và cách sử dụng con trỏ trong C: Khai báo con trỏ Để khai báo một con trỏ, bạn sử dụng dấu

include


02 sau kiểu dữ liệu. Ví dụ:
gcc -o hello hello.c

9

Gán giá trị cho con trỏ

Con trỏ có thể được gán bất kỳ địa chỉ của biến nào cùng kiểu dữ liệu với nó. Điều này được thực hiện bằng cách sử dụng toán tử 

define PI 3.14159265

define MAX_SIZE 100

31 để lấy địa chỉ của biến. Ví dụ:

gcc -o hello hello.o


0

Truy cập giá trị của con trỏ

Để truy cập giá trị mà con trỏ đang trỏ đến, bạn sử dụng toán tử 

include


02\. Ví dụ:
gcc -o hello hello.o

1

Sử dụng con trỏ để thay đổi giá trị biến

Con trỏ cho phép bạn thay đổi giá trị của biến mà nó đang trỏ đến. Ví dụ:
gcc -o hello hello.o

2

Thực hiện các phép tính với con trỏ

Bạn có thể thực hiện các phép tính số học và logic với con trỏ để di chuyển giữa các địa chỉ bộ nhớ và thực hiện các phép tính trên dữ liệu. Ví dụ:
gcc -o hello hello.o

3

Con trỏ và mảng

Mảng và con trỏ trong C có mối quan hệ mật thiết. Tên mảng một cách tường minh là một con trỏ tới phần tử đầu tiên trong mảng, và bạn có thể sử dụng con trỏ để truy cập các phần tử của mảng.

Dưới đây là một ví dụ về cách sử dụng con trỏ và mảng trong ngôn ngữ lập trình C. Trong ví dụ này, chúng ta sẽ tạo một mảng các số nguyên và sử dụng một con trỏ để truy cập các phần tử của mảng.
gcc -o hello hello.o

4
  • Chúng ta đã khai báo một mảng `
# include
05 có 5 phần tử và khởi tạo nó với các giá trị từ 10 đến 50.

  • Sau đó, chúng ta đã khai báo một con trỏ kiểu
    .section .data  
    
    hello:
    .string "Hello, World!\n"  
    .section .text  
    
    .globl _start _start:
    movl $4, %eax  
    movl $1, %ebx  
    movl $hello, %ecx  
    movl $13, %edx  
    int $0x80  
    movl $1, %eax  
    int $0x80  
    

    7 có tên

    `

include
07.
  • Chúng ta gán địa chỉ của mảng `
# include

05 cho con trỏ


# include   
07 bằng cách sử dụng tên mảng làm con trỏ vào phần tử đầu tiên của mảng.

  • Trong vòng lặp `

include
10, chúng ta sử dụng con trỏ để truy cập và in giá trị của từng phần tử của mảng. Sau đó, chúng ta di chuyển con trỏ tới phần tử tiếp theo trong mảng bằng cách sử dụng  

include
11.

Kết quả của chương trình sẽ in ra giá trị của các phần tử trong mảng:

gcc -o hello hello.o


5

Con trỏ và chuỗi

Chuỗi trong C thường được biểu diễn bằng một mảng các ký tự và kết thúc bằng ký tự null (

define PI 3.14159265

define MAX_SIZE 100

48). Con trỏ thường được sử dụng để xử lý chuỗi, vì chúng cho phép bạn truy cập và thay đổi từng ký tự trong chuỗi một cách dễ dàng.

Dưới đây là một ví dụ về cách sử dụng con trỏ để làm việc với chuỗi (string) trong ngôn ngữ lập trình C:

gcc -o hello hello.o


6
  • Chúng ta đã khai báo một mảng ký tự có tên `
# include
13 và khởi tạo nó với chuỗi "Hello, World!".

  • Sau đó, chúng ta đã khai báo một con trỏ kiểu `

define PI 3.14159265

define MAX_SIZE 100
47 có tên  

include
07.
  • Chúng ta gán địa chỉ của mảng `
# include

13 cho con trỏ


# include   
07, vì mảng là một chuỗi ký tự và tên mảng chính là con trỏ tới phần tử đầu tiên của chuỗi.

  • Sử dụng vòng lặp

    gcc -c -o file1.o file1.c gcc -c -o file2.o file2.c

    2, chúng ta duyệt qua chuỗi bằng cách sử dụng con trỏ. Chúng ta kiểm tra ký tự mỗi lần và in nó ra màn hình, sau đó di chuyển con trỏ tới ký tự tiếp theo trong chuỗi bằng cách sử dụng

    `

include
11.
  • Chuỗi kết thúc bằng ký tự null `
# define PI 3.14159265 # define MAX_SIZE 100

48, nên chúng ta dừng khi gặp ký tự null.

Kết quả của chương trình sẽ in ra chuỗi "Hello, World!" lên màn hình.

Con trỏ là một tính năng mạnh mẽ trong ngôn ngữ C, nhưng nó cũng đòi hỏi kiểm soát cẩn thận và làm việc chính xác với bộ nhớ để tránh lỗi segmentation fault và rò rỉ bộ nhớ.

5.14 File I/O (Input/Output)

File I/O (Input/Output) trong ngôn ngữ lập trình C là quá trình đọc và ghi dữ liệu từ và vào các tệp (file). Các tệp được sử dụng để lưu trữ và truyền dữ liệu giữa chương trình và hệ thống tệp. Dưới đây là một số ví dụ về cách thực hiện File I/O trong C:

Ghi dữ liệu vào tệp

Để ghi dữ liệu vào một tệp, bạn cần mở tệp để ghi bằng cách sử dụng hàm


# include 

21. Sau đó, bạn có thể sử dụng các hàm như


# include 

22 hoặc


# include 

23 để ghi dữ liệu vào tệp. Ví dụ:

gcc -o hello hello.o

7

Trong ví dụ này, chúng ta mở tệp "example.txt" để ghi (mode "w" - viết tắt của write). Sau đó, chúng ta sử dụng


# include 

22 để ghi chuỗi "Hello, World!" vào tệp và đóng tệp sau khi đã hoàn thành.

Đọc dữ liệu từ tệp

Để đọc dữ liệu từ một tệp, bạn cũng cần mở tệp nhưng trong mode đọc bằng


# include 

21. Sau đó, bạn có thể sử dụng các hàm như


# include 

26 hoặc


# include 

27 để đọc dữ liệu từ tệp. Ví dụ:

gcc -o hello hello.o

8

Trong ví dụ này, chúng ta mở tệp "example.txt" để đọc (mode "r" - viết tắt của read). Sau đó, chúng ta sử dụng


# include 

26 để đọc dữ liệu từ tệp vào mảng


# include 

29 và in ra nội dung đã đọc.

File I/O trong C là một phần quan trọng của việc làm việc với tệp và dữ liệu trên hệ thống tệp. Nó cho phép bạn lưu trữ, đọc, và xử lý dữ liệu từ và vào các tệp theo nhiều cách khác nhau.

6. Tài liệu học ngôn ngữ C

Có rất nhiều tài liệu giúp bạn học ngôn ngữ C, tuy nhiên lựa chọn đúng tài liệu phù hợp với bản thân cũng như thật sự chất lượng thì không phải chuyện dễ. Dưới đây là một số tài liệu được 200Lab chọn lọc để giúp bạn nắm thật chắc ngôn ngữ C:

  1. Sách K&R C: "The C Programming Language" của Brian Kernighan và Dennis Ritchie (K&R C) là một trong những tài liệu cơ bản nhất cho ngôn ngữ C. Cuốn sách này giúp bạn nắm vững cú pháp cơ bản và cách sử dụng C một cách hiệu quả.
  2. Sách C Programming Absolute Beginner's Guide: Cuốn sách này của Perry và Miller cung cấp một cách tiếp cận dễ hiểu cho người mới học lập trình C. Nó bao gồm nhiều ví dụ thực tế và dự án để thực hành.
  3. Learn-C.org: Trang web này cung cấp một khóa học trực tuyến miễn phí về ngôn ngữ C. Nó bao gồm các bài giảng và bài tập để bạn thử nghiệm kiến thức.
  4. GeeksforGeeks C Programming: GeeksforGeeks cung cấp nhiều bài hướng dẫn, ví dụ và bài tập về lập trình C. Đây là nguồn tài liệu tốt để rèn luyện kỹ năng lập trình C.

7. Cách học ngôn ngữ C hiệu quả

Ngôn ngữ C tính đến thời điểm hiện tại mặc dù đã cũ nhưng vẫn được sử dụng để giảng dạy ở rất nhiều trường Đại học, đặc biệt là ở Việt Nam. Chính vì thế nếu bạn đang là sinh viên, bạn cần nắm thật chắc ngôn ngữ này, vì nó sẽ giúp bạn rất nhiều ở chặng đường phía trước.

Dưới đây là các bước thực hiện để học ngôn ngữ C một cách hiệu quả được đúc rút từ kinh nghiệm cá nhân:

Bước 1: Tìm hiểu qua các khái niệm cơ bản

Bước đầu tiên là các bạn cần đọc qua các khái niệm cơ bản trong C, hiểu được chúng dùng để làm gì sau đó nhớ cú pháp của chúng, ở bước này quan trọng nhất là cần nắm được cách sử dụng chúng.

Bước 2: Tiến hành làm bài tập

Hãy bắt đầu với những bài tập theo từng chủ đề trước, từ dễ đến khó. Các bạn có thể tìm trên Google 500 bài code thiếu nhi hoặc làm các bài tập mà thầy cô giao trên trường. Ở bước này điều quan trọng nhất là cố gắng tìm ra lời giải đúng của mỗi bài, từ cách làm đấy đem đi làm những bài tương tự nhưng ở mức độ khó hơn một chút.

Bước 3: Quay lại tìm hiểu về khái niệm cơ bản

Trong lúc làm bài tập chắc chắn sẽ có lúc bạn bị quên một số khái niệm, đừng ngần ngại mà hãy quay lại để đọc lại chúng. Lúc này bạn sẽ được thấy những khái niệm này hoạt động như thế nào trong thực tế sử dụng, giúp bạn nhớ nó lâu hơn.

Bước 4: Luyện tập, luyện tập, và luyện tập

Đến bước này thì việc còn lại của bạn chỉ là lặp đi lặp lại 3 bước trên, hãy cố gắng làm thật nhiều bài tập, xử lý thật nhiều vấn đề khác nhau. Từ đấy sẽ giúp bạn nắm rõ các khái niệm và nâng cao được tư duy.

Bước 5: Không bỏ cuộc

Bước cuối cùng và cũng là bước quan trọng nhất đấy là không được bỏ cuộc. Hãy cố gắng để tìm ra lời giải bằng mọi giá dù nó có tốn của bạn rất nhiều thời gian. Khi tìm ra được lời giải bạn sẽ thấy thời gian bạn bỏ ra là xứng đáng.

8. Kết luận

C là ngôn ngữ cơ bản nhất và là nền tảng cho rất nhiều ngôn ngữ khác về sau. Chính vì thế hiểu về C là rất quan trọng và là cần thiết đối với tất cả lập trình viên ngày nay.