C (ngôn ngữ lập trình) – Wikipedia tiếng Việt

Ngôn ngữ lập trình 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.

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

C là một ngôn từ lập trình tương đối nhỏ gọn quản lý và vận hành gần với phần cứng và nó giống với ngôn từ Assembler hơn hầu hết những ngôn từ bậc cao. Hơn thế, C đôi lúc được nhìn nhận như là ” có năng lực di động “, cho thấy sự khác nhau quan trọng giữa nó với ngôn từ bậc thấp như là Assembler, đó là việc mã C hoàn toàn có thể được dịch và thi hành trong hầu hết những máy tính, hơn hẳn những ngôn từ hiện tại trong khi đó thì Assembler chỉ hoàn toàn có thể chạy trong một số ít máy tính đặc biệt quan trọng. Vì nguyên do này C được xem là ngôn từ bậc trung .C đã được tạo ra với một tiềm năng là làm cho nó thuận tiện để viết những 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ộn bề với những đặc tả phức tạp của ngôn từ. Cuối cùng C có thêm những công dụng sau :

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:

Mặc dù C còn thiếu nhiều tính năng hữu dụng nhưng nguyên do quan trọng để C được gật đầu vì nó được cho phép những trình dịch mới được tạo ra một cách nhanh gọn trên những nền tảng mới và vì nó được cho phép người lập trình dễ trấn áp đượ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 những ngôn từ khác. Thường thì chỉ có ngôn từ ASM chỉnh bằng tay chạy nhanh hơn ( ngôn từ C ), do tại ASM trấn áp được toàn bộ máy. Mặc dù vậy, với sự tăng trưởng những trình dịch C, và với sự phức tạp của những CPU văn minh có vận tốc cao, C đã dần thu nhỏ độc lạ về vận tốc này .Một nguyên do nữa cho việc C được sử dụng thoáng đãng và hiệu suất cao là do những trình dịch, những thư viện và những ứng dụng thông dịch của những ngôn từ bậc cao khác lại thường được tạo nên từ C .
Ví dụ đơn thuần sau đây được thấy trong lần in tiên phong 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 những 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 hiển thị. Mặc dù vậy, nó hoàn toà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 thông tư vào đâu khi chương trình này được thực thi .

# include

intmain(void) { printf(" hello, world ! ") ; return0; }

Chương trình trên sẽ dịch đúng trong hầu hết những trình dịch tương hỗ chuẩn ANSI C hay chuẩn C99 .Sau đây là những nghiên cứu và 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.h cho 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.Dòng tiên phong này là một thông tư tiền giải quyết và xử lý. Điều này sẽ làm cho bộ tiền giải quyết và xử lý ( bộ tiền giải quyết và xử lý này là một công cụ để kiểm tra mã nguồn trước khi nó được dịch ) triển khai thay dòng lệnh đó bởi hàng loạt những dòng mã hay thực thể trong tập tin mà nó đề cập đến ( tức là tập tin ). Dấu ngoặc nhọn bao quanhcho biết rằng tập tin này hoàn toàn có thể tìm thấy trong những nơi đã định trước cho bộ tiền giải quyết và xử lý biết trải qua cácđến những tập tin. Tập hợp những tập tin được khai báo sử dụng qua những thông tư tiền giải quyết và xử lý còn được gọi là

intmain(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ã int chỉ 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 main sẽ không cần đến tham số để gọi nó. Xem thêm Dòng trên bộc lộ một hàm chuẩn tên. Hàm này có mục tiêu đặc biệt quan trọng trong C. Khi chương trình thi hành thì hàmđược gọi thứ nhất. Phần mãchỉ ra rằngcủa hàm ( tức là giá trị màsẽ được trả về sau khi thực thi ) sẽ có kiểu là một số nguyên. Còn phần mãcho biết rằng hàmsẽ 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ẽ).

return0;

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ó 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 ứng những kiểu mảng đa chiều. Các giá trị chỉ số của những mảng đa chiều thì được gán theo thứ tự hàng chính. Một cách có ý nghĩa thì những mảng này hoạt động giải trí như là mảng của những mảng nhưng thực ra chúng được phân bổ như là mảng một chiều với việc tính và tạo những 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ữ tài liệu[sửa|sửa mã nguồn]

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

  1. Sự cấp phép vùng nhớ tĩnh : khoảng chừng trống dành cho đối tượng người dùng thì được phân phối trong phần mã nhị phân ở thời hạn dịch ; những đối tượng người dùng này có một thời hạn sống vĩnh viễn theo sự sống sót của phần mã nhị phân chứa chúng ( những đối tượng người dùng ) .
  2. 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.Sự cấp phép vùng nhớ tự động hóa : Các đối tượng người tiêu dùng trong thời điểm tạm thời hoàn toàn có thể được chứa trong một chồng ( ), và khoảng trống này thì được trả về một cách tự động hóa và hoàn toàn có thể được dùng lại sau khi khối mã mà chúng ( tức những đối tượng người dùng trong thời điểm tạm thời ) được khai báo đã thực thi xong .
  3. malloc(), realloc()free() từ một khu vực của bộ nhớ có tên là free() để hoàn trả chúng lại cho bộ nhớ.Sự cấp phép vùng nhớ động : Các khối của bộ nhớ với bất kỳ cỡ lớn mong ước nào đều hoàn toàn có thể được nhu yếu ( hay xin ) trong thời hạn thi hành bằng cách dùng những hàm thư viện như làvàtừ một khu vực của bộ nhớ có tên là heap ; những khối này hoàn toàn có thể được tái dụng sau khi gọi hàmđể hoàn trả chúng lại cho bộ nhớ .

Ba giải pháp này thích hợp cho những trường hợp khác nhau và có những hậu quả khác nhau. Ví dụ, kiểu cấp phép tĩnh sẽ không cần thời hạn ( để thống kê giám sát ) cho sự cấp phép, kiểu cấp phép tự động hóa sẽ cần một khoảng chừng thời hạn nào đó cho dự trù, và kiểu cấp phát động hoàn toàn có thể yên cầu một lượng lớn thời hạn dùng dễ tính toán cho việc cấp phép và hoàn trả ( những vùng nhớ đã được nhu yếu trước đó ). Mặt khác, khoảng trống của chồng thường số lượ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ép vùng nhớ động là được cho phép sự cấp phép cho những đối tượng người dùng mà size của nó chỉ hoàn toàn có thể biết được trong lúc thi hành. Hầu hết những chương trình C đều dùng nhiều cả ba phương cách này .Khi hoàn toàn có thể thì sự cấp phép tự động hóa hay sự cấp phép tĩnh thường được dề nghị dùng vì kho nhớ được quản trị 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ép và hoàn trả những vùng nhớ bằng tay. Rất tiếc nhiều cấu trúc tài liệu hoàn toàn có thể trương nở trong thời hạn thực thi và vì kiểu cấp phép tĩnh và kiểu tự động hóa phải có một độ lớn cố định và thắt chặt ở thời hạn dịch nên trong nhiều trường hợp mà buộc phải dùng kiểu cấp phát động. Các dãy biến hóa về độ lớn là một ví dụ nổi bật của trường hợp này. ( Xem ví dụ từ bài malloc về những dãy được cấp phép vùng nhớ động. )

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à charint 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 {} để 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, whilefor. 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.

Các yếu tố 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ép vùng nhớ[sửa|sửa mã nguồn]

Một yếu tố với C ( và đây thường là yếu tố lớn cho những người mới làm quen với C ) là việc cấp phép ( vùng nhớ ) một cách tự động hóa hay một cách động cho những đốì tượng mà không khởi động chúng. Các đối tượng người dùng này, khởi đầu, chứa những giá trị bất kể trong khoảng chừng nhớ mà chúng được cấp phép. Các giá trị này hoàn toàn có thể là những giá trị ngẫu nhiên còn lại trong bộ nhớ mà chưa được làm sạch, chúng trọn vẹn không Dự kiến được. Nếu một chương trình có khai biến mà lại không gán giá trị khởi đầ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ì hoàn toàn có thể gây ra những phản ứng không lường trước được của chương trình đó. Hầu hết những trình dịch C văn minh hoàn toàn 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 trọn vẹn hiệu suất cao .

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.

Mặc dù C có tương hỗ riêng cho những mảng tĩnh, nhưng nó không kiểm tra xem những chỉ số mảng có hợp lệ hay không ( kiểm tra biên ). Thí dụ, người ta hoàn toàn có thể viết thành phần thứ sáu của một mảng được định nghĩa với 5 thành phần, và điều này hoàn toàn có thể mang lại những hậu quả không mong ước. 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 bảo mật an ninh trong những chương trình viết bằng C. Mặt khác, do sự số lượng giới hạn về kỹ thuật kiểm tra biên ở thời gian C sinh ra ( 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 tác động ảnh hưởng nặng đến vận tốc thực thi, đặc biệt quan trọng là trong những 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.

C có nhiều điểm yếu trong cú pháp. Đáng quan tâm 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ơn &| 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.

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

Các yếu tố về bảo dưỡng[sửa|sửa mã nguồn]

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

  • 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 những lỗi. Việc kiểm tra và truy thuế kiểm toán mã nguồn tự động hóa thì rất hiệu suất cao trong mọi ngôn từ. Chẳng hạn cho C là Lint. Một thực tiễn là sử dụng Lint để phát hiện những 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ời bởi một trình dịch C. Cũng có một thư viện cho việc triển khai kiểm tra những biên của mảng và một dạng số lượng giới hạn của việc tự động hóa 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à những công cụ này không phải là vạn năng. Bởi vì sự linh động của C, nhiều kiểu lỗi như thể việc dùng sai những hàm tham lượng động, việc dùng chỉ số ngoài biên của mảng và quản trị bộ nhớ không đúng thì không hề phát hiện được. Mặc dù vậy, nhiều trường hợp ( lỗi ) thường thì đều hoàn toàn có thể được nhận ra .

Những tăng trưởng khởi đầ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 hạn phát minh 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 từ trước đó là B .Thêm vào đó, những điểm khác với ngôn từ nguyên thủy ” B ” : Ken Thompson kể tới ngôn từ lập trình BCPL, nhưng ông ta cũng đã tạo ra ngôn từ là Bon để vinh danh vợ mình .Có nhiều truyền thuyết thần thoại về nguồn gốc của C và hệ quản lý và điều hành tương quan tới nó là Unix gồm có :

  • 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 Travel Lưu trữ 2012-04-29 tại WebCite. 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 những máy PDP-11 / 20. Đây là lần tiên phong mà nhân của một hệ quản lý và điều hành được lắp thành bằng một ngôn từ khác hơn Assembly .

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 trình làng những công dụ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 =+ 10i = +10).

K&R C thường được xem là phần cơ bản nhất của ngôn từ 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 trình làng, nó đã được xem như thể ” 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 năng lực di dời ( tái dụng trên nhiều máy ) chính do không phải mọi trình dịch đều tương hỗ hàng loạt ANSI C, và một cách hài hòa và hợp lý là mã viết trong K&R C cũng là mã hợp lệ trong ANSI C .Trong những phiên bản trước kia của C, chỉ có những hàm nào trả về 1 số ít 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ề 1 số ít nguyên .Ví dụ việc gọi với nhu yếu của sự khai báo trước :

longintSomeFunction( ) ;

intCallingFunction

()

{ longintret; ret=SomeFunction( ) ; }

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

intCallingFunction( )
{
intret;
ret=SomeOtherFunction( ) ;
}

intSomeOtherFunction( )
{
return0;
}

Bởi vì nguyên mẫu của K&R đã không gồm có bất kỳ thông tin nào về những tham số của hàm, công dụng kiểm tra kiểu của những đối số đã không được thực thi, mặc dầu một số ít trình dịch sẽ cho ra thông tin 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 tính năng ” không chính thức ” đã được thêm vào cho ngôn từ, được tương hỗ bởi những trình dịch của AT&T và 1 số ít nơi khác. Trong đó gồm có :

  • 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 chừng cuối thập niên 1970, C mở màn thay thế sửa chữa vai trò của BASIC như là một ngôn từ lập trình cho microcomputer. Suốt thập niên 1980 nó đã được chấp thuận đồng ý dùng trong IBM PC, và sự thông dụng của nó khởi đầ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 những cấu trúc ngôn từ lập trình hướng đối tượng người dùng .Ngôn ngữ họ tạo ra gọi là C + + nay trở thành ngôn từ lập trình ứng dụng phổ cập nhất trên hệ điều hành quản lý Microsoft Windows ; C vẫn còn rất phổ cập trong quốc tế UNIX. Một ngôn từ khác cũng được tăng trưởng trong khoảng chừng thời hạn này là Objective-C, cũng là một lan rộng ra lập trình hướng đối tượng người dùng cho C. Dù không phổ cập như C + +, nó được dùng để tăng trưởng những ứng dụng Cocoa của Mac OS X .Trong 1983, Viện Tiêu chuẩn Quốc gia Hoa Kỳ ( ANSI ) xây dựng hội đồng X3J11 để hoàn tất một tiêu chuẩn dặc tả của C. Sau một quy trình khó khăn vất vả và lâu bền hơn, 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 từ 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 cụ thể nhỏ được kiểm soát và đ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 quy 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ó đảm nhiệm nhiều tính năng không chính thức của K&R C như thể một hệ quả. Xa hơn, hội đồng tiêu chuẩn cũng làm cho ANSI C gồm có thêm nhiều công dụng mới, như thể những nguyên mẫu của hàm ( mượn từ C + + ), và năng lực tiền giải quyết và 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__
externintgetopt(int,char*const*,constchar*) ;
# else
externintgetopt( ) ;
# 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).

Sau quy trình chuẩn hóa ANSI, đặc tả của ngôn từ C tương đối được giữ nguyên trong một thời hạn, trong khi C + + liên tục xâ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 ca tụng là ” C99 “. Nó đã tiếp thu ANSI C trong tháng 3 năm 2000 .Những công dụng mới trong C99 gồm có :

  • 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 mê hoặc trong việc tương hỗ cho chuẩn C99 là một hiệu quả trộn lẫn. Trong khi GCC và nhiều trình dịch khác hiện tương hỗ hầu hết những công dụ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 có vẻ như không thú vị để thêm vào những tương hỗ này .

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

[1] Phiên bản cuối cùng C99 đã tạo ra thêm nhiều tính năng xung đột (giữa C và C++). Các sự khác nhau này tạo ra khó khăn để viết các chương trình và thư viện đẻ có thể được dịch và hoạt động chính xác trong cả hai loại mã C hay C++, đồng thời gây nhầm lẫn cho những người lập trình dùng cả hai ngôn ngữ này. Sự chênh lệch này cũng gây khó khăn cho ngôn ngữ này có thể tiếp thu các tính năng của ngôn ngữ kia.C++ nguyên là sự kế thừa từ C. Mặc dù vậy, không phải mọi chương trình trong C đều hợp lệ trong C++. Vì là hai ngôn ngữ độc lập, số lượng không tương thích giữa hai ngôn ngữ này đã tăng lên.Phiên bản cuối cùng C99 đã tạo ra thêm nhiều tính năng xung đột (giữa C và C++). Các sự khác nhau này tạo ra khó khăn để viết các chương trình và thư viện đẻ có thể được dịch và hoạt động chính xác trong cả hai loại mã C hay C++, đồng thời gây nhầm lẫn cho những người lập trình dùng cả hai ngôn ngữ này. Sự chênh lệch này cũng gây khó khăn cho ngôn ngữ này có thể tiếp thu các tính năng của ngôn ngữ kia.Bjarne Stroustrup, cha đẻ của C + + đã lập đi lập lại rằng : [ 2 ] Các đặc thù không thích hợp giữa C và C + + nên được hạ thấp càng nhiều càng tốt để lan rộng ra tối đa năng lực hoạt động giải trí thông suốt của hai ngôn từ này. Một số người tranh biện rằng vì C và C + + là hai ngôn từ khác nhau, sự thích hợp giữ chúng thì có í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 thích hợp không được tàn phá cố gắng nỗ lực để nâng cao mỗi ngôn từ đứng riêng .Ngày nay, những khác nhau cơ bản, không kể những lan rộng ra thêm vào của C + + như là những lớp, những tiêu bản, những khoảng trống tên, và quá tải, giữa hai ngôn từ 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 tính năng mà Open tiên phong 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.

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

C được dùng như thể một ngôn từ trung gian vì nó hoàn toàn có thể xuất thành dạng tập tin object hay ngôn từ máy. Việc này giúp C trở nên dễ luâ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à những hệ điều hành quản lý 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 đó, những đầu ra của mã nguồn C đùng một cái trở nên rất là dễ luân chuyển, và có năng lực dùng trong dạng *. obj hay mã máy được tối ưu hóa. Dầu sao thì C được phong cách thiết kế như thể một ngôn từ lập trình, nó không phải là lý tưởng cho việc dùng như thể một ngôn từ trung gian. Điều này dẫn tới việc tăng trưởng những ngôn từ trung gian lấy C làm cơ sở, như thể một 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à list một số ít trình dịch thông dụng :

  • 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,…

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

Source: https://dvn.com.vn
Category: Hỏi Đáp

Alternate Text Gọi ngay