Chuyển tới nội dung
Home » C# Full Stack Developer | Tài Liệu Học Ngôn Ngữ C

C# Full Stack Developer | Tài Liệu Học Ngôn Ngữ C

TÂY DU KÝ BỊA TRUYỆN | ACTION C

Các cấu trúc dòng điều khiển[sửa | sửa mã nguồn]

Một cách cơ bản thì C là ngôn ngữ dạng tự do. Trong phần này, tất cả các chữ “mệnh đề” có nghĩa tương đương với chữ “câu lệnh”.

Các mệnh đề phức hợp[sửa | sửa mã nguồn]

Câu lệnh phức hợp được bọc trong dấu ngoặc và còn được gọi là khối mã. Các câu lệnh phức hợp trong C có dạng.

{

Khối mã được dùng như là phần thân của một hàm hay được đặt bất kì ở vị trí nào mà một câu lệnh đơn giản có thể đặt. Nghĩa là, về ý nghĩa văn phạm thì câu lệnh đơn giản và câu lệnh phức hợp là tương đương nhau.

Các mệnh đề biểu thức[sửa | sửa mã nguồn]

Một câu lệnh (hay một mệnh đề) của C có dạng:

là một mệnh đề biểu thức. Nếu biểu thức này không có nội dung (mà chỉ còn lại dấu thì biểu thức được gọi là

mệnh đề null

(hay mệnh dề rỗng). (Theo ngôn ngữ máy Assembler thì mệnh đề null sẽ tương đương với câu lệnh NOP; chiếm 1 byte chỉ làm nhiệm vụ tăng địa chỉ của chồng (stack) lên 1 đơn vị.)

Các mệnh đề lựa chọn (hay điều kiện)[sửa | sửa mã nguồn]

Có ba loại mệnh đề lựa chọn: hai loại dùng từ khóa

if

và một loại dùng từ khóa

switch

. Đó là:

Dạng dùng từ khóa if[sửa | sửa mã nguồn]

if (

)

if (

)

; else

Trong dạng này, nếu phần trong ngoặc đơn có giá trị khác 0 hay có giá trị “đúng” (true) thì dòng điều khiển sẽ chuyển vào để thực thi

. Nếu trong câu lệnh

if

có thêm từ khóa

else

thì

sẽ được thực thi một khi

có giá trị 0 hay giá trị “sai”.


Nhắc lại: như trên thì vị trí mỗi mệnh đề đều có thể thay bằng một khối mã.

Trong cách viết mã lồng nhau phức tạp bao gồm nhiều mệnh đề

if

thì từ khóa

else

sẽ được gán vào mệnh đề

if

phía trên gần nhất nào chưa được ghép. Để tránh sự nhầm lẫn cách tốt nhất là lồng chúng vào trong các dấu và .

Dạng dùng từ khóa switch[sửa | sửa mã nguồn]

Mệnh đề

switch

sẽ gây ra việc chuyển dòng điều khiển sang một trong những mệnh đề con kế tiếp tùy theo giá trị của một biểu thức X (biểu thức này phải có kiểu nguyên). Các mệnh đề con này thường là các mệnh đề phức hợp. Đứng trước mỗi mệnh đề con sẽ là một từ khóa

case

, sau đó là một biểu thức hằng Hi, và dấu hai chấm gắn liền tiếp theo đó là mệnh đề con Mi.

Khi giá trị của X trùng với một giá trị Hi được nêu ở đâu thì mệnh đề con đi gắn liền với hằng tại đó (tức là Mi) sẽ được thực thi.

Nếu X không bằng với bất kì giá trị Hi nào thì người lập trình có thể dùng thêm từ khóa

default

, sau đó là dấu hai chấm và tiếp theo là một mệnh đề con Mdefault. Mệnh đề con này sẽ được thực thi khi mà giá trị của X khác với mọi giá trị hằng Hi.

Lưu ý:

  • Trong câu lệnh

    switch

    thì không cho phép có hai giá trị hằng bằng nhau. Nghĩa là khi X được đánh giá thì chỉ có tối đa một mệnh đề con được thực thi.
  • Các câu lệnh

    switch

    có thể được dùng trong dạng lồng vào nhau (nest), một từ khóa

    case

    hay

    default

    sẽ thuộc vào câu lệnh

    switch

    bên trong nhất (hay nhỏ nhất) chứa nó.
  • Một khi dòng điều khiển hoàn tất câu lệnh con Mi thì nó sẽ tiếp tục thi hành các câu lệnh con Mi+1 theo sau cho đến khi nó bị yêu cầu ngưng bởi câu lệnh nhảy (mà thường dược dùng nhiều nhất là câu lệnh

    break

    )

Trong dạng thí dụ dưới đây, nếu

có giá trị bằng

thì mệnh đề các biểu thức

,

, và

sẽ lần lần lượt được thực thi theo thứ tự nếu như trong chúng không có câu lệnh

break

. Nhưng vì trong mã thí dụ có câu lệnh

break

nên dòng điều khiển sẽ ngưng và kết thúc câu lệnh

switch

khi thi hành lệnh

break

này.




switch (

) { case

:

case

:

break;case

:

default:






Các mệnh đề tái lặp (hay vòng lặp)[sửa | sửa mã nguồn]

C có 3 dạng câu lệnh vòng lặp:

Vòng lặp do[sửa | sửa mã nguồn]

do

while (

);

Trong mệnh đề này thì mệnh đề được thực thi lặp lại cho tới khi nào

được đánh giá (hay có giá trị) là

true

. Một khi

không còn có giá trị

true

nữa thì vòng lặp sẽ bị kết thúc.

Vòng lặp while[sửa | sửa mã nguồn]

while (


chỉ được thực thi hay thực thi lặp lại khi

có giá trị là

true

. Nếu

có giá trị

false

thì câu lệnh sẽ bị kết thúc ngay lập tức.


Vòng lặp for[sửa | sửa mã nguồn]

Dạng C89 của vòng lặp

for

là:

for (

;

;

Nó đã được tổng quát hóa trong C99 thành:

for (


;

Khi cả ba biểu thức đều hiện diện trong một câu lệnh

for

, thì mệnh đề:

for (e1; e2; e3) s;

sẽ tương đương với

e1; while (e2) { s; e3; }

Bất kì biểu thức nào trong vòng lặp

for

có thể được loại bỏ. Một biểu thức bị mất (e2 chẳng hạn) có thể làm cho vòng lặp biến thành vòng lặp vô hạn.

Thí dụ: vòng lặp

for

sau đây 3 biểu thức ở dạng phức hợp và ngăn cách nhau bởi dấu chấm phẩy:

for (x=10,y=1;((x>4) && (y<8)); x–,y+=2) printf(“x = %d, y = %d \n”, x,y);

Kết quả thực thi màn hình sẽ hiển thị như sau:

x = 10, y = 1 x = 9, y = 3 x = 8, y = 5 x = 7, y = 7

Vòng lặp kết thúc vì điều kiện trong biểu thức thứ nhì ((x>4) && (y<8)) không còn đúng nữa.

Các mệnh đề nhảy (hay bước nhảy)[sửa | sửa mã nguồn]

Các lệnh nhảy sẽ thay đổi dòng điều khiển một cách vô điều kiện. có 4 kiểu mệnh đề nhảy trong C là

goto

,

continue

,

break

, và

return

.

goto[sửa | sửa mã nguồn]

câu lệnh

goto

sẽ có dạng:

goto

nhãn phải có mặt trong hàm có chứa câu lệnh

goto

. Khi đọc đến lệnh này, dòng điều khiển sẽ chuyển đến mệnh đề nhãn.

continue[sửa | sửa mã nguồn]

Mệnh đề

continue

chỉ có thể xuất hiện trong một vòng lặp và có tác dụng làm cho dòng điều khiển chuyển sang chu kì mới của vòng lặp trong cùng nhất (có chứa câu lệnh). Các dạng sử dụng bao gồm

while (expression) { /*… */ cont:; }

do { /*… */ cont:; } while (expression);

for (optional-expr; optexp2; optexp3) { /*… */ cont:; }

break[sửa | sửa mã nguồn]

Mệnh đề

break

dùng để kết thúc một câu lệnh vòng lặp hay câu lệnh

switch

ngay lập tức và chuyển tiếp đến câu lệnh tiếp theo sau của vòng lặp đó.

return[sửa | sửa mã nguồn]

Một hàm trả dòng điều khiển về nơi gọi nó bằng câu lệnh

return

. Khi lệnh

return

được theo sau bởi một biểu thức thì biểu thức đó sẽ được đánh giá và giá trị này sẽ được trả về cho nơi đã gọi hàm. Khi return được gọi mà không có biểu thức đi kèm thì giá trị trả về là không xác định.

Cú pháp[sửa | sửa mã nguồn]

C có nhiều điểm yếu trong cú pháp. Đáng chú ý là:

  • Nguyên mẫu của hàm nào không chỉ ra tham số nào thì được hiểu ngầm là cho phép một tập bất kì các tham số. Một vấn đề về cú pháp đã được đề ra cho khả năng tương thích ngược của K&R C, về việc thiếu các nguyên mẫu.
  • Một số sự chọn lựa đáng ngờ vực về thứ tự ưu tiên của các toán tử, chẳng hạn như

    ==

    “nối kết” một cách chặt chẽ hơnvàtrong các biểu thức như là

    x & 1 == 0

    .
  • Việc dùng toán tử “=” một cách dễ nhầm lẫn. Nếu dùng trong đẳng thức toán học để chỉ các phép gán, dẫn tới các phép gán không chủ định trong việc so sánh và dẫn tới một ấn tượng sai lầm rằng phép gán có tính bắc cầu. Ví dụ: việc dùng câu lệnh

    if (x=0) {...}

    sẽ dễ gây ra các lỗi bất ngờ.
  • Thiếu các toán tử infix cho các đối tượng phức tạp, đặc biệt là cho các phép toán trên dãy các ký tự làm cho chương trình phụ thuộc nặng nề lên các phép toán rất khó đọc.
  • Dựa vào quá nhiều trên hệ thống ký hiệu làm cơ sở cho cú pháp ngay cả ở nơi không tường minh như là “&&” và “||” thay vì dùng “and” và “or”.
  • Cú pháp khai báo không được dễ hiểu, đặc biệt cho hàm của các con trỏ. Trong tình huống hoàn toàn tương tự của C++, nhà nghiên cứu Damian Conway nói về cú pháp của khai báo như sau:

    Khó để mà đặc tả một kiểu trong C++ bởi vì thực tế là một số phần tử của việc khai báo (như là con trỏ) thì là các toán tử tiền tố trong khi một số khác (như là mảng) lại là toán tử hậu tố (nghĩa là phải đặt đứng trước tên con trỏ và dặt

    []

    sau tên mảng—người dịch). Nhừng toán tử khai báo này lại có các thứ tự ưu tiên khác nhau, cần phải được đặt trong các dấu ngoặc cẩn thận để đạt được sự khai báo mong muốn.
    Ben Werther & Damian Conway.
  • Khó để mà đặc tả một kiểu trong C++ bởi vì thực tế là một số phần tử của việc khai báo (như là con trỏ) thì là các toán tử tiền tố trong khi một số khác (như là mảng) lại là toán tử hậu tố (nghĩa là phải đặt

A Modest Proposal: C++ Resyntaxed. Section 3.1.1. 1996.

Các vấn đề về bảo trì[sửa | sửa mã nguồn]

Có một số vấn đề khác của C mà không trực tiếp do các lỗi hay sai sót, nhưng lại làm ngăn trở khả năng của người lập trình để xây đựng một hệ thống cỡ lớn, có thể bảo trì và ổn định. Các điển hình bao gồm:

  • Hệ thống rời rạc vì các câu lệnh định nghĩa nhập (

    #include

    ) dựa trên các dòng chữ nội tại rải rác không đồng nhất (trong các tập tin) nhằm giữ các nguyên mẫu và các định nghĩa được đồng bộ. Điều này làm tăng đáng kể số lần cho việc tạo dựng (phần mềm).
  • Mô hình chuyển dịch rối rắm. Nó buộc phải có việc theo dõi các sự phụ thuộc (về mã) bằng tay và nó ngăn cấm sự tối ưu hóa trình dịch giữa các mô dun (ngoại trừ sự tối ưu hoá thời gian liên kết).
  • Hệ thống kiểu yếu dẫn tới việc các chương trình có lỗi hiển nhiên nhưng vẫn được dịch mà không bị bắt lỗi.

Các công cụ kiểm tra tĩnh cho trình dịch[sửa | sửa mã nguồn]

Nhiều công cụ đã được tạo ra để giúp người lập trình C tránh dược các lỗi. Việc kiểm tra và kiểm toán mã nguồn tự động thì rất hiệu quả trong mọi ngôn ngữ. Chẳng hạn cho C là Lint. Một thực tế là sử dụng Lint để phát hiện các mã có nghi vấn khi một chương trình được viết lần đầu. Một khi chương trình đã qua được Lint, thì nó được chuyển dịch bởi một trình dịch C. Cũng có một thư viện cho việc tiến hành kiểm tra các biên của mảng và một dạng giới hạn của việc tự động dọn rác, nhưng đó không phải là một phần tiêu chuẩn của C.

Điều nên nhận thức là các công cụ này không phải là vạn năng. Bởi vì sự linh hoạt của C, nhiều kiểu lỗi như là việc dùng sai các hàm tham lượng động, việc dùng chỉ số ngoài biên của mảng và quản lý bộ nhớ không đúng thì không thể phát hiện được. Mặc dù vậy, nhiều trường hợp (lỗi) thông thường đều có thể được nhận ra.

TÂY DU KÝ BỊA TRUYỆN | ACTION C
TÂY DU KÝ BỊA TRUYỆN | ACTION C

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.

Cú pháp[sửa | sửa mã nguồn]

Không như Fortran, C là ngôn ngữ dạng tự do, cho phép người lập trình tùy tiện sử dụng ký tự

whitespace

để xếp đặt (cách trình bày) mã nguồn. Các dòng chú giải hoặc có thể đặt nằm giữa

/*



*/

hoặc viết từng dòng bắt đầu bởi

//

theo sau đó là các chú giải.

Mỗi tập tin chứa mã có thể chứa các khai báo và các định nghĩa hàm. Các định nghĩa hàm, chính chúng, cũng chứa các khai báo và các mệnh đề. Các khai báo thì hoặc là định nghĩa những kiểu mới với các từ khoá như

struct

,

union

, và

enum

, hoặc là gán các kiểu và đăng ký vùng chứa (trong bộ nhớ) cho các biến mới (Ví dụ: char *myname = “ABC”). Các từ khoá như là

char



int

cũng như là ký hiệu con trỏ chỉ tới là các kiểu sẵn có. Các khối mã thì được đóng ở giữa các dấu và để chỉ ra phần (mã) mà các khai báo và các cấu trúc điều khiển (bên trong dấu ngoặc) có hiệu lực.

Như là một ngôn ngữ mệnh lệnh, C phụ thuộc vào các mệnh đề (câu lệnh) để làm hầu hết các việc. Hầu hết các mệnh đề lại là các mệnh đề biểu thức mà một cách đơn giản chúng tạo nên việc đánh giá các biểu thức đó—và trong quá trình này, các biến nhận được các giá trị mới hoặc các giá trị này được trả ra. Các mệnh đề dòng điều khiển cũng có hiệu lực cho việc thực thi có điều kiện hay có lặp lại, mà chúng được cấu tạo với các từ khóa như là

if

,

else

,

switch

,

do

,

while



for

. Các nhảy dòng cũng có thể thực hiện qua câu lệnh

goto

. Nhiều phép toán khác nhau được cung cấp sẵn để thực thi trên các phép tính cơ sản về số học, lô gíc, so sánh, kiểu bit, chỉ số của mảng, và phép gán giá trị. Các biểu thức cũng gọi các hàm, bao gồm một số lượng lớn các hàm thư viện, để tiến hành các thao tác chung.

KARIK x ONLY C - CÓ CHƠI CÓ CHỊU (OFFICIAL MUSIC VIDEO)
KARIK x ONLY C – CÓ CHƠI CÓ CHỊU (OFFICIAL MUSIC VIDEO)

Việc chuyển các đối số qua dòng lệnh[sửa | sửa mã nguồn]

Các tham số được gõ vào một dòng lệnh thì được chuyển vào một chương trình C với hai biến sẵn có – một để đếm số đối số là

argc

và biến kia là một mảng con trỏ có tên là

argv

, mà mỗi con trỏ chỉ đến một đối số (các đối số được xem như là mảng ký tự)

Thí dụ mệnh lệnh

myFilt p1 p2 p3

(Lưu ý: không có gì bảo đảm rằng các dãy ký tự riêng lẻ là liền nhau)

Đối với thí dụ này, các giá trị riêng lẻ của các tham số có thể được truy cập bằng

argv[1]

,

argv[2]

, và

argv[3]

như là được chỉ ra trong chương trình sau:

#include

int main(int argc, char *argv[]) { int i; printf (“argc\t= %i\n”, argc); for (i = 0; i < argc; i++) printf (“argv[%i]\t= %s\n”, i, argv[i]); return 0; }

Tập tin I/O[sửa | sửa mã nguồn]

Trong C, I/O được tiến hành qua một nhóm các hàm trong thư viện chuẩn. Trong ANSI/ISO C, những hàm đó được định nghĩa trong

stdio.h

.

Tiêu chuẩn I/O[sửa | sửa mã nguồn]

Ba tiêu chuẩn dòng I/O được định nghĩa sẵn là:


  • stdin

    đầu vào chuẩn

  • stdout

    đầu ra chuẩn

  • stderr

    lỗi chuẩn

Các dòng này được tự động mở và đóng lại bởi môi trường của thời gian thi hành, chúng không cần và không nên được mở một cách rõ ràng.

Thí dụ sau minh họa làm thế nào một chương trình bộ lọc được cấu trúc một cách điển hình:

#include

int main() { int c; while ((c = getchar()) != EOF) { /* do various things to the characters */ if (anErrorOccurs) { fputs(“an error eee occurred\n”, stderr); break; } /*… */ putchar(c); /*… */ } return 0; }

HIEUTHUHAI x LOWNA | -237°C [Lyrics Video]
HIEUTHUHAI x LOWNA | -237°C [Lyrics Video]

Tham khảo[sửa | sửa mã nguồn]

  • Brian Kernighan, Dennis Ritchie: The C Programming Language. Also known as K&R — The original book on C.

    • 1st, Prentice Hall 1978; ISBN 0-13-110163-3. Pre-ANSI C.
    • 2nd, Prentice Hall 1988; ISBN 0-13-110362-8. ANSI C.
  • ISO/IEC 9899. The official C:1999 standard, along with defect reports and a rationale.
  • Samuel P. Harbison, Guy L. Steele: C: A Reference Manual. This book is excellent as a definitive reference manual, and for those working on C compilers. The book contains a BNF grammar for C.

    • 4th, Prentice Hall 1994; ISBN 0-13-326224-3.
    • 5th, Prentice Hall 2002; ISBN 0-13-089592-X.
  • Derek M. Jones: The New C Standard: A Cultural and Economic Commentary, Addison-Wesley, ISBN 0-201-70917-1, online material
  • Robert Sedgewick: Algorithms in C, Addison-Wesley, ISBN 0-201-31452-5 (Part 1–4) and ISBN 0-201-31663-3 (Part 5)
  • William H. Press, Saul A. Teukolsky, William T. Vetterling, Brian P. Flannery: Numerical Recipes in C (The Art of Scientific Computing), ISBN 0-521-43108-5

Nhược điểm

Ngoài những thế mạnh kể trên, ngôn ngữ C cũng còn một số thiếu sót dù cho đây là ngôn ngữ lý tưởng cho người mới bắt đầu lập trình do cú pháp, thuật toán và cấu trúc mô-đun đơn giản. Sau đây là một số nhược điểm trong quá trình sử dụng C:

* Tính năng OOP: C không hỗ trợ mở rộng đối với các tính năng lập trình hướng đối tượng (OOP) để tạo lớp con (subclass) từ lớp cha. Không giống như ngôn ngữ Java, Python hoặc C++, việc sử dụng lại mã hiện có trong C tương đối khó khăn.

* Tính năng Namespace: C thiếu các tính năng của vùng tên, có nghĩa là không thể sử dụng lại cùng một tên biến trong một phạm vi. Điều này đồng nghĩa với việc không thể khai báo hai biến có cùng tên.

* Kiểm tra thời gian chạy: Ngôn ngữ C thường không hiển thị lỗi mã sau mỗi dòng mã. Thay vào đó, tất cả các lỗi sẽ được trình biên dịch trình bày sau khi chương trình được viết. Nhược điểm này gây khó khăn trong việc kiểm tra mã đối với chương trình lớn.

* Xử lý ngoại lệ: C thiếu khả năng xử lý ngoại lệ chẳng hạn như lỗi, sự bất thường có thể xảy ra trong mã nguồn.

* Hàm tạo và hàm hủy: Vì C không hướng đối tượng nên nó không cung cấp các tính năng hàm tạo và hàm hủy. Việc tạo hoặc hủy một biến trong C phải được thực hiện thủ công thông qua hàm hoặc bằng các phương tiện khác.

* Thu gom bộ nhớ rác: C không được trang bị tính năng thu gom bộ nhớ rác – là một trong những tính năng quan trọng để tự động lấy lại bộ nhớ từ các đối tượng không còn cần trong thư viện/ứng dụng.

4. Ngôn ngữ C được sử dụng trong trường hợp nào?

Trong thực tế, ngôn ngữ lập trình C không nằm giới hạn ở việc phát triển hệ điều hành và ứng dụng mà còn được sử dụng trong các lĩnh vực như phát triển giao diện đồ họa người dùng, môi trường phát triển tích hợp,…

Dưới đây là một số trường hợp sử dụng cho ngôn ngữ C:

– Hệ điều hành (chẳng hạn như Unix cùng tất cả các ứng dụng Unix);

– Cơ sở dữ liệu, bao gồm Oracle, MySQL, Microsoft SQL Server và PostgreSQL – một phần được viết bằng C;

– Trình biên dịch ngôn ngữ, bao gồm trình biên dịch C;

– Soạn thảo văn bản;

– Bộ đệm in;

– Trình hợp dịch;

– Trình điều khiển mạng;

– Các chương trình hiện đại như Git và FreeBSD;

– Thông dịch viên ngôn ngữ;

– Các tiện ích khác, chẳng hạn như trình điều khiển chuột, điều khiển bàn phím,…

Sự khác biệt giữa C và C++

Mặc dù 2 cụm từ C và C++ nghe có vẻ quen thuộc nhưng cách sử dụng và tính năng của cả hai ngôn ngữ lại khác nhau ở một mức độ nhất định. C++ là một superset được kế thừa từ ngôn ngữ C, sử dụng một tập hợp các khái niệm lập trình hoàn toàn khác. C là ngôn ngữ lập trình thủ tục, trong khi C++ cung cấp hỗ trợ OOP.

Một số điểm khác biệt bao gồm:

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.

Nguồn tham khảo:

https://www.techtarget.com/searchwindowsserver/definition/C

Program in C
Program in C

Ứ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),…

Ưu điểm

Ngôn ngữ C mang trong mình nhiều đặc điểm đặc biệt, khiến nó trở thành một trong những ngôn ngữ được sử dụng rộng rãi nhất mọi thời đại. Sau đây là lợi ích chính của việc sử dụng C:

* Có cấu trúc: Cách tiếp cận lập trình đối với C thường có cấu trúc rõ ràng, chia nhỏ vấn đề thành các mô-đun hoặc chức năng nhỏ hơn dễ hiểu và sửa đổi.

* Linh động: C không nằm cố định trong bất kì một thiết bị nào. Chương trình C có thể được thực thi trên nhiều máy khác nhau.

* Là ngôn ngữ lập trình cấp trung: Ngôn ngữ C có thể hỗ trợ tính năng của cả ngôn ngữ cấp thấp và cấp cao.

* Thư viện phong phú: C cung cấp một thư viện với vô vàn tính năng sẵn có, giúp đẩy nhanh quá trình phát triển phần mềm.

* Cấp phát bộ nhớ động: C hỗ trợ tính năng cấp phát bộ nhớ động (Dynamic memory allocation), nhằm mục đích giải phóng bộ nhớ được cấp phát bất kỳ lúc nào bằng cách sử dụng hàm free().

* Nhanh chóng: Đây là ngôn ngữ dựa trên trình biên dịch, giúp quá trình biên dịch và thực thi mã nhanh hơn. Vì chỉ có các tính năng thiết yếu và bắt buộc được đưa vào C nên nó giúp tiết kiệm năng lượng xử lý và cải thiện tốc độ.

* Con trỏ: C sử dụng con trỏ (pointer – là một biến) giúp cải thiện hiệu suất bằng cách cho phép tương tác trực tiếp với bộ nhớ hệ thống.

* Đệ quy: C cho phép nhà phát triển sử dụng tính năng đệ quy (phương pháp dùng trong chương trình máy tính trong đó có một hàm tự gọi chính nó) bằng cách cung cấp khả năng sử dụng lại mã cho mọi chức năng.

* Khả năng mở rộng: Một chương trình C có thể dễ dàng mở rộng bằng cách thêm vào mã đã viết sẵn một/một vài thay đổi nhỏ. Từ đó có thể dễ dàng thêm tính năng hay chức năng mới.

Yêu Là \
Yêu Là \”Tha Thu\” | Only C | Em Chưa 18 OST | Official Music Video

Ngôn ngữ trung gian[sửa | sửa mã nguồn]

C được dùng như là một ngôn ngữ trung gian vì nó có thể xuất thành dạng tập tin object hay ngôn ngữ máy. Việc này giúp C trở nên dễ vận chuyển hay dễ tối ưu hóa. Các trình dịch C thường có sẵn cho nhiều loại CPU và các hệ điều hành và hầu hết những trình dịch đó cho ra được tập tin *.obj cũng như ngôn ngữ máy có tối ưu hóa. Do đó, các đầu ra của mã nguồn C đột nhiên trở nên rất là dễ vận chuyển, và có khả năng dùng trong dạng *.obj hay mã máy được tối ưu hóa. Dầu sao thì C được thiết kế như là một ngôn ngữ lập trình, nó không phải là lý tưởng cho việc dùng như là một ngôn ngữ trung gian. Điều này dẫn tới việc phát triển các ngôn ngữ trung gian lấy C làm cơ sở, như là một C–.

Ngôn ngữ lập trình C là gì?

C là một ngôn ngữ lập trình máy tính. Được dùng để tạo ra các chỉ dẫn cho máy tính.

C được phát triển bởi Dennis M. Ritchie để phát triển hệ điều hành UNIX sau đó được sử dụng để phát triển các phần mềm…

C có thể chạy trên hầu hết các hệ điều hành.

C được coi như là ngôn ngữ mẹ vì nó được dùng để viết ra nhiều ngôn ngữ khác như C++, Python, Java …

C được đánh giá là dễ học so với các ngôn ngữ bậc thấp như Assembly hay Pascal… Do đó nó được sử dụng làm môn lập trình cơ sở (Hiện tại một số trường đại học đã chuyển môn cơ sở sang Javascript). Tuy nhiên so với các ngôn ngữ bậc cao như Java, Python thì nó lại khó học hơn rất nhiều.

C là một ngôn ngữ bậc trung, được sử dụng để:

  • Kết hợp với Assembly để viết các trình điều khiển
  • Viết hệ điều hành
  • Viết ra các ngôn ngữ khác
  • Sử dụng để viết các hệ thống core

Nhìn chung thì C khá là khó (bởi khái niệm con trỏ, quản lý bộ nhớ) và thường trên trường chỉ học ở mức độ cơ bản. Nếu theo lập trình về phần cứng, hệ điều hành hay các hệ thống core thì mới tiếp tục học nâng cao… do đó mới đầu học C các bạn thấy khó nhằn một chút thì cũng bình thường.

C Programming Full Course for free 🕹️
C Programming Full Course for free 🕹️

Các vấn đề của C[sửa | sửa mã nguồn]

Một câu nói phổ biến được người ta lập lại nhiều lần của một nhà thiết kế trình dịch Bjarne Stroustrup, người sáng lập ra C++, là “C makes it easy to shoot yourself in the foot.” (tạm dịch: “C làm cho việc bạn tự hại bạn trở nên dễ dàng”) [1]. Nói cách khác, C cho phép nhiều phép toán không mong muốn trong một cách tổng quát, và do đó, nhiều lỗi đơn giản đã được tạo ra bởi một người lập trình mà chúng lại không thể phát hiện qua trình dịch hay ngay cả không phát hiện ra trong lúc thi hành. Điều này là nguyên nhân của một số chương trình có các ứng xử không lường trước được và có các lỗ hổng về an toàn. Một dị bản ngôn ngữ C là Cyclone điều chỉnh được một phần trong số các vấn đề như vậy.

Một lý do của các vấn đề nêu trên là để tránh cho cái giá quá cao phải trả cho việc kiểm soát (lỗi) ở thời gian dịch và thời gian thi hành. Một lý do khác là sự đòi hỏi để giữ C được càng hiệu quả và càng uyển chuyển càng tốt. Một ngôn ngữ càng mạnh thì càng khó khăn cho ngôn ngữ lập trình đó để làm rõ ràng mọi thứ trong các chương trình (được viết trong ngôn ngữ này). Một số việc kiểm tra đã được dựa trên các công cụ bên ngoài, những công cụ như vậy được bàn đến trong phần Các công cụ kiểm tra tĩnh bên ngoài cho trình dịch.

Sự cấp phát vùng nhớ[sửa | sửa mã nguồn]

Một vấn đề với C (và đây thường là vấn đề lớn cho những người mới làm quen với C) là việc cấp phát (vùng nhớ) một cách tự động hay một cách động cho các đốì tượng mà không khởi động chúng. Các đối tượng này, ban đầu, chứa các giá trị bất kì trong khoảng nhớ mà chúng được cấp phát. Các giá trị này có thể là các giá trị ngẫu nhiên còn lại trong bộ nhớ mà chưa được làm sạch, chúng hoàn toàn không dự đoán được. Nếu một chương trình có khai biến mà lại không gán giá trị ban đầu, thường là 0 (cho kiểu số) hay null (cho kiểu con trỏ) hay “” (cho kiểu dãy ký tự,…) thì có thể gây ra các phản ứng không lường trước được của chương trình đó. Hầu hết các trình dịch C hiện đại có thể phát hiện và cảnh cáo về việc “quên gán giá trị khởi động” trong nhiều trường hợp, nhưng cũng không hoàn toàn hiệu quả.

Một vấn đề thường thấy khác là bộ nhớ

heap

không thể được tái dụng cho tới khi nó được hoàn trả lại về cho bộ nhớ bởi người lập trình bằng câu lệnh

free()

. Hậu quả là nếu người lập trình quên hoàn trả các vùng đã cấp phát về cho bộ nhớ và lại tiếp tục dùng các lệnh cấp phát, thì càng lúc càng nhiều các phần của bộ nhớ bị chiếm chỗ. Lỗi này là một loại lỗi kiểu

memory leak

tức là “rỉ bộ nhớ”. Ngược lại, cũng có trường hợp trả tự do phần đã cấp phát về cho bộ nhớ quá sớm và lại tiếp tục sử dụng vùng nhớ đã trả về thì cũng có thể dễ gây ra việc nhận sai các giá trị hay tạo ra các tình huống không lường trước được. Lý do là vì máy tính khi nhận lại các vùng đã được trả sẽ có thể dùng vùng nhớ đó cho các việc khác. Một số ngôn ngữ xử lý chuyện này với việc tự động dọn rác.

Các con trỏ[sửa | sửa mã nguồn]

Các con trỏ là một nguồn gốc chính của nhiều nguy hiểm bởi vì chúng không được kiểm tra, một con trỏ có thể được tạo ra để chỉ tới bất kì đối tượng nào bất kể kiểu nào, kể cả các mã (nhị phân), và khi được dùng đến (hay được viết ra), có thể gây ra các hiệu ứng không lường trước được. Mặc dù hầu hết các con trỏ thường chỉ tới các chỗ an toàn, chúng vẫn có thể di chuyển tới những chỗ không an toàn như khi dùng các phép toán số học trên các con trỏ (thường là cộng trừ trên các địa chỉ mà chúng chỉ tới), vùng nhớ chỗ chúng chỉ tới có thể đã được trả về và đã được tái dụng (con trỏ đu đưa), chúng có thể đã không được khởi động (con trỏ hoang), hay chúng được trực tiếp gán một giá trị nào đó qua việc dùng toán tử đổi kiểu (cast) hoặc được gán qua một con trỏ đã bị hủy hoại. Một vấn đề khác với các con trỏ là việc C cho phép tự do chuyển đổi giữa hai kiểu con trỏ bất kì. Các ngôn ngữ khác điều chỉnh các vấn đề này bằng cách dùng các kiểu tham chiếu bị giới hạn hơn.

Các mảng[sửa | sửa mã nguồn]

Mặc dù C có hỗ trợ riêng cho các mảng tĩnh, nhưng nó không kiểm tra xem các chỉ số mảng có hợp lệ hay không (kiểm tra biên). Thí dụ, người ta có thể viết phần tử thứ sáu của một mảng được định nghĩa với 5 phần tử, và điều này có thể mang lại các hậu quả không mong muốn. Lỗi này thuộc loại lỗi tràn bộ nhớ đệm. Đây là nguồn gốc của nhiều lỗ hổng an ninh trong các chương trình viết bằng C. Mặt khác, do sự giới hạn về kỹ thuật kiểm tra biên ở thời điểm C ra đời (khi gần như chưa có kỹ thuật kiểm tra biên), nên việc kiểm tra biên trở nên ảnh hưởng nặng đến tốc độ thực thi, đặc biệt là trong các tính toán số.

Các mảng đa chiều rất cần thiết khi cài đặt các thuật toán số (chủ yếu áp dụng cho đại số tuyến tính) để chứa các ma trận. Nhưng cấu trúc mảng theo C không những không đáp ứng mà còn không tương hợp cho thao tác chuyên biệt này. Vấn đề này đã được bàn thảo trong sách Numerical Recipes in C, chương 1.2, trang 20 ff (đọc trực tuyến). Người ta có thể tìm thấy ở đây một giải pháp tốt được dùng xuyên suốt trong cả cuốn sách này.

Các hàm tham lượng động[sửa | sửa mã nguồn]

Một vấn đề thường thấy khác là về các hàm tham lượng động (variadic function), tức là, các hàm mà có thể thay đổi được số lượng của các tham số. Không giống như các nguyên mẫu khác của hàm trong C, kiểm tra số lượng tham số ở thời điểm dịch là không bắt buộc bởi tiêu chuẩn, và một cách tổng quát là không thể kiểm tra được nếu không có thêm thông tin. Nếu dữ liệu có kiểu không đúng được chuyển vào, thì hậu quả sẻ không lường được, và thường tạo sự hư hại hoàn toàn. Các hàm tham lượng động cũng xử lý các hằng số con trỏ rỗng trong một cách không biết trước được.

Thí dụ: Họ các hàm printf cung cấp bởi thư viện chuẩn, được dùng để định dạng các dòng chữ xuất ra, thì có tiếng vì các lỗi trong giao diện tham lượng động của nó; nó dựa trên một sự định dạng của dãy ký tự để biểu trưng số và kiểu của các tham số theo sau.

Mặc dù kiểm tra kiểu của các hàm tham lượng động từ thư viện chuẩn là một vấn đề về chất lượng của sự thiết lập, nhiều trình dịch hiện đại đặc biệt tiến hành kiểm tra kiểu của việc gọi

printf

, và sản sinh ra các cảnh cáo nếu danh mục tham số mà không tương ứng với dãy ký tự định dạng. Dẫu sao thì không phải tất cả các lần gọi printf đều có thể được kiểm tra một cách tĩnh bởi vì có thể dãy ký tự định dạng chỉ được lập thành ở thời gian thực thi, khi mà các hàm tham lượng động thường vẫn không kiểm tra được.

Liên kết ngoài[sửa | sửa mã nguồn]

C[sửa | sửa mã nguồn]

  • comp.lang.c Frequently Asked Questions
  • The Development of the C LanguageLưu trữ 2013-05-23 tại Wayback Machine by Dennis M. Ritchie
  • Programming in C (Tài liệu thu thập từ Lysator)
  • International Obfuscated C Code Contest
  • Programming C ở Wikibooks
  • The New C Standard: An economic and cultural commentary — sách không xuất bản về “Phân tích chi tiết của tiêu chuẩn quốc tế cho ngôn ngữ C”

Cú pháp ngôn ngữ C

Bài viết này cần thêm chú thích nguồn gốc để kiểm chứng thông tin.

Cú pháp ngôn ngữ (lập trình) C là tập hợp các quy tắc nhằm xác định cách thức để viết và dịch trong ngôn ngữ lập trình C.

Thí dụ:

// Dòng này sẽ được bỏ qua (không đọc) bởi trình dịch. /* Các dòng này cũng được bỏ qua bởi trình dịch */ … (Tiếp tục mã C)

Tự học các kiến thức cơ bản của lập trình C chỉ trong 1 giờ | Vũ Nguyễn Coder
Tự học các kiến thức cơ bản của lập trình C chỉ trong 1 giờ | Vũ Nguyễn Coder

Thứ tự đánh giá[sửa | sửa mã nguồn]

Một trình dịch C có thể đánh giá các biểu thức theo thứ tự bất kì giữa dãy các điểm. Dãy các điểm được định nghĩa bởi:

  • Các kết thúc của mệnh đề tại các dấu chấm phẩy.
  • Toán tử dãy: dấu phẩy.
  • Các toán tử ngắn mạch: gồm phép và (

    &&

    ) và phép hoặc (

    ||

    ).
  • Toán tử điều kiện (

    A?B:C

    ): Giá trị của biểu thức A được đánh giá trước. Nếu A là đúng thì B sẽ được đánh giá bỏ qua biểu thức C. Nếu A sai thì B bị bỏ qua và chỉ có C được đánh giá tiếp.

Lưu ý: Các biểu thức đứng trước trong một dãy điểm sẽ luôn luôn được đánh giá trước các biểu thức theo sau. Trong trường hợp đánh giá của các ngắn mạch, biểu thức thứ hai có thể sẽ không cần được đánh giá. Thí dụ, trong biểu thức

(a() || b())

, nếu

a()

trả về giá trị đúng thì trình dịch sẽ không cần đánh giá

b()

nữa (vì lúc đó đã đủ để kết luận mệnh đề

(a() || b())

là đúng). Trong thực hành viết mã, nhiều lập trình viên thiếu kinh nghiệm rất dễ bị lọt vào tình huống rằng trình dịch không chịu tiến hành những gì họ muốn mà bỏ qua nhiều bước dẫn tới các kết quả không chính xác mặc dù về lô gíc họ không hề sai sót. Trong trường hợp như vậy, cách tốt nhất là hãy xem lại thật kỹ các mệnh đề Bool và đặc tính đánh giá này của trình dịch. Các lỗi này thuộc loại rất khó tìm ra bởi vì nó hoàn toàn chính xác về mặt cú pháp, về mặt toán học và ngay cả về mặt thuật toán xử lý và rất dễ dẫn đến nhiều kết quả sai về mặt tính toán mà người lập trình không ngờ. Đây cũng là điểm khác nhau giữa ngôn ngữ toán học thuần túy (dùng trong các mã giả –pseudo code ) và thực tế của ngôn ngữ lập trình.

Các phép toán[sửa | sửa mã nguồn]

Xem thêm bài chính Phép toán trong C và C++

Để tham khảo, sau đây là bảng thứ tự ưu tiên của các phép toán theo C89:

Phép toán Mô tả Hướng tiến hành
ngoặc đơn (nhóm)

phần chỉ số của mảng

từ trái sang phải
tiền tố tăng/giảm

dấu dương/âm

từ phải sang trái
nhân/chia/chia lấy phần dư từ trái sang phải
cộng/trừ
phép toán bit

left shift/right shift
quan hệ nhỏ hơn/nhỏ hơn hay bằng

quan hệ lớn hơn/lớn hơn hay bằng

bằng với/khác với
phép toán bit
phép toán bít
phép toán bit
phép toán bool
phép toán bool
điều kiện tam phân từ phải sang trái
phép gán giá trị trực tiếp

phép gán giá trị cộng thêm/trừ bớt

toán tử từ trái sang phải

Nguồn: C Operator Precedence and Associativity

C in 100 Seconds
C in 100 Seconds

Xem thêm[sửa | sửa mã nguồn]

Tiếng Việt[sửa | sửa mã nguồn]

  • Cú pháp ngôn ngữ C
  • Kiểu và khai báo biến trong C
  • Các công cụ: Cygwin, GCC, make, Linker

Tiếng Anh[sửa | sửa mã nguồn]

Wikibooks tiếng Anh có chủ đề về Ngôn ngữ lập trình C
  • C preprocessor
  • C standard library
  • C library
  • C string
  • C syntax
  • List of articles with C programs
  • Objective-C
  • Operators in C and C++
  • Programming tool: Dev-C/C++, DJGPP,, LCC, SPlint, Small-C, C–,

Các trình dịch quan trọng[sửa | sửa mã nguồn]

Những trình dịch về C ngày nay thương được cung cấp kèm chung với C++ và ngay cả trình dịch cho ngôn ngữ Assembly. Những sản phẩm trình dịch được bán phổ biến trên thị trường cũng thường cung cấp thêm nhiều công cụ trợ giúp cho người lập trình như là IDE, debugger,…

Sau đây là danh sách một số trình dịch phổ biến:

  • GCC trình dịch hoàn toàn miễn phí của theo giấy phép GNU toàn bộ gói sẽ bao gồm trình dịch của nhiều ngôn ngữ điển hình là C/C++ và Fortran. Đây là trình dịch chính dùng cho các hệ diều hành Linux. Nó hỗ trợ hầu hết các tiêu chuẩn C/C++. Tuy nhiên vì là miễn phí nên nó không cung cấp các phương tiện đồ họa hỗ trợ cho việc sửa lỗi và viết mã mặc dù nó cũng có các công cụ để giúp phát hiện lỗi rất mạnh như gdb.
  • Turbo C++, Borland C/C++, trình dịch này ngày nay đã đổi tên thành Borland Builder và bị giảm sút thị phần rất nhiều nhưng đây là trình dịch có hỗ trợ chuẩn C98.
  • Microsoft C/C++, đây là trình dịch chỉ được dùng chủ yếu để phát triển các phần mềm trên các hệ Windows. Trình dịch này rất mạnh về các hỗ trợ đồ họa cũng như các công cụ để phát triển và sản xuất phần mềm. Rất tiếc, trình dịch này không hoàn toàn tương thích với các chuẩn. Để có mã nguồn theo chuẩn thì người lập trình phải cài đặt lại một số thông số mặc định. Một điểm yếu của trình dịch này là nó không hỗ trợ cho các hệ điều hành nào không do Microsoft sản xuất.
  • Ngoài ra, còn rất nhiều trình dịch khác ở mức độ ít phổ biến hơn như là trình dịch C/C++ của Intel, Bell Labs,…
Melodico - No Te Merezco ft. C-Kan
Melodico – No Te Merezco ft. C-Kan

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.

Quan hệ với C++[sửa | sửa mã nguồn]

Bjarne Stroustrup, cha đẻ của C++ đã lập đi lập lại rằng:[2] Các tính chất không tương thích giữa C và C++ nên được hạ thấp càng nhiều càng tốt để mở rộng tối đa khả năng hoạt động thông suốt của hai ngôn ngữ này. Một số người tranh biện rằng vì C và C++ là hai ngôn ngữ khác nhau, sự tương thích giữ chúng thì hữu ích nhưng không phải có tính sống còn, theo lập trường này, nỗ lực để giảm sự không tương thích không được phá hủy cố gắng để nâng cao mỗi ngôn ngữ đứng riêng.

Ngày nay, những khác nhau căn bản, không kể các mở rộng thêm vào của C++ như là các lớp, các tiêu bản, các không gian tên, và quá tải, giữa hai ngôn ngữ là:


  • inline

    — các hàm inline có giá trị toàn cục trong C++ và chỉ có giá trị trong phạm vi tập tin trong C.
  • Từ khóa

    bool

    trong C99 thì có riêng tập tin tiêu dề của nó là. Các chuẩn C trước đây đã không định nghĩa kiểu

    boolean

    và nhiều phương pháp không tương thích đã được dùng để mô phỏng kiểu boolean.
  • Các hằng ký tự (được đặt trong dấu ) có độ lớn của một

    int

    trong C và có độ lớn của một

    char

    trong C++. Mặc dù vậy, ngay cả trong C các hàng này sẽ không bao giờ vượt quá giá trị của một

    char

    , cho nên việc chuyển đổi kiểu

    (char)'a'

    thì hoàn toàn an toàn.
  • Nhừng từ khóa mới thêm vào trong C++ sẽ không thể dược dùng làm các tên trong C như trước đây nữa. (Ví dụ:

    try

    ,

    catch

    ,

    template

    ,

    new

    ,

    delete

    ,…).
  • Trong C++, trình dịch tự động tạo một “thẻ” cho mỗi

    struct

    ,

    union

    hay

    enum

    , do vậy,

    struct S {};

    trong C++ tương đương với

    typedef struct S {} S;

    trong C.

C99 tiếp thu một số tính năng mà xuất hiện đầu tiên trong C++. Trong số đó là:

  • Bắt cuộc khai báo nguyên mẫu của hàm.
  • Thêm từ khóa

    inline

    .
  • Hủy bỏ “hiểu ngầm” của sự trả về sẽ có kiểu int.
Restream hôm qua...............
Restream hôm qua……………

C (ngôn ngữ lập trình)

Bài viết này cần thêm chú thích nguồn gốc để kiểm chứng thông tin.

C là một ngôn ngữ mệnh lệnh được phát triển từ đầu thập niên 1970 bởi Dennis Ritchie để dùng trong hệ điều hành UNIX. Từ đó, ngôn ngữ này đã lan rộng ra nhiều hệ điều hành khác và trở thành một những ngôn ngữ phổ dụng nhất. C là ngôn ngữ rất có hiệu quả và được ưa chuộng nhất để viết các phần mềm hệ thống, mặc dù nó cũng được dùng cho việc viết các ứng dụng. Ngoài ra, C cũng thường được dùng làm phương tiện giảng dạy trong khoa học máy tính mặc dù ngôn ngữ này không được thiết kế dành cho người nhập môn.

Ngôn ngữ lập trình C là gì?

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.

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ó.

The Narcissistic C-Suite
The Narcissistic C-Suite

Code ví dụ C – Hello World

Ví dụ in ra dòng chữ ‘Hello World’ bằng ngôn ngữ lập trình C.

Tạo file

Hello.c

với nội dung sau:

#include

int main() { printf(“Hello World!”); return 0; }

(Các file viết bằng ngôn ngữ c có đuôi mở rộng là

.c

)


  • #include

    khai báo file

    stdio.h

    , nó là một thư viện mà ta có thể dùng sẵn. Trong trường hợp này hàm

    printf()

    mà mình sử dụng đã được định nghĩa trong

    sdtio.h

    , nếu mình không khai báo

    stdio.h

    thì chương trình sẽ không hiểu hàm

    printf()

    là gì.

  • int main()

    hàm main (chương trình viết bởi c sẽ bắt đầu chạy từ hàm

    main

    ).

    int

    biểu thị kết quả trả về của hàm

    main

    là kiểu số nguyên
  • Các dấu

    {}

    được dùng để đánh dấu mở đầu và kết thúc của một khối lệnh, một hàm. Dấuđược dùng để kết thúc 1 lệnh

  • printf("Hello World!");

    thực hiện in ra dòng chữ Hello World!

  • return 0;

    kết quả trả về của hàm main là. Trong ví dụ này thì giá trị trả về của hàm main không quan trọng (mình sẽ nói rõ về phần nà sau)

Chạy file Hello.c

File

Hello.c

được viết bằng ngôn ngữ C. Do đó để chạy được file

Hello.c

ta phải biên dịch nó thành mã máy (file .exe) để chạy.

Để biên dịch được các file viết bằng ngôn ngữ C ta cần cài đặt trình biên dịch (complier) cho ngôn ngữ C.

Nếu bạn đang sử dụng linux thì không cần cài đặt thêm vì linux viết bằng C nên nó đã tích hợp sẵn trình biên dịch C.

Trường hợp bạn sử dụng Windows thì phải cài thêm trình biên dịch C như MinGW, Cygwin…

(Xem lại: Hướng dẫn cài đặt trình biên dịch C/C++ MinGW)

Mình sử dụng Windows và đã cài trình compiler MinGW.

Thực hiện compile file

Hello.c

thành file .exe

Ví dụ file Hello.c mình để ở folder

D:\programming

, mình sẽ mở màn hình cmd, di chuyển tới folder

D:\programming

Chạy lệnh

gcc Hello.c -o hello.exe

để build (biên dịch/compile) file Hello.c thành file

hello.exe

Chạy file hello.exe vừa tạo ra ta sẽ thấy in ra dòng chữ

Hello World!

trên màn hình.

Okay! Done!

C.Ronaldo ăn mừng kiểu mới trong ngày tỏa sáng ở AFC Champions League

C.Ronaldo đã bước sang tuổi 39 vào 10 ngày trước. Dù vậy, tiền đạo người Bồ Đào Nha thêm một lần chứng minh tuổi tác không phải là vấn đề lớn với mình. Bằng chứng là anh vẫn tỏa sáng ở AFC Champions League để giúp Al Nassr giành chiến thắng 1-0 trước đối thủ đồng hương Al Feiha ở lượt đi vòng 1/8 AFC Champions League.

C.Ronaldo ăn mừng kiểu mới sau khi ghi bàn vào lưới Al Feiha ở AFC Champions League (Ảnh chụp màn hình).

Mặc dù được đánh giá cao hơn trong trận đấu này nhưng Al Nassr thi đấu khá chật vật trước Al Feiha hiểu quá rõ về mình. Hầu hết những pha lên bóng của Al Nassr trong hiệp 1 đều bị chặn lại.

Phải tới cuối hiệp 1, Al Nassr mới có được cơ hội nguy hiểm đầu tiên. C.Ronaldo được đặt vào vị trí thuận lợi nhưng lại không thể giành chiến thắng trong pha đối mặt với thủ môn Stojkovic bên phía Al Feiha.

Sang hiệp 2, C.Ronaldo lại bỏ lỡ cơ hội ngon ăn khác ở phút 64 khi thủ thành Stojkovic vẫn thi đấu xuất sắc. Tấn công nhiều không ghi được bàn thắng, Al Nassr suýt phải trả giá.

Phút 68, Sakala của Al Feiha đã xé toang mành lưới của Al Nassr nhưng trọng tài xác định người chuyền bóng là Onyekuru rơi vào thế việt vị.

Bàn thắng tinh tế của C.Ronaldo mang tới lợi thế lớn cho Al Nassr ở AFC Champions League (Ảnh: Al Nassr).

Sau tình huống “chết hụt”, C.Ronaldo và các đồng đội chơi thận trọng hơn. Cuối cùng, điều gì đến đã đến. Phút 81, CR7 có pha bật nhả với Brozovic trước khi dứt điểm một chạm tinh tế qua đầu thủ thành Stojkovic.

Đáng chú ý, sau pha lập công này, CR7 không ăn mừng kiểu “siuuu” như thường lệ. Thay vào đó, cầu thủ này đặt tay trước ngực rồi nhắm mắt lại.

Với chiến thắng 1-0 trên sân của Al Feiha, Al Nassr chiếm lợi thế nhất định trước trận đấu lượt về vòng 1/8 AFC Champions League diễn ra vào thứ Tư tuần sau.

Đài Khí tượng thủy văn khu vực Nam bộ cho biết: Ngày hôm qua (15.2), nắng nóng gay gắt đã xảy ra diện rộng trên khu vực miền Đông và vài nơi ở miền Tây với nhiệt độ gia tăng so với ngày hôm trước, phổ biến dao động từ 35 – 37 độ C.

Khu vực trung tâm TP.HCM nắng nóng gay gắt vào trưa 15.2

CHÍ NHÂN

Đặc biệt tại Biên Hòa (Đồng Nai) có mức nhiệt cao nhất lên tới 38 độ C và độ ẩm thấp nhất chỉ có 34%.

Dự báo, nắng nóng tiếp tục xảy ra trên diện rộng ở các tỉnh miền Đông và một vài nơi ở miền Tây. Nhiệt độ cao nhất ở miền Đông từ 35 – 37 độ C, cụ thể tại Bình Phước có thể đạt tới 38 độ C. Miền Tây có nơi 35 – 36 độ C.

Riêng tại TP.HCM, nhiệt độ ngày 15.2, cao nhất tại Tân Sơn Nhất 36 độ C và độ ẩm thấp nhất chỉ 35%. Trong 24 – 48 giờ tới, TP.HCM tiếp tục chịu ảnh hưởng của đợt nắng nóng với nhiệt độ cao nhất phổ biến từ 35 – 36 độ C. Khu vực chịu ảnh hưởng nắng nóng gay gắt gồm các quận: 1 – 3 – 4, Tân Bình, Tân Phú, Phú Nhuận, Bình Tân, Hóc Môn, Q.12, Củ Chi, Q.7, TP.Thủ Đức…

Dự báo những khu vực chịu nắng nóng gay gắt ở TP.HCM trong ngày 16.2

Đài Khí tượng thủy văn khu vực Nam bộ

Cảnh báo, nắng nóng diện rộng có khả năng kéo dài đến khoảng ngày 18.2 (mùng 9 tết). Do ảnh hưởng của nắng nóng, độ ẩm trong không khí giảm thấp nên nguy cơ xảy ra cháy nổ và hỏa hoạn ở khu vực dân cư do nhu cầu sử dụng điện tăng cao. Đối với nhiều người du xuân và trở về TP.HCM trong những ngày tới cần đặc biệt chú ý bảo vệ sức khỏe trước nguy cơ nắng nóng. Vì nắng nóng còn có thể gây tình trạng mất nước, kiệt sức, đột quỵ do sốc nhiệt khi tiếp xúc lâu với nền nhiệt độ cao.

Nắng nóng đến 37 – 38 độ C ở các tỉnh miền Đông

Đài Khí tượng thủy văn khu vực Nam bộ

Ứng xử không xác định[sửa | sửa mã nguồn]

Một khía cạnh thú vị (mặc dù chắc không đơn nhất) của tiêu chuẩn C là ứng xử của một số dạng mã chắc chắn dẫn tới tình trạng không xác định. Trong thực tế, điều này có nghĩa là chương trình tạo ra từ mã này có thể làm bất kì gì từ việc thực thi đúng theo ý muốn cho đến việc hư hỏng mỗi lần nó chạy.

Thí dụ: mã sau đây gây ra ứng xử không xác định, vì biến được dùng tới nhiều hơn một lần (đồng thời lại có sự biến đổi của chính trong lúc tính toán) qua biểu thức

a = b + b++;

:

#include

int main (void) { int a, b = 1; a = b + b++; printf (“%d\n”, a); return 0; }

Vì không có dãy điểm giữa việc truy cập của trong

b + b++

, nó tạo nên tình trạng là trình dịch có thể tự quyết định tăng trước hay sau khi cộng (tùy theo trình dịch và trạng thái máy!) dẫn đến kết quả là 2 hay 3. Mặc dù vậy, vì để cho phép trình dịch thực hiện tốt các quá trình tối ưu, tiêu chuẩn này (của C) còn có thể tạo ra tình hình tệ hơn như trình bày trên. Một cách tổng quát, mọi sự diều chỉnh (hay thay đổi giá trị) và truy cập giữa các dãy điểm có thể gây ra tình huống bất định.

(Restream) Khánh đang chơi thì có kèo nên Levi vào thay
(Restream) Khánh đang chơi thì có kèo nên Levi vào thay

Dãy ký tự[sửa | sửa mã nguồn]

Dãy ký tự có thể được thay đổi nội dung của nó mà không cần đến thư viện chuẩn. Tuy nhiên, thư viện này có nhiều hàm có thể dùng cho cả dãy ký tự có kết thúc 0 và mảng không có ký tự kết thúc kiểu

char

. Trong phần này từ “dãy” được để chỉ dãy ký tự.

Các hàm thường dùng là:


  • strcat(dest, source)

    – nối một dãy ký tự

    source

    tiếp vào vị trí cuối của dãy ký tự

    dest

  • strchr(source, c)

    – tìm vị trí sự xuất hiện đầu tiên củatrong dãy ký tự

    source

    và trả về con trỏ chỉ tới vị trí đó hay con trỏ trống nếukhông tìm thấy trong

    source

  • strcmp(a, b)

    – so sánh hai dãy ký tự a và b (theo thứ tự từ điển); trả về số âm nếunhỏ hơn, 0 nếu chúng bằng nhau, dương nếulớn hơn

  • strcpy(dest, source)

    – chép và thay các ký tự của dãy

    source

    vào dãy

    dest

  • strlen(st)

    – trả về độ dài của

    st

  • strncat(dest, source, n)

    – nối tối đaký tự từ dãy

    source

    tiếp vào vị trí cuối của dãy

    dest

    ; các ký tự sau dấu kết thúc

    null

    sẽ không được chép vào

  • strncmp(a, b, n)

    – so sánh từ ký tự đầu cho đến tối đaký tự từ hai dãyvà(theo thứ tự từ điển); hàm trả về số âm nếu phần so sánh củanhỏ hơn, 0 nếu bằng nhau, và dương nếu lớn hơn

  • strncpy(dest, source, n)

    – chép từ đầu đến tối đaký tự từ dãy

    source

    vào dãy

    dest

  • strrchr(source, c)

    – tìm vị trí hiện lần cuối cùng của ký tựtrong dãy

    source

    và trả về một con trỏ chỉ vào vị trí đó hay con trỏ trống nếu không tìm thấytrong đó

Các hàm ít dùng tới hơn là:


  • strcoll(s1, s2)

    – so sánh hai dãy theo một trình tự địa phương đặc thù

  • strcspn(s1, s2)

    – trả về chỉ số của ký tự đầu tiên trong

    s1

    trùng với ký tự bất kì nào trong

    s2

  • strerror(err)

    – trả về một dãy ký tự dưới dạng một thông báo lỗi ứng với mã (câu viết) trong

    err

  • strpbrk(s1, s2)

    – trả về một con trỏ chỉ vào ký tự đầu tiên nào trong

    s1

    mà trùng với ký tự bất kì trong

    s2

    hay một con trỏ trống nếu không tìm thấy

  • strspn(s1, s2)

    – trả về chỉ số của ký tự đầu tiên trong

    s1

    mà nó không xuất hiện trong

    s2

  • strstr(source, subst)

    – trả về một con trỏ chỉ tới vị trí của dãy

    subst

    trong dãy

    source

    hay trả về một con trỏ rỗng nếu không tồn tại một dãy như vậy bên trong

    source

  • strtok(s1, s2)

    – trả về một con trỏ chỉ đến một

    token

    bên trong

    s1

    mà được phân chia ra bởi các ký tự trong

    s2

  • strxfrm(s1, s2, n)

    – chuyển đổi

    s2

    thành

    s1

    dùng các quy tắc địa phương đặc thù

El Nino khiến nắng nóng sớm và gay gắt

Năm 2023, đợt nắng nóng gay gắt vượt 37 độ C xảy ra vào ngày 8 – 9.4. Cụ thể, ngày 8.4, nhiệt độ cao nhất tại Sở Sao (Bình Dương) là 37,5 độ C, Biên Hòa (Đồng Nai) 37,4 độ C, Đồng Phú (Bình Phước) 37,2 độ C. Trong ngày 9.4, nhiệt độ nhiều nơi ở miền Đông tiếp tục duy trì từ 36 – 37 độ C. Riêng TP.HCM và các tỉnh miền Tây là 35 – 36 độ C.

Nhiều nơi ở TP.HCM tiếp tục có nắng nóng trong ngày 16.2

CHÍ NHÂN

Đến đầu tháng 5, Nam bộ chịu một đợt nắng nóng gay gắt trên diện rộng. Nhiệt độ dao động từ 35 – 38 độ C, riêng Biên Hòa (Đồng Nai) là 38,7 độ C.

Như vậy, so với cùng giai đoạn này năm trước, năm nay nắng nóng đến sớm và gay gắt. Điều này là do tác động của hiện tượng El Nino đang vào giai đoạn cao điểm.

Theo Trung tâm dự báo khí tượng thủy văn quốc gia: Hiện El Nino tiếp tục duy trì đến tháng 3 – 5.2024. Từ tháng 6 – 8.2024, dự báo có khả năng chuyển sang trạng thái trung tính.

Cảnh báo nắng nóng ở Nam bộ

Hiện tượng nắng nóng tại Nam bộ sẽ xuất hiện tại khu vực miền Đông từ nửa cuối tháng 2 và sẽ mở rộng dần sang khu vực miền Tây trong tháng 3, tháng 4 đến nửa đầu tháng 5. Tại khu vực Tây Bắc Bắc bộ và Trung bộ nắng nóng có khả năng đến sớm và xuất hiện nhiều hơn so với trung bình nhiều năm.

Bên cạnh nắng nóng, khu vực Tây nguyên và Nam bộ tình trạng khô hạn còn tiếp diễn trong thời kỳ từ tháng 3 – 4.2024.

Nhiệt độ trung bình giai đoạn từ tháng 3 – 5.2024, trên phạm vi toàn quốc phổ biến cao hơn khoảng 0,5 – 1,5 độ C so với trung bình nhiều năm.

Ngôn ngữ lập trình C kể từ khi ra mắt đã phát triển không ngừng và cho đến thời điểm hiện tại, ứng dụng của loại ngôn ngữ này vẫn vô cùng phổ biến

Ngôn ngữ lập trình C là gì?

Ngôn ngữ lập trình C là một trong những ngôn ngữ cơ bản, sơ cấp cho người mới bắt đầu làm quen với lập trình. Loại ngôn ngữ này cung cấp cho người học quyền truy cập cấp thấp vào bộ nhớ của hệ thống.

Cụ thể, một chương trình viết bằng C trước khi khởi chạy bằng máy tính phải được chạy qua trình biên dịch C (C compiler) để chuyển đổi nó thành định dạng file thực thi (đuôi exe.). Ngoài ra, nhiều phiên bản hệ thống dựa trên hệ điều hành Unix được viết bằng C và đã được chuẩn hóa như một phần của Giao diện hệ điều hành di động (POSIX).

Ngày nay, chúng ta có thể thấy rằng ngôn ngữ lập trình C chạy trên nhiều nền tảng phần cứng và hệ điều hành khác nhau như Microsoft và Linux.

Xem thêm: Ngôn ngữ lập trình là gì? Ngôn ngữ lập trình phổ biến hiện nay

Lịch sử của ngôn ngữ C

Vào đầu những năm 1970, ngôn ngữ lập trình C được phát triển tại Phòng thí nghiệm Bell của AT&T bởi nhà khoa học máy tính Dennis Ritchie, bằng cách kế thừa những tính năng sẵn có của ngôn ngữ B – được xem là tiền thân của C.

Ban đầu, mục đích của ngôn ngữ C là viết mã cho hệ điều hành Unix dùng để giúp quản lý tệp và thư mục trên hệ thống máy chủ, sử dụng các chương trình hợp ngữ (assembly program) và giao tiếp trực tiếp với phần cứng máy tính. Các chương trình hợp ngữ thường phức tạp và dài dòng, vậy nên lập trình viên cần một ngôn ngữ có bộ hướng dẫn thao tác thân thiện với người dùng. C đã hoàn toàn đáp ứng những yêu cầu này, giúp loại bỏ những nhược điểm vốn có ở một vài ngôn ngữ trước đó như BASIC, B và Ngôn ngữ lập trình kết hợp cơ bản (BCPL). Bởi tính phổ biến cũng như sự linh hoạt, C đã sớm được phát hành rộng rãi để sử dụng trên đa nền tảng và nhanh chóng được thương mại hóa.

Cho tới nay, C vẫn được sử dụng phổ biến trong các dự án phát triển web cùng nhiều ngôn ngữ phổ biến như Java, PHP, JavaScript – những ngôn ngữ có tính năng hoặc cú pháp “mượn” trực tiếp hoặc gián tiếp từ C.

Ứng dụng máy tính để bàn được sử dụng tại các doanh nghiệp

Ưu và nhược điểm của ngôn ngữ C

Full Highlights KDF vs FOX | Match 36 | LCK Mùa Xuân 2024
Full Highlights KDF vs FOX | Match 36 | LCK Mùa Xuân 2024

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ụ:


int age; // Khai báo một biến kiểu int có tên là "age"

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

    int

    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ụ:


int count = 0; // Khai báo và khởi tạo biến "count" với giá trị ban đầu là 0

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ụ:

    my_variable

    ,

    myVariable

    .
  • 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ư

    int

    ,

    if

    ,

    while

    ,

    return

    , 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ụ,

    myVariable



    myvariable

    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 hoặc, bạn nên sử dụng tên biến mô tả giá trị của nó, chẳng hạn

    age

    ,

    count

    ,

    total

    , 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ụ:

    myVariableName

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

    my_variable_name

    . 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ụ:


age = 25; // Gán giá trị 25 cho biến "age"

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.


int x = 10; // Khai báo và khởi tạo biến x với giá trị ban đầu là 10 x = 20; // Gán giá trị mới cho biến x, giờ x = 20

  • 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

    int

    , 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ụ:


int x = 10; x = x + 5; // Gán giá trị mới cho biến x bằng cách sử dụng biểu thức

  • 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ụ:


int x = 5; int y = 10; x = y = 15; // Điều này gán giá trị 15 cho cả x và y

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):


#include

int main() { int x = 10; // Đây là một biến cục bộ trong hàm main if (x > 5) { int y = 20; // Đây là một biến cục bộ trong khối mã if printf("x = %d, y = %d\n", x, y); } // Biến y không thể truy cập ở đây vì nó đã vượt ra khỏi phạm vi của khối mã if printf("x = %d\n", x); return 0; }

Trong ví dụ trên, biến và là biến cục bộ. Biến được khai báo trong hàm

main

, và biến được khai báo trong khối mã của câu lệnh

if

. Biến có phạm vi trong toàn bộ hàm

main

, trong khi biến chỉ có phạm vi trong khối mã của câu lệnh

if

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

if

, biến không còn tồn tại.

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


#include

int globalVar = 100; // Đây là biến toàn cục int main() { int x = 10; // Đây là biến cục bộ trong hàm main printf("x = %d, globalVar = %d\n", x, globalVar); return 0; }

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

globalVar

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 là biến cục bộ, chỉ có phạm vi trong hàm

main

. 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ụ:

    int x = 10;
  • 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ụ:

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

    float

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

    double z = 3.14159265359;
  • char: Kiểu dữ liệu ký tự (character) được sử dụng để lưu trữ một ký tự. Ví dụ:

    char ch = 'A';
  • 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

    int

    . Ví dụ:

    long int b = 1000L;
  • 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ụ:

    unsigned int c = 20;
  • 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ụ:

    unsigned char uc = 'B';

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

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

printf



scanf

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


printf("Định dạng chuỗi", tham số1, tham số2, ...);

  • Ví dụ:


int age = 30; float salary = 1000.50; printf("Tuổi: %d\n", age); printf("Lương: %.2f\n", salary);

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


scanf("Định dạng chuỗi", &biến1, &biến2, ...);

  • Ví dụ:


int age; float salary; printf("Nhập tuổi: "); scanf("%d", &age); printf("Nhập lương: "); scanf("%f", &salary);

Lưu ý rằng trong

scanf

, bạn phải sử dụng dấu trước tên biến để truyền địa chỉ của biến đó cho

scanf

. Điều này là cần thiết để

scanf

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ư

%d

hay

%s

được sử dụng trong

printf

với

scanf

đượ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:


kiểu_dữ_liệu tên_hàm(tham_số) { // Khối mã của hàm // Thực hiện các công việc ở đây return giá_trị_trả_về; // Optional }


  • kiểu_dữ_liệu

    : Đâ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

    void

    .

  • tên_hàm

    : Đâ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.

  • tham_số

    : Đâ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ể.

  • khối mã của hàm

    : Đâ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ể.

  • giá_trị_trả_về

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

    return

    để 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

    return

    với

    void

    .
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ụ:


int sum(int a, int b) { return a + b; } int main() { int x = 5; int y = 3; int result = sum(x, y); // Gọi hàm sum và lưu kết quả vào result return 0; }

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ụ:


int multiply(int a, int b) { return a * b; // Hàm trả về một giá trị kiểu int } void greet() { printf("Hello, World!\n"); // Hàm không trả về giá trị (void) }

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ề.


// Hàm nguyên mẫu cho hàm sum int sum(int a, int b); int main() { int x = 5; int y = 3; int result = sum(x, y); // Gọi hàm sum và lưu kết quả vào result return 0; } // Định nghĩa hàm sum sau main hoặc trong một tệp mã nguồn khác int sum(int a, int b) { return a + b; }

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ự (

char

). 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 (

'\0'

) để 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ụ:


char myString[20] = "Hello, World!";

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

    %s


char result = "Hello" printf("Ket qua la %s", result);

  • Sao chép chuỗi: Bạn có thể sử dụng hàm

    strcpy

    để sao chép một chuỗi vào một chuỗi khác.


char source[] = "Hello"; char destination[20]; strcpy(destination, source); // Sao chép chuỗi source vào chuỗi destination

  • 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

    strcat

    .


char str1[] = "Hello"; char str2[] = ", World!"; strcat(str1, str2); // Kết quả: "Hello, World!"

  • So sánh chuỗi: Hàm

    strcmp

    được sử dụng để so sánh hai chuỗi.


char str1[] = "apple"; char str2[] = "banana"; int result = strcmp(str1, str2); // Kết quả: result < 0 (str1 đứng trước str2 trong thứ tự từ điển)

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

    strlen

    .


char str[] = "Hello, World!"; int length = strlen(str); // Kết quả: 13

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

'\0'

, 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:


kiểu_dữ_liệu tên_mảng[kích_thước];


  • kiểu_dữ_liệu

    : Đây là kiểu dữ liệu của các phần tử trong mảng, ví dụ

    int

    ,

    float

    ,

    char

    , …

  • tên_mảng

    : Đây là tên của mảng.

  • kích_thước

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

Ví dụ:


int numbers[5]; // Định nghĩa mảng numbers với 5 phần tử kiểu int char letters[10]; // Định nghĩa mảng letters với 10 phần tử kiểu char

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

[]

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


int numbers[5] = {10, 20, 30, 40, 50}; int firstNumber = numbers[0]; // firstNumber = 10 int thirdNumber = numbers[2]; // thirdNumber = 30

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


kiểu_dữ_liệu tên_mảng[số_hàng][số_cột];


  • kiểu_dữ_liệu

    : Đây là kiểu dữ liệu của các phần tử trong mảng 2D, ví dụ

    int

    ,

    float

    ,

    char

    , …

  • tên_mảng

    : Đây là tên của mảng.

  • số_hàng

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

  • số_cột

    : Số lượng cột trong mảng.

Ví dụ:


int matrix[3][3]; // Định nghĩa mảng 2D với 3 hàng và 3 cột float data[2][4]; // Định nghĩa mảng 2D với 2 hàng và 4 cột

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

[][]

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


int matrix[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; int element = matrix[1][2]; // element = 6

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 if…else

Câu lệnh

if...else

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:


if (điều_kiện) { // Khối mã thực hiện nếu điều_kiện là true } else { // Khối mã thực hiện nếu điều_kiện là false }

Ví dụ:


int x = 3; printf("x lớn hơn 5.\n"); } else { printf("x không lớn hơn 5.\n"); }

5.8.2 Câu lệnh if… elseif …else

Câu lệnh

if...else if...else

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:


if (điều_kiện_1) { // Khối mã thực hiện nếu điều_kiện_1 là true } else if (điều_kiện_2) { // Khối mã thực hiện nếu điều_kiện_2 là true } else { // Khối mã thực hiện nếu không có điều kiện nào là true }

Ví dụ:


int x = 7; if (x < 5) { printf("x nhỏ hơn 5.\n"); } else if (x == 5) { printf("x bằng 5.\n"); } else { printf("x lớn hơn 5.\n"); }

5.8.3 Câu lệnh switch

Câu lệnh

switch

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:


switch (biểu_thức) { case giá_trị_1: // Khối mã thực hiện nếu biểu_thức bằng giá_trị_1 break; case giá_trị_2: // Khối mã thực hiện nếu biểu_thức bằng giá_trị_2 break; // Các trường hợp khác default: // Khối mã thực hiện nếu không có trường hợp nào khớp }

Ví dụ:


int choice = 2; switch (choice) { case 1: printf("Bạn đã chọn tùy chọn 1.\n"); break; case 2: printf("Bạn đã chọn tùy chọn 2.\n"); break; default: printf("Tùy chọn không hợp lệ.\n"); }

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:


for (khởi_tạo; điều_kiện; cập_nhật) { // Khối mã thực hiện trong mỗi vòng lặp }

Ví dụ:


for (int i = 0; i < 5; i++) { printf("Giá trị của i: %d\n", i); }

5.9.2 Vòng lặp while

Vòng lặp

while

đượ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:


while (điều_kiện) { // Khối mã thực hiện trong mỗi vòng lặp }

Ví dụ:


int x = 0; while (x < 5) { printf("Giá trị của x: %d\n", x); x++; }

5.9.3 Vòng lặp do…while:

Vòng lặp

do...while

tương tự với

while

, 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:


do { // Khối mã thực hiện ít nhất một lần } while (điều_kiện);

Ví dụ:


int y = 0; do { printf("Giá trị của y: %d\n", y); y++; } while (y < 5);

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

struct

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:


struct TenCauTruc { kieu_du_lieu1 thanh_vien1; kieu_du_lieu2 thanh_vien2; // ... };

Ví dụ:


struct Person { char name[50]; int age; float height; };

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.


struct Person { int id; char firstName[20]; char lastName[20]; int age; char address[100]; }; int main(){ struct Person ps1, ps2; // Khai báo mảng struct Person ps[100]; }

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 => Truy xuất tới thuộc tính khi khai báo biến bình thường.
  • Sử dụng

    ->

    => 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

firstName

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


Person ps; printf("First name: %s", ps.firstName);

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

enum

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

{}

. Ví dụ:


enum Season { Spring, Summer, Autumn, Winter };

Trong ví dụ này,

Season

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

Spring

,

Summer

,

Autumn

, và

Winter

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.

Spring

có giá trị là 0,

Summer

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ụ:


enum Day { Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };

Trong ví dụ này,

Monday

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ụ:


enum Season currentSeason = Spring;

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.


void printDay(enum Day day) { switch (day) { case Monday: printf("Monday"); break; case Tuesday: printf("Tuesday"); break; // ... } }

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

    stdio.h

    chứa hàm liên quan đến đầu vào và đầu ra, như

    printf

    ,

    scanf

    , và

    fgets

    . 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.
  2. 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.
  3. Tiêu đề (Header Files): Tiêu đề (header) là tệp văn bản có phần mở rộng

    .h

    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

    . Ví dụ:

    #include

    .
  4. 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.
  5. 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 sau kiểu dữ liệu. Ví dụ:


int *ptr; // Con trỏ kiểu int double *ptr2; // Con trỏ kiểu double

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ử để lấy địa chỉ của biến. Ví dụ:


int x = 10; int *ptr = &x // Gán địa chỉ của biến x cho con trỏ ptr

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ử . Ví dụ:


int y = *ptr; // Lấy giá trị mà con trỏ ptr đang trỏ đến (giá trị của biến x)

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ụ:


*ptr = 20; // Thay đổi giá trị của biến x thành 20

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ụ:


int arr[5] = {10, 20, 30, 40, 50}; int *ptr = arr; // Gán địa chỉ của mảng cho con trỏ // Di chuyển con trỏ qua các phần tử mảng và in giá trị for (int i = 0; i < 5; i++) { printf("Gia tri cua phan tu thu %d: %d\n", i, *ptr); ptr++; // Di chuyen con tro toi phan tu tiep theo }

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.


#include

int main() { int arr[5] = {10, 20, 30, 40, 50}; // Khai báo một con trỏ kiểu int và gán địa chỉ của mảng cho nó int *ptr = arr; // Sử dụng con trỏ để truy cập và in các phần tử của mảng for (int i = 0; i < 5; i++) { printf("Gia tri cua phan tu thu %d: %d\n", i, *ptr); ptr++; // Di chuyển con trỏ tới phần tử tiếp theo trong mảng } return 0; }

  • Chúng ta đã khai báo một mảng

    arr

    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

    int

    có tên

    ptr

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

    arr

    cho con trỏ

    ptr

    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

    for

    , 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

    ptr++

    .

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


Gia tri cua phan tu thu 0: 10 Gia tri cua phan tu thu 1: 20 Gia tri cua phan tu thu 2: 30 Gia tri cua phan tu thu 3: 40 Gia tri cua phan tu thu 4: 50

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 (

'\0'

). 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:


#include

int main() { char greeting[] = "Hello, World!"; // Một mảng ký tự (chuỗi) char *ptr = greeting; // Con trỏ kiểu char trỏ tới chuỗi // Sử dụng con trỏ để truy cập và in từng ký tự của chuỗi while (*ptr != '\0') { printf("%c", *ptr); ptr++; // Di chuyển con trỏ tới ký tự tiếp theo } return 0; }

  • Chúng ta đã khai báo một mảng ký tự có tên

    greeting

    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

    char

    có tên

    ptr

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

    greeting

    cho con trỏ

    ptr

    , 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

    while

    , 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

    ptr++

    .
  • Chuỗi kết thúc bằng ký tự null

    '\0'

    , 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

fopen()

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

fprintf()

hoặc

fputc()

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


#include

int main() { FILE *file; file = fopen("example.txt", "w"); // Mở tệp để ghi if (file != NULL) { fprintf(file, "Hello, World!\n"); fclose(file); } else { printf("Khong the mo tệp.\n"); } return 0; }

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

fprintf()

để 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

fopen()

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

fscanf()

hoặc

fgetc()

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


#include

int main() { FILE *file; char data[100]; // Mảng để lưu dữ liệu từ tệp file = fopen("example.txt", "r"); // Mở tệp để đọc if (file != NULL) { fscanf(file, "%s", data); // Đọc dữ liệu từ tệp vào mảng printf("Noi dung trong tệp: %s\n", data); fclose(file); } else { printf("Khong the mo tệp.\n"); } return 0; }

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

fscanf()

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

data

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.

Các đặc trưng[sửa | sửa mã nguồn]

Tổng quan[sửa | sửa mã nguồn]

C là một ngôn ngữ lập trình tương đối nhỏ gọn vận hành gần với phần cứng và nó giống với ngôn ngữ Assembler hơn hầu hết các ngôn ngữ bậc cao. Hơn thế, C đôi khi được đánh giá như là “có khả năng di động”, cho thấy sự khác nhau quan trọng giữa nó với ngôn ngữ bậc thấp như là Assembler, đó là việc mã C có thể được dịch và thi hành trong hầu hết các máy tính, hơn hẳn các ngôn ngữ hiện tại trong khi đó thì Assembler chỉ có thể chạy trong một số máy tính đặc biệt. Vì lý do này C được xem là ngôn ngữ bậc trung.

C đã được tạo ra với một mục tiêu là làm cho nó thuận tiện để viết các chương trình lớn với số lỗi ít hơn trong mẫu hình lập trình thủ tục mà lại không đặt gánh nặng lên vai người viết ra trình dịch C, là những người bề bộn với các đặc tả phức tạp của ngôn ngữ. Cuối cùng C có thêm những chức năng sau:

  • Một ngôn ngữ cốt lõi đơn giản, với các chức năng quan trọng chẳng hạn như là những hàm hay việc xử lý tập tin sẽ được cung cấp bởi các bộ thư viện các thủ tục.
  • Tập trung trên mẫu hình lập trình thủ tục, với các phương tiện lập trình theo kiểu cấu trúc.
  • Một hệ thống kiểu đơn giản nhằm loại bỏ nhiều phép toán không có ý nghĩa thực dụng.
  • Dùng ngôn ngữ tiền xử lý, tức là các câu lệnh tiền xử lý C, cho các nhiệm vụ như là định nghĩa các macro và hàm chứa nhiều tập tin mã nguồn (bằng cách dùng câu lệnh tiền xử lý dạng

    #include

    chẳng hạn).
  • Mức thấp của ngôn ngữ cho phép dùng tới bộ nhớ máy tính qua việc sử dụng kiểu dữ liệu

    pointer

    .
  • Số lượng từ khóa rất nhỏ gọn.
  • Các tham số được đưa vào các hàm bằng giá trị, không bằng địa chỉ.
  • Hàm các con trỏ cho phép hình thành một nền tảng ban đầu cho tính đóng và tính đa hình.
  • Hỗ trợ các bản ghi hay các kiểu dữ liệu kết hợp do người dùng từ khóa định nghĩa

    struct

    cho phép các dữ liệu liên hệ nhau có thể được tập hợp lại và được điều chỉnh như là toàn bộ.

Một số chức năng khác mà C không có (hay còn thiếu) nhưng có thể tìm thấy ở các ngôn ngữ khác bao gồm:

  • An toàn kiểu,
  • Tự động Thu dọn rác,
  • Các lớp hay các đối tượng cùng với các ứng xử của chúng (xem thêm OOP),
  • Các hàm lồng nhau,
  • Lập trình tiêu bản hay Lập trình phổ dụng,
  • Quá tải và Quá tải toán tử,
  • Các hỗ trợ cho đa luồng, đa nhiệm và mạng.

Mặc dù C còn thiếu nhiều chức năng hữu ích nhưng lý do quan trọng để C được chấp nhận vì nó cho phép các trình dịch mới được tạo ra một cách nhanh chóng trên các nền tảng mới và vì nó cho phép người lập trình dễ kiểm soát được những gì mà chương trình (do họ viết) thực thi. Đây là điểm thường làm cho mã C chạy hiệu quả hơn các ngôn ngữ khác. Thường thì chỉ có ngôn ngữ ASM chỉnh bằng tay chạy nhanh hơn (ngôn ngữ C), bởi vì ASM kiểm soát được toàn bộ máy. Mặc dù vậy, với sự phát triển các trình dịch C, và với sự phức tạp của các CPU hiện đại có tốc độ cao, C đã dần thu nhỏ khác biệt về tốc độ này.

Một lý do nữa cho việc C được sử dụng rộng rãi và hiệu quả là do các trình dịch, các thư viện và các phần mềm thông dịch của các ngôn ngữ bậc cao khác lại thường được tạo nên từ C.

Ví dụ “hello, world”[sửa | sửa mã nguồn]

Ví dụ đơn giản sau đây được thấy trong lần in đầu tiên của cuốn “The C Programming Language”, và đã trở thành bài tiêu chuẩn trong chương nhập môn của hầu hết các loại sách giáo khoa về lập trình. Chương trình hiển thị câu “hello, world!” trên đầu ra chuẩn, mà thường là một màn hình. Mặc dù vậy, nó có thể xuất ra một tập tin hay xuất ra trên một thiết bị phần cứng kể cả trên một vùng chứa, tùy thuộc vào việc đầu ra chuẩn được chỉ thị vào đâu khi chương trình này được thực thi.

#include

int main(void) { printf(“hello, world!”); return 0; }

Chương trình trên sẽ dịch đúng trong hầu hết các trình dịch hỗ trợ chuẩn ANSI C hay chuẩn C99.

Sau đây là các phân tích theo từng dòng mã của ví dụ trên

#include


#include. Điều này sẽ làm cho bộ tiền xử lý (bộ tiền xử lý này là một công cụ để kiểm tra mã nguồn trước khi nó được dịch) tiến hành thay dòng lệnh đó bởi toàn bộ các dòng mã hay thực thể trong tập tin mà nó đề cập đến (tức là tập tin


stdio.h). Dấu ngoặc nhọn bao quanh


stdio.hcho biết rằng tập tin này có thể tìm thấy trong các nơi đã định trước cho bộ tiền xử lý biết thông qua các đường tìm kiếm đến các tập tin


header. Tập hợp các tập tin được khai báo sử dụng qua các chỉ thị tiền xử lý còn được gọi là các tập tin bao gồm.

int main(void)


main. Hàm này có mục đích đặc biệt trong C. Khi chương trình thi hành thì hàm


main()được gọi trước tiên. Phần mã


intchỉ ra rằng giá trị trả về của hàm


main(tức là giá trị mà


main()sẽ được trả về sau khi thực thi) sẽ có kiểu là một số nguyên. Còn phần mã


(void)cho biết rằng hàm


mainsẽ không cần đến tham số để gọi nó. Xem thêm Void

Dấu ‘{‘ cho biết sự bắt đầu của định nghĩa của hàm

main

.

printf(“hello, world\n”);

Dòng trên gọi đến một hàm chuẩn khác tên là

printf

. Hàm này đã được khai báo trước đó trong tập tin

stdio.h

. Dòng này sẽ cho phép tìm và thực thi mã (đã được hỗ trợ sẵn) với ý nghĩa là hiển thị lên đầu ra chuẩn dòng chữ

hello, world

.Mã ký tự

\n

là một dãy thoát được chuyển dịch thành dấu ký tự EOL (viết tắt từ chữ End-Of-Line) có nghĩa là chuyển vị trí dấu nhắc xuống đầu một dòng kế. Giá trị trả về của hàm

printf

(theo khai báo nguyên mẫu chuẩn của hàm này trong C) có kiểu

int

, nhưng vì giá trị trả về này không được (người lập trình) dùng tới nên giá trị đó bị bỏ qua (một cách lặng lẽ).

return 0;

Dòng này sẽ kết thúc việc thực thi mã của hàm

main

và buộc nó trả về giá trị 0 (là một số nguyên như khai báo ban đầu

int main

).

Dấu ‘}’ cho biết việc kết thúc mã cho hàm

main

.

Các kiểu[sửa | sửa mã nguồn]

C có một hệ thống kiểu tương tự như của Pascal, mặc dù chúng khác nhau trong một số khía cạnh. Có nhiều kiểu cho các số nguyên với nhiều cỡ cho có đấu và không có dấu, có kiểu số floating point, kiểu các ký tự

char

, các kiểu thứ tự

enum

, kiểu bản ghi

record

và kiểu đơn vị

union

.

C tạo ra sự mở rộng mạnh mẽ việc sử dụng của kiểu các con trỏ

pointer

, một dạng đơn giản các tham chiếu mà chúng chứa địa chỉ các vùng nhớ. Các con trỏ có thể được tham chiếu ngược (dereference) để lấy về giá trị của dữ liệu được chứa trong địa chỉ đó (địa chỉ mà con trỏ chỉ vào). Địa chỉ này có thể được điều chỉnh bằng các phép gán thông thường và các phép toán số học trên con trỏ. Trong thời gian thực thi, một con trỏ đại diện cho một địa chỉ của bộ nhớ. Trong thời gian chuyển dịch, nó là một kiểu phức tạp đại diện cho cả địa chỉ và kiểu của dữ liệu. Điều này cho phép các biểu thức bao gồm các con trỏ được kiểm tra về kiểu. Các con trỏ thì được dùng cho nhiều mục tiêu trong C. Các dòng ký tự

string

thường được đại diện bởi một con trỏ chỉ tới một dãy của các ký tự. Sự cấp phát bộ nhớ động, được miêu tả sau đây, thì được tiến hành thông qua các con trỏ.

Một con trỏ rỗng có nghĩa là nó không chỉ đến một chỗ nào hết. Điều này có ích trong những trường hợp như là con trỏ

next

trong một nút cuối của một danh sách liên kết

linked list

. Việc tham chiếu ngược một con trỏ trống gây ra các biểu hiện không dự đoán trước được. Các con trỏ kiểu

void

thì lại có thể chỉ đến một đối tượng mà không cần biết kiểu của đối tượng đó. Điều này đặc biệt hữu dụng trong lập trình tiêu bản bởi vì cỡ và kiểu của các đối tượng mà chúng chỉ tới thì không thể biết được và do đó không thể thực hiện tham chiếu ngược, nhưng chúng lại có thể được hoán chuyển thành các con trỏ của các kiểu khác.

Các kiểu mảng

array

trong C thì có cỡ cố định, độ lớn tĩnh của nó phải được biết trước trong thời gian chuyển dịch. Điều này gây nhiều trở ngại trong thực tế bởi vì người ta có thể chỉ định các vùng nhớ ở thời gian thực thi dựa trên các thư viện chuẩn và hành xử chúng như là các mảng. Không như các ngôn ngữ khác, C biểu thị các mảng giống như trường hợp các con trỏ: chúng đóng vai trò một địa chỉ của bộ nhớ và một kiểu dữ liệu. Do đó, các giá trị chỉ số có thể vượt quá cỡ của một mảng.

C cũng cung cấp các kiểu mảng đa chiều. Các giá trị chỉ số của các mảng đa chiều thì được gán theo thứ tự hàng chính. Một cách có ý nghĩa thì các mảng này hoạt động như là mảng của các mảng nhưng thực chất chúng được phân bố như là mảng một chiều với việc tính và tạo các vị trí tương đối.

C thường được dùng trong việc lập trình các hệ thống bậc thấp, ở đó có thể cần thiết để xem số nguyên như là một địa chỉ của bộ nhớ, là một giá trị double precision, hay là một kiểu con trỏ. Trong các trường hợp này, C cung cấp việc hoán chuyển, mà phép toán này sẽ bắt buộc chuyển đổi giá trị từ một kiểu sang một kiểu khác. Dùng phép hoán chuyển sẽ làm mất đi phần nào tính an toàn mà thường được cung cấp bởi hệ thống kiểu.

Lưu trữ dữ liệu[sửa | sửa mã nguồn]

Một trong những chức năng quan trọng nhất của một ngôn ngữ lập trình là việc cung cấp cơ sở cho việc quản lý bộ nhớ và các đối tượng được chứa trong bộ nhớ. C cung ứng 3 phương cách để cấp phát bộ nhớ cho các đối tượng:

  1. Sự cấp phát vùng nhớ tĩnh: khoảng trống dành cho đối tượng thì được cung cấp trong phần mã nhị phân ở thời gian dịch; những đối tượng này có một thời gian sống lâu dài theo sự tồn tại của phần mã nhị phân chứa chúng (các đối tượng).
  2. Sự cấp phát vùng nhớ tự động: Các đối tượng tạm thời có thể được chứa trong một chồng (stack), và khoảng trống này thì được trả về một cách tự động và có thể được dùng lại sau khi khối mã mà chúng (tức các đối tượng tạm thời) được khai báo đã thực thi xong.
  3. Sự cấp phát vùng nhớ động: Các khối của bộ nhớ với bất kì cỡ lớn mong muốn nào đều có thể được yêu cầu (hay xin) trong thời gian thi hành bằng cách dùng các hàm thư viện như là

    malloc()

    ,

    realloc()



    free()

    từ một khu vực của bộ nhớ có tên là heap; các khối này có thể được tái dụng sau khi gọi hàm

    free()

    để hoàn trả chúng lại cho bộ nhớ.

Ba phương án này thích hợp cho các tình huống khác nhau và có những hậu quả khác nhau. Ví dụ, kiểu cấp phát tĩnh sẽ không cần thời gian (để tính toán) cho sự cấp phát, kiểu cấp phát tự động sẽ cần một khoảng thời gian nào đó cho dự tính, và kiểu cấp phát động có thể đòi hỏi một lượng lớn thời gian dùng dễ tính toán cho việc cấp phát và hoàn trả (các vùng nhớ đã được yêu cầu trước đó). Mặt khác, khoảng trống của chồng thường giới hạn cho vùng nhớ tĩnh hay cho khoảng trống của heap, và chỉ kiểu cấp phát vùng nhớ động là cho phép sự cấp phát cho các đối tượng mà kích thước của nó chỉ có thể biết được trong lúc thi hành. Hầu hết các chương trình C đều dùng nhiều cả ba phương cách này.

Khi có thể thì sự cấp phát tự động hay sự cấp phát tĩnh thường được dề nghị dùng vì kho nhớ được quản lý bởi trình dịch, giải phóng cho người lập trình những lồi lầm phiền hà khi phải xin cấp phát và hoàn trả các vùng nhớ bằng tay. Rất tiếc nhiều cấu trúc dữ liệu có thể trương nở trong thời gian thực thi và vì kiểu cấp phát tĩnh và kiểu tự động phải có một độ lớn cố định ở thời gian dịch nên trong nhiều tình huống mà buộc phải dùng kiểu cấp phát động. Các dãy thay đổi về độ lớn là một ví dụ điển hình của trường hợp này. (Xem ví dụ từ bài malloc về các dãy được cấp phát vùng nhớ động.)

[16.02.2024] NS vs KT | Game 1 | Bình Luận Tiếng Việt | LCK Mùa Xuân 2024
[16.02.2024] NS vs KT | Game 1 | Bình Luận Tiếng Việt | LCK Mùa Xuân 2024

Lịch sử[sửa | sửa mã nguồn]

Những phát triển ban đầu[sửa | sửa mã nguồn]

Phát triển khởi đầu của C xảy ra ở AT&T Bell Labs giữa 1969 và 1973; theo Ritchie thì thời gian sáng tạo nhất là vào năm 1972. Nó được đặt tên là C vì nhiều đặc tính của nó rút ra từ một ngôn ngữ trước đó là B.

Thêm vào đó, các điểm khác với ngôn ngữ nguyên thủy “B”: Ken Thompson kể tới ngôn ngữ lập trình BCPL, nhưng ông ta cũng đã tạo ra ngôn ngữ là Bon để vinh danh vợ mình.

Có nhiều truyền thuyết về nguồn gốc của C và hệ điều hành liên quan tới nó là Unix bao gồm:

  • Sự phát triển của C là kết quả của các lập trình viên đã muốn chơi Space TravelLưu trữ 2014-08-06 tại Wayback Machine. Họ đã chơi nó trên mainframe của hãng làm việc, nhưng bị thiếu khả năng (chạy) và phải hỗ trợ khoảng 100 người dùng, Thompson và Ritchie tìm thấy rằng họ đã không có đủ sự kiểm soát tàu vũ trụ (của trò chơi) để tránh được các va chạm khỏi sự chuyển dịch của các thiên thạch. Do đó, họ quyết định để xuất trò chơi này sang một máy PDP-7 để không trong văn phòng. Nhưng nó lại không có hệ điều hành; do đó, họ viết một hệ điều hành. Tiếp tục, họ quyết định để xuất hệ điều hành này sang PDP-11 của văn phòng nhưng việc này thật khó vì tất cả mã đều là ngôn ngữ Assembly. Họ quyết định dùng một ngôn ngữ dễ xuất cấp cao để hệ điều hành có thể xuất được dễ dàng từ máy tính này sang máy khác. Họ đã tìm đến ngôn ngữ B, nhưng nó lại thiếu các chức năng để khai thác một số khả năng của PDP-11. Vậy nên họ đã sáng tạo ra một ngôn ngữ mới là C.
  • Unix nguyên đã được phát triển để tạo ra một hệ thống tự động lập hồ sơ cho các bằng phát minh. Phiên bản đầu tiên của Unix đã phát triển từ ngôn ngữ Assembly. Sau đó, ngôn ngữ C đã được phát triển để từ đó thay thế hệ điều hành mới.

Cho đến 1973, C đã trở nên đủ mạnh để dùng viết nhân cho Unix, thay vì trước nó chúng được viết bằng Assembly trong các máy PDP-11/20. Đây là lần đầu tiên mà nhân của một hệ điều hành được lắp thành bằng một ngôn ngữ khác hơn Assembly.

K&R C[sửa | sửa mã nguồn]

Năm 1978, Ritchie và Brian Kernighan xuất bản lần đầu cuốn The C Programming Language. Sách này được những người lập trình biết tới như là “K&R”, được dùng trong nhiều năm như là một đặc tả không chính thức của C. Phiên bản C mà cuốn sách đó đề cập thường được gọi là “K&R C”. (Lần xuất bản thứ hai của cuốn này cũng bao gồm chuẩn ANSI C).

K&R giới thiệu các chức năng sau đây:

  • Kiểu dữ liệu

    struct
  • Kiểu dữ liệu

    long int
  • Kiểu dữ liệu

    unsigned int
  • Toán tử

    =+

    đã được đổi thành

    +=

    , và tương tự cho các toán tử khác để tránh gây hiểu nhầm cho bộ phân tích từ vựng của trình dịch C. (Ví dụ: sự giống nhau dễ lầm lẫn của hai câu lệnh

    i =+ 10



    i = +10

    ).

K&R C thường được xem là phần cơ bản nhất của ngôn ngữ mà nó cần phải có cho một trình dịch C. Trong nhiều năm, ngay cả sau khi ANSI C được giới thiệu, nó đã được xem như là “mẫu số chung nhỏ nhất” mà người lập trình C phải bám lấy nếu muốn có được khả năng dịch chuyển (tái dụng trên nhiều máy) bởi vì không phải mọi trình dịch đều hỗ trợ toàn bộ ANSI C, và một cách hợp lý là mã viết trong K&R C cũng là mã hợp lệ trong ANSI C.

Trong các phiên bản trước đây của C, chỉ có những hàm nào trả về một số khác số nguyên mới cần được khai báo trước khi dùng. Một hàm dùng mà không có bất kì sự khai báo nào trước đó được giả thiết là sẽ trả về một số nguyên.

Ví dụ việc gọi với yêu cầu của sự khai báo trước:

long int SomeFunction(); int CallingFunction() { long int ret; ret = SomeFunction(); }

Ví dụ việc gọi mà không cần phải khai báo trước:

int CallingFunction() { int ret; ret = SomeOtherFunction(); } int SomeOtherFunction() { return 0; }

Bởi vì nguyên mẫu của K&R đã không bao gồm bất kì thông tin nào về các tham số của hàm, chức năng kiểm tra kiểu của các đối số đã không được tiến hành, mặc dù một số trình dịch sẽ cho ra thông báo cảnh cáo nếu một hàm đã được gọi với số lượng tham số không đúng.

Trong nhiều năm tiếp theo của sự tái bản K&R C, nhiều chức năng “không chính thức” đã được thêm vào cho ngôn ngữ, được hỗ trợ bởi các trình dịch của AT&T và một số nơi khác. Trong đó bao gồm:

  • Các hàm có kiểu

    void

    và dữ liệu có kiểu

    void *

    .
  • Các hàm trả về các kiểu

    struct

    hay

    union

    .
  • Tên của các miền trong một không gian tên cho mỗi kiểu

    struct

    .
  • Phép gán cho kiểu dữ liệu

    struct

    .
  • Hằng

    const

    được xem là đối tượng chỉ cho phép đọc.
  • Một thư viện chuẩn được sự hợp tác để xây dựng bởi nhiều nhà sản xuất.
  • Các kiểu enumeration.
  • Kiểu chính xác đơn

    float

    .

ANSI C và ISO C[sửa | sửa mã nguồn]

Vào khoảng cuối thập niên 1970, C bắt đầu thay thế vai trò của BASIC như là một ngôn ngữ lập trình cho microcomputer. Suốt thập niên 1980 nó đã được chấp thuận dùng trong IBM PC, và sự phổ biến của nó bắt đầu tăng một cách lớn lao.

Trong cùng thời kỳ, Bjarne Stroustrup và đồng nghiệp ở Bell Labs đã bắt tay cho thêm vào C các cấu trúc ngôn ngữ lập trình hướng đối tượng.

Ngôn ngữ họ tạo ra gọi là C++ nay trở thành ngôn ngữ lập trình ứng dụng phổ biến nhất trên hệ điều hành Microsoft Windows; C vẫn còn rất phổ biến trong thế giới UNIX. Một ngôn ngữ khác cũng được phát triển trong khoảng thời gian này là Objective-C, cũng là một mở rộng lập trình hướng đối tượng cho C. Dù không phổ biến như C++, nó được dùng để phát triển các ứng dụng Cocoa của Mac OS X.

Trong 1983, Viện Tiêu chuẩn Quốc gia Hoa Kỳ (ANSI) thành lập hội đồng X3J11 để hoàn tất một tiêu chuẩn dặc tả của C. Sau một quá trình khó khăn và lâu dài, tiêu chuẩn đã hoàn tất vào 1989 và được công nhận là “Programming Language C” ANSI X3.159-1989. Phiên bản ngôn ngữ này thường được nhắc đến như là ANSI C.

Trong 1990, Tiêu chuẩn ANSI C (với một vài chi tiết nhỏ được điều chỉnh) đã được tiêu chuẩn hóa bởi Tổ chức Quốc tế về Tiêu chuẩn hóa (ISO) như là ISO/IEC 9899:1990.

Một điểm mạnh của quá trình tiêu chuẩn hoá ANSI C là làm cho K&R C trở thành một tập con của nó; nó tiếp nhận nhiều chức năng không chính thức của K&R C như là một hệ quả. Xa hơn, hội đồng tiêu chuẩn cũng làm cho ANSI C bao gồm thêm nhiều chức năng mới, như là các nguyên mẫu của hàm (mượn từ C++), và khả năng tiền xử lý mạnh hơn.

Ngày nay, ANSI C được hỗ trợ bởi hầu hết các trình dịch. Hầu hết các mã C ngày nay được viết dựa ttrên ANSI C. Mọi chương trình chỉ viết trong chuẩn C thì sẽ đảm bảo việc thực thi chính xác trên mọi nền nào cho phép dùng C. Mặc dù vậy, nhiều chương trình đã viết ra chỉ dịch được trong một số nền hoặc với một số trình dịch nào đó bởi vì các lý do sau:

  1. Dùng các thư viện không chuẩn, như là cho GUI.
  2. Một số trình dịch không hoàn toàn theo đúng chuẩn ANSI C hay các chuẩn tiếp sau trong các chế độ làm việc mặc nhiên của chúng.
  3. Phụ thuộc vào kích thước của một số kiểu dữ liệu cũng như là endian của nền. (Chẳng hạn, trong một số nền kích thước của kiểu

    int

    có thể nhiều hơn hay ít hơn—4, 8 hay 16 byte—trong nền khác.)

Macro

__STDC__

có thể được dùng để chẻ mã nguồn thành các phần theo ANSI C và K&R

#if __STDC__ extern int getopt(int,char * const *,const char *); #else extern int getopt(); #endif

Một số chuyên gia khuyên rằng dùng

#if __STDC__

như trên, thay cho

#ifdef __STDC__

bởi vì một số trình dịch cài giá trị

__STDC__

về 0 để chỉ việc không theo chuẩn ANSI (trong khi một số trình dịch khác lại cài về giá trị khác 0).

C99[sửa | sửa mã nguồn]

Sau quá trình chuẩn hóa ANSI, đặc tả của ngôn ngữ C tương đối được giữ nguyên trong một thời gian, trong khi C++ tiếp tục thâm nhập. (Đúng ra, đã có tu chính số 1 tạo ra phiên bản mới của C trong 1995, nhưng phiên bản này hiếm khi được đồng thuận.) Cho đến cuối thập niên 1990 một tiêu chuẩn mới đã được phát hành là ISO 9899:1999. Tiêu chuẩn này thường được mệnh danh là “C99”. Nó đã tiếp thu ANSI C trong tháng 3 năm 2000.

Những chức năng mới trong C99 bao gồm:

  • Các hàm

    inline

    .
  • Các biến có thể được khai báo ở bất kì chỗ nào (như là trong C++).
  • Nhiều kiểu dữ liệu mới được đưa vào bao gồm kiểu

    long long int

    (để giảm khó khăn trong việc chuyển hệ từ 32-bit sang 64-bit), kiểu boolean và kiểu

    complex

    để dùng cho các số phức.
  • Các mảng có chiều dài thay đổi được.
  • Hỗ trợ cho dòng lệnh chú giải bắt đầu với

    //

    như trong C++ và nhiều ngôn ngữ khác.
  • Nhiều hàm thư viện mới như là

    snprintf()

    .
  • Nhiều tập tin tiêu dề như là

    stdint.h

    .

Điều thú vị trong việc hỗ trợ cho chuẩn C99 là một kết quả pha trộn. Trong khi GCC và nhiều trình dịch khác hiện hỗ trợ hầu hết các chức năng của C99, thì trình dịch của Microsoft và Borland lại không tuân theo và hai công ty này dường như không thích thú để thêm vào các hỗ trợ này.

Khai báo dữ liệu[sửa | sửa mã nguồn]

Các kiểu dữ liệu cơ bản[sửa | sửa mã nguồn]

Nhiều ngôn ngữ lập trình kể cả C, biểu thị các số trong hai dạng: nguyên và thực (hay không nguyên). Sự khác nhau này hình thành từ khía cạnh kỹ thuật của các cách thức xử lý và lưu trữ các giá trị trong bộ nhớ.


intđược dùng để biểu thị các số nguyên. Kiểu nguyên có trong nhiều kích cỡ khác nhau tùy theo phân lượng bộ nhớ được dùng và độ lớn cao nhất1. Các từ khóa, có tên là các định tính, được dùng thêm vào để điều chỉnh lại kích cỡ là:


short,


longvà


long long2. Kiểu ký tự mà từ khóa của nó là


char, biểu thị đơn vị nhỏ nhất có thể địa chỉ hóa được (bởi kiến trúc máy tính) thường là một byte với 8 bit.

Dạng thực được dùng để biểu thị các số thập phân hay các bộ phận hữu tỉ. Mặc dù vậy chúng không hoàn toàn chính xác mà chỉ là các biểu thị gần đúng.

Có 3 kiểu giá trị thực bao gồm: loại có độ chính xác đơn (có đặc tả là

float

), loại có độ chính xác kép (có đặc tả là

double

), và loại có độ chính xác kép mở rộng (có đặc tả là

long double

). Mỗi loại dùng để biểu thị các giá trị không nguyên trong một dạng khác nhau.

Các kiểu nguyên có thể hoặc là có dấu (

signed

) hay không dấu (

unsigned

). Nếu không chỉ rõ khi khai báo thì mặc định (hiểu ngầm) sẽ là loại có dấu. Một ngoại lệ là các kiểu

char

,

signed char



unsigned char

đều khác nhau, và kiểu

char

có thể là loại có dấu hay không có dấu.

Đối với loại có dấu, thì

bit

có nghĩa cao nhất được dùng để biểu thị dấu (dương hay âm). Thí dụ một số nguyên 16-bit loại có dấu thì chỉ dùng 15 bit để biểu thị giá trị (tuyệt đối) của nó còn bit có nghĩa cao nhất lại được dùng để cho biết đó là số dương hay âm. Đối với loại không dấu thì bit này lại được dùng thêm vào để biểu thị giá trị.

1 = Trong các kiểu nguyên, độ lớn cao nhất biểu thị giá trị lớn nhất (tùy theo có dấu hay không) mà nó biểu thị. (Xem thêm bản dưới đây)

2 = Từ khóa

long long

được đưa thêm vào trong chuẩn C99.

Các hằng số xác định các giá trị biên[sửa | sửa mã nguồn]

Tập tin tiêu đề chuẩn

limits.h

sẽ xác định các giá trị nhỏ nhất và lớn nhất của các kiểu nguyên cơ bản cũng như là xác định các giới hạn khác. Tập tin tiêu đề chuẩn

float.h

sẽ xác định các giá trị nhỏ nhất và lớn nhất của các kiểu

float

,

double

, và

long double

. Nó cũng xác định các giới hạn khác liên quan tới việc xử lý các giá trị của dấu chấm động (floating-point) có độ chính xác đơn hay có độ chính xác kép như là chúng được định nghĩa trong chuẩn IEEE 754.

Đặc tả hiểu ngầm Đặc tả viết rõ ra Giá trị nhỏ nhất Giá trị lớn nhất
viết y hệt
viết y hệt
viết y hệt
không viết gì hết,

1— Định tính

long long

chỉ được hỗ trợ trong các trình dịch thỏa mãn chuẩn C99.

2— Các hằng

LLONG_MIN

,

LLONG_MAX

, và

ULLONG_MAX

chỉ được định nghĩa trong

limits.h

nếu trình dịch tương ứng thỏa mãn chuẩn C99.

Các giá trị biên điển hình[sửa | sửa mã nguồn]

Sau đây là danh sách kích cỡ và các biên điển hình của các kiểu nguyên. Các giá trị này có thể khác nhau tùy theo kiến trúc (máy và trình dịch). ISO C cung cấp tiêu đề inttypes.h, trong đó, có xác định các kiểu nguyên có dấu và không có dấu nhưng điều chắc chắn là kích cỡ đều nằm trong khoảng 8 đến 64 bit.

Đặc tả hiểu ngầm Đặc tả viết rõ ra Số bit Số byte Giá trị nhỏ nhất Giá trị lớn nhất
viết y hệt -128 or 0 127 hay 255
viết y hệt -128 127
viết y hệt 255
16 -32 768 32 767
16 65 535
32 -2 147 483 648 2 147 483 647
32 4 294 967 295
64 -9 223 372 036 854 775 808 9 223 372 036 854 775 807
64 18 446 744 073 709 551 615

1— Định tính

long long

chỉ được hỗ trợ trong các trình dịch thỏa mãn chuẩn C99.

Kích cỡ và giới hạn của kiểu cơ bản

int

(mà không có các định tính

short

,

long

, hay

long long

) có thể thay đổi khác nhau (nhiều hơn các kiểu nguyên khác) tùy theo sư thiết lập (của trình dịch hay của kiến trúc máy). Cho nên, một thao tác thông thường là định nghĩa nó như là một đồng nghĩa của

size_t

— trong tập tin

sys/types.h

— và do đó, đủ sức để thích ứng với giá trị lớn nhất khả dĩ của con trỏ mà có thể được cấp phát bởi hệ thống. Đặc tả UNIX đơn chỉ ra rằng kiểu

int

phải có ít nhất 32 bit, mặc dù chuẩn ISO C chỉ yêu cầu có 16 bit.

Các kiểu tham chiếu[sửa | sửa mã nguồn]

Tiền tố ký tự được dùng để biểu thị kiểu tham chiếu, mà thường được biết đến như là một con trỏ. Nếu như đặc tả

int

biểu thị một kiểu nguyên cơ bản thì đặc tả

int *

lại biểu thị một tham chiếu kiểu nguyên, tức là một loại con trỏ. Các giá trị tham chiếu tương quan sẽ bao gồm hai phần thông tin: đó là địa chỉ của vùng nhớ và một kiểu dữ liệu. Câu lệnh sau đây khai báo một biến tham chiếu nguyên có tên là

ptr

:

int *ptr;

Sự tham chiếu[sửa | sửa mã nguồn]

Khi một con trỏ địa phương được khai báo, thì chưa có một địa chỉ nào gán cho nó cả. Địa chỉ tương quan với một con trỏ có thể được thay đổi (hay hình thành) qua phép gán. Trong thí dụ sau, biến

ptr

sẽ cài cho nó chỉ tới cùng một dữ liệu như là biến nguyên cơ bản :

int *ptr; int a; ptr = &a

Để làm được việc này, toán tử tham chiếu (hay còn gọi là tham chiếu ngược) đã được dùng tới. Nó trả về vị trí của biến trong bộ nhớ (tức là địa chỉ) hay là chỗ chứa thực thể theo sau (trong thí dụ thì thực thể đó là a). Toán tử này như là công việc nó làm thường được gọi là toán tử “địa chỉ”.

Trong trường hợp tổng quát, cụm từ giá trị tham chiếu được dùng để nói về địa chỉ trong bộ nhớ của sự tham chiếu (hay tham chiếu ngược).

Sự tham chiếu ngược[sửa | sửa mã nguồn]

Cùng một biểu hiện, giá trị có thể đọc về từ một giá trị tham chiếu. Trong thí dụ sau, biến nguyên cơ bản sẽ được gán đến dữ liệu mà dữ liệu đó được tham chiếu bởi

ptr

:

int *ptr; int a, b; a = 10; //gán cho a giá trị là 10 ptr = &a //phép gán địa chỉ của a (tức là &a) lên con trỏ ‘ptr’ //để ptr bây giờ chỉ đến địa chỉ có nội dung là 10 b = *ptr //phép gán này cho b một giá trị nằm ở địa chỉ mà ‘ptr’ //chỉ tới tức là giá trị của b sẽ là 10

Để hoàn tất được thao tác này, toán tử tham chiếu ngược () đã được dùng. Nó trả về dữ liệu cơ bản. Dữ liệu này có giá trị là một tham chiếu chỉ tới (tức là một địa chỉ). Biểu thức

*ptr

là một cách viết khác của giá trị 10 (gán cho b).

Việc quá tải của ký tự * có hai biểu hiện liên hệ mà có thể gây ra sự nhầm lẫn. Hiểu được sự khác nhau giữa việc dùng nó như là một tiền tố trong một khai báo (con trỏ) và việc xem nó là toán tử tham chiếu trong một biểu thức là rất quan trọng.

Sự tham chiếu tương đương và các mệnh đề cơ bản[sửa | sửa mã nguồn]

Bảng sau đây là danh sách các mệnh đề tương đương giữa kiểu cơ bản và kiểu tham chiếu (hay tham chiếu ngược). Trong đó, biến cơ bản và biến tham chiếu

ptr

được hiểu ngầm.

Đến một giá trị cơ bản Đến một giá trị tham chiếu
Từ một giá trị cơ bản
Từ một giá trị tham chiếu

Các mảng[sửa | sửa mã nguồn]

Khai báo mảng tĩnh[sửa | sửa mã nguồn]

Trong C, mảng được dùng để biểu thị một cấu trúc của một dãy nhiều giá trị có cùng một kiểu được xếp thứ tự. Một mảng gọi là tĩnh nếu độ dài của dãy mảng này cố định. Sự khai báo của mảng tĩnh có cú pháp sau:

int array[n];

trong đó, tên của mảng là

array

sẽ có thể chứa được n giá trị của kiểu cơ bản

int

. Trong thực hành, phần bộ nhớ cho n giá trị nguyên này được để dành riêng và được gán cho mảng này (mặc dù giá trị của các phần tử trong mảng chưa được xác định). Biến

array

thực chất là một kiểu tham chiếu của kiểu nguyên; nó khởi thủy sẽ chỉ tới địa chỉ của giá trị đầu tiên trong mảng.

Truy cập các phần tử[sửa | sửa mã nguồn]

Các giá trị của một mảng được gọi là các phần tử trong mảng.

  • Một cách để truy cập đến các phần tử này là dùng đến cặp ký tự ngoặc vuông dạng

    [k]

    . Trong đó k là chỉ số (hay vị trí thứ tự đếm từ 0). Như vậy, phần tử thứ k trong mảng

    array

    sẽ có cú pháp


array[k]

Giá trị trả về của

array[k]

chính là giá trị mà nó chứa ở vị trí k. Thoạt nhìn thì cú pháp của việc truy cập này trông giống như cú pháp khi mảng

array

được khai báo nhưng về chức năng thì hoàn toàn khác nhau.

Chỉ số bắt đầu của một mảng là 0. Như vậy, chỉ số lớn nhất của một mảng bằng tổng số các phần tử trong mảng trừ đi 1. Thí dụ mảng có 10 phần tử thì giá trị của phần tử đầu tiên của A là

A[0]

và của phần tử cuối dùng là A[9].

  • Một cách truy cập khác là dùng con trỏ số học để tham chiếu đến giá trị của các phần tử trong mảng.

Bảng sau đây sẽ minh họa cách dùng của cả hai phương pháp:

Phần tử vị trí
Kiểu dùng cơ bản
Dùng con trỏ
Các mảng động[sửa | sửa mã nguồn]

C không cung cấp phương tiện để kiểm tra biên cho các mảng. Nghĩa là nó không thể bắt được các lỗi khi gán cho một mảng chỉ số âm hay chỉ số vượt quá độ đài của mảng đó. Và hơn thế nữa các chỉ số trong một mảng có thể vượt khỏi độ dài sẵn có của mảng đó.

Vì các mảng là thuần nhất, tức là nó chỉ chứa các dữ liệu có chung một kiểu nên hai thành phần thông tin cần nhớ là địa chỉ của phần tử đầu tiên và kiểu của dữ liệu.

Nhắc lại về cú pháp để khai báo một mảng tĩnh, tức là tạo ra một biến tham chiếu nguyên và cấp phát một vùng nhớ tương ứng cho nó:

int array[n];

Cách biểu hiện này có thể được tái lập với sự giúp đỡ của thư viện chuẩn C. Hàm

calloc

cung cấp một cách đơn giản để cấp phát một vùng nhớ. Hai tham số dùng đến sẽ là số lượng các phần tử và kích cỡ (độ lớn) của mỗi phần tử. Khi việc cấp phát bộ nhớ hoàn thành,

calloc

trả về một con trỏ chỉ tới phần tử đầu tiên và gán cho mọi phần tử giá trị khởi động là 0. Nếu sự cấp phát vùng nhớ không thành công (vì bộ nhớ không đủ chỗ trống hạn) thì

calloc

trả về con trỏ rỗng. Thí dụ: đoạn mã sau đây có chức năng tương đương với việc khai báo một mảng tĩnh của n phần tử có kiểu

int

:

int *array; array = calloc(n, sizeof(int));

Tuy nhiên, điểm vượt trội của cách khai báo này là việc sử dụng cấp phát vùng nhớ động . Kích cỡ của mảng (tức là lượng không gian nhớ được cấp phát một cách an toàn cho mảng) lại có thể được thay đổi sau khi đã khai báo.

Một khi việc cấp phát vùng nhớ động không còn cần thiết nữa thì phần bộ nhớ đó nên được trả về cho hệ điều hành. Thao tác này có thể tiến hành bằng hàm

free

. Nó cần một tham số: tên của con trỏ mà trước đây đã xin cấp phát vùng nhớ. Một cách an toàn hơn là sau khi đã trả vùng nhớ về cho hệ điều hành, người lập trình cũng nên cài (hay gán) cho con trỏ liên đới giá trị

NULL

để hủy bỏ địa chỉ mà nó đang chỉ tới (nhằm tránh gây ra các hiệu ứng phụ do việc tham chiếu của con trỏ này có thể gây ra).

free(array); array = NULL;

Các mảng đa chiều[sửa | sửa mã nguồn]

C có hỗ trợ việc dùng mảng đa chiều. Việc định nghĩa chúng giống như là tạo ra mảng của các mảng , mặc dù vậy trong thực tế nó không hoàn toàn đúng. Cú pháp sau:

int array2D[số_hàng][số_cột];

sẽ định nghĩa một mảng hai chiều; chiều thứ nhất có

số_hàng

phần tử. Chiều thứ hai sẽ có

số_hàng * số_cột

các phần tử—một tập hợp của

số_cột

các phần tử mà mỗi phần tử là một chiều thứ nhất.

Các mảng đa chiều hoàn toàn có thể được xem như là dãy của các con trỏ. Trong thí dụ trên,

array2D

(nếu

số_hàng

là 1) sẽ là một tham chiếu giá trị nguyên mà nó chỉ tới một mảng của

số_cột

các phần tử.

Game bẫy gà và người chơi giải trí.
Game bẫy gà và người chơi giải trí.

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.

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.

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; }

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,

#define

đượ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ư

printf

hay

scanf

, 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

stdio.h

. Để 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

    là trình biên dịch C.

  • -o hello

    chỉ định tên của tệp thực thi đầu ra sẽ là “hello”.

  • hello.c

    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

.o

) 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

-S

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:


gcc -o myprogram file1.o file2.o

Trong ví dụ này:


  • gcc

    là trình liên kết (linker).

  • -o myprogram

    chỉ định tên của tệp thực thi đầu ra sẽ là “myprogram”.

  • file1.o



    file2.o

    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

    file1.o



    file2.o

    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ừ

    file1.o



    file2.o

    để 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

    main

    .

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

Nga treo cờ ở Avdeevka, người dân bật khóc; NATO lộ chiến thuật đấu Hạm đội Biển Đen | Tin360 News
Nga treo cờ ở Avdeevka, người dân bật khóc; NATO lộ chiến thuật đấu Hạm đội Biển Đen | Tin360 News

Các hàm[sửa | sửa mã nguồn]

Cú pháp[sửa | sửa mã nguồn]

Một hàm C phải bao gồm một kiểu trả về (kiểu đó trả về

void

nếu không có giá trị trả về), một tên xác định, một danh sách các tham số để trong ngoặc đơn (nếu danh sách này không có tham số nào thì ghi là

void

bên trong dấu ngoặc), sau đó là khối các câu lệnh (hay khối mã) và/hay các câu lệnh

return

. (Nếu kiểu trả về là

void

thì mệnh đề này không bắt buộc phải có. Ngược lại, cũng không bắt buộc chỉ có một câu lệnh

return

mà tùy theo kỹ thuật, người lập trình có thể dẫn dòng mã sao cho mọi hướng chẻ nhánh đều được trả về đúng kiểu.)


tên_hàm(

) {

return

; }



Trong đó, của biến thì được khai báo như là kiểu dữ liệu và tách rời nhau bởi dấu phẩy :


var1, var2,…, varN;

Toàn bộ danh sách này được đặt trong ngoặc đơn ngay sau tên_hàm.

Thí dụ[sửa | sửa mã nguồn]

Hàm

add

tính tổng hai số có kiểu integer, hàm

abs

tính trị tuyệt đối của số có kiểu integer, và chương trình (hàm main) hiển thị hai dòng

1 + 1 = 2



absolute value of -2 is 2

#include

; //Chú giải: dòng này khai báo thư viện là stdio.h int add(int x, int y) { return x + y; } int abs(int x) { if (x > 0) return x; if (x < 0) return -x; if (x == 0) return 0; /* đây chỉ là thí dụ cho thấy C có khả năng dùng nhiều hơn 1 câu lệnh

return

hoàn toàn có thể dùng các câu lệnh khác đơn giản hon. */ } int main(void) { { int z; int y; printf(“nhap z:”); scanf(“%d”,&z); printf(“nhap y:”); scanf(“%d”,&y); printf(“%d + %d = %d\n”, z, y,add(z, y)); printf (“gia tri tuyet doi cua %d la %d”, y, abs(y)); } }

Chú ý: phần mã trên đã được thử thành công dùng trình dịch GNU (cho ANSI C và C99)

Mô tả[sửa | sửa mã nguồn]

Trong các câu lệnh tiền xử lý, ở cấp độ cao nhất, một chương trình ngôn ngữ C luôn có một chuỗi các khai báo cho các tập tin bao gồm.

Sau đó là các khai báo của phạm vi tập tin. Các khai báo này giới thiệu các hàm, các biến và các kiểu biến. Các hàm trong C nhìn tương tự với các chương trình con của Fortran hay các thủ tục của Pascal. Định nghĩa của hàm xuất hiện trong phần thân của nó (phần giữa bộ dấu ngoặc và theo sau nguyên dạng của hàm).

Các chương trình trong C để tạo các ứng dụng trực tiếp đều cần phải có một hàm đặc biệt tên là

main

, đây sẽ là hàm đầu tiên được gọi khi chương trình bắt đầu thực thi. Sau đây là một chương trình đầy đủ mặc dù không có mấy ứng đụng thiết thực.

int main (void) { return 0; }

Hàm code>main thường gọi các hàm khác để giúp nó hoàn tất công việc (tuỳ theo sự lập trình của người dùng).

Trọng một số trường hợp C được dùng không phải để tạo ra các ứng dụng trực tiếp mà để dùng với hệ điều hành hay các nơi khác (như là phát triển các bộ điều vận, các phần sụn, hay các thư viện…). Những trường hợp như vậy thì người lập trình hoàn toàn tự do trong việc giải quyết làm sao để xử lý khởi động chương trình, đặc biệt nó sẽ không cần định nghĩa hàm

main

.

Các hàm có thể được viết ra bởi người lập trình hay được cung cấp sẵn bởi các thư viện. Các thư viện cần được khai báo (sử dụng) bằng cách nêu tên các tập tin tiêu dề trong câu lệnh dạng

#include tập tin tiêu đề

. Một số hàm thư viện như là

printf

đã được định nghĩa bởi chuẩn C, chúng được tham chiếu như là các hàm thư viện chuẩn.

Một hàm có thể trả về một giá trị cho môi trường gọi nó. Khi hàm

main

trả về giá trị 0 chỉ dấu cho rằng toàn bộ chương trình đã hoàn tất thành công và kết thúc. Hàm

printf

cùng có giá trị trả về, đó là số lượng ký tự đã hiển thị, nhưng giá trị này thường bị bỏ qua không dùng.

Truyền các biến[sửa | sửa mã nguồn]

Các biến trong C được truyền qua các hàm bằng giá trị trong khi nhiều ngôn ngữ khác lại được truyền bằng tham chiếu (hay bằng địa chỉ). Điều này có nghĩa là hàm chỉ chép lại các giá trị và không thể thay đổi các giá trị đó của các biến (hay đối số) đưa vào. Để có thể thay đổi được giá trị của các biến truyền vào, người lập trình có thể truyền địa chỉ của nó vào hàm và tham chiếu ngược nó trong hàm được dùng (xem thêm kiểu tham chiếu)

void incInt(int *x) { (*x)++; // tăng giá trị của x trong

main

1 đơn vị } int main(void) { int x = 0; incInt(&x); // chuyển một tham chiếu vào incInt cho 'x' return 0; }

Để có thể chuyển một con trỏ (mà có thể cần đổi địa chỉ nó chỉ đến), có thể chuyển một tham chiếu cho con trỏ (tham chiếu này chỉ đến địa chỉ của con trỏ):

void setInt(int **p, int n) { *p = (int *) malloc(sizeof(int)); // đăng ký một vùng nhớ *p = n; // cài giá trị vào } int main(void) { int *p; //khai báo một con trỏ kiểu integer setInt(&p, 42); // chuyển giá trị của ‘p’ vào. return 0; }


int **p

sẽ định nghĩa một con trỏ chỉ đến con trỏ (thay vì chỉ đến các kiểu dữ liệu thông thường) tức là chỉ đến địa chỉ của con trỏ .

Hàm scanf làm việc theo cùng một cách thức:

int x; scanf(“%d”, &x);

Keywords searched by users: c# full stack developer

See more here: kientrucannam.vn

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *