Tiếp tục mã C Các hàm Cú pháp 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ế
Trang 1Cú pháp ngôn ngữ (lập trình) C
là tập hợp các qui 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)
Các hàm
Cú pháp
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.)
<kiểu_trả_về> tên_hàm(<danh sách tham số>)
{
<các_câu_lệnh>
return <biến (hay giá trị) có kiểu là kiểu_trả_về>;
}
Trong đó, <danh sách tham số> của N 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 ,:
<kiểu_dữ_liệu> var1, var2, , varN ;
Toàn bộ danh sách này được đặt trong ngoặc đơn ngay sau tên_hàm
Trang 2Thí dụ
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 và absolute value of -2 is 2
#include <stdio.h>; //Chú giải: dòng này khai báo tập tin bao gồm 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
<code>return</code>
hoàn toàn có thể dùng các câu lệnh khác đơn giản hơn */
}
void main(void)
{
int z = 1;
int y = -2;
printf("%d + 1 = %d\n", z, add(z, 1));
printf ("absolute value of %d is %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)
Hàm chủ main có kiểu trả về là void nên không cần câu lệnh return
Mô tả
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)
Trang 3Cá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
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 *y)
{
(*y)++; // tăng giá trị của x trong <code>main<code> 1 đơn vị
}
void main(void)
{
int x = 0;
incInt(&x); // chuyển một tham chiếu vào incInt cho 'x'
}
Trang 4Để 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 điạ 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
}
void 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
}
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ỏ p
Hàm scanf làm việc theo cùng một cách thức:
int x;
scanf("%d", &x);
Các cấu trúc dòng điều khiể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
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
{ <danh sách khai báo tùy chọn> <đanh sách câu lệnh tùy chọn> }
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
Trang 5Các mệnh đề biểu thức
Một câu lệnh (hay một mệnh đề) của C có dạng:
<biểu thức tùy chọn>;
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)
Có 3 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 :
if (<biểu thức>)
<mệnh đề1>
if (<biểu thức>)
<mệnh đề1>
else
<mệnh đề2>
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 <mệnh đề1> Nếu trong câu lệnh if có thêm từ khóa else thì <mệnh đề2> sẽ được thực thi một khi <biểu thức> 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 - case
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
Trang 6Lư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 <biểu thức X> có giá trị bằng <hằng H2> thì mệnh đề các biểu thức <mệnh đề M2>,<mệnh đề M3>, và <mệnh đề Mdefault> 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 (<biểu thức X>)
{
case <hằng H 1 > :
<mệnh đề M 1 >
case <hằng H 2 > :
<mệnh đề M 2 >
break;
case <hằng H 3 > :
<mệnh đề M 3 >
default :
<mệnh đề M default >
}
Trang 7Các mệnh đề tái lặp (hay vòng lặp)
C có 3 dạng câu lệnh vòng lặp:
Vòng lặp do
do
<mệnh đề>
while (<biểu thức>);
Trong mệnh đề này thì mệnh đề được thực thi lặp lại cho tới khi nào <biểu thức> được đánh giá (hay có giá trị) là true Một khi <biểu thức> 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
while (<biểu thức>)
<mệnh đề>
<mệnh đề> chỉ được thực thi hay thực thi lặp lại khi <biểu thức> có giá trị là true Nếu <biểu thức> có giá trị false thì câu lệnh sẽ bị kết thúc ngay lập tức
lặp for
Dạng C89 của vòng lặp for là:
for (<biểu thức 1> ; <biểu thức 2> ; <biểu thức 3>)
<câu lệnh>
Nó đã được tổng quát hóa trong C99 thành:
for (<khai báo> <biểu thức 1> ; <biểu thức 2>)
<câu lệnh>
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
Trang 8Thí 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)
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
câu lệnh goto sẽ có dạng:
goto <nhãn>;
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
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: ;
}
Trang 9Mệ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
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ác phép toá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
sự lựa chọn phần tử, nhận dạng
sự lựa chọn phần tử, con trỏ
từ trái sang phải
++ và
+ và
-! và ~
(cast)
*
&
sizeof
tiền tố tăng/giảm dấu dương/âm phép toán Bool NOT/phần bù 0 kiểu bit đổi kiểu
tham chiếu ngược tham chiếu
độ lớn
từ phải sang trái
*, /, và % nhân/chia/mô dun
từ trái sang phải
+ và - cộng/trừ
<< và >> phép toán bit <ocde>left shift/right shift
< và <=
> và >=
quan hệ nhỏ hơn/nhỏ hơn hay bằng quan hệ lớn hơn/lớn hơn hay bằng
== và != bằng với/khác với
& phép toán bit AND
^ phép toán bít XOR
Trang 10| phép toán bit OR
&& phép toán bool AND
|| phép toán bool OR
?: điều kiện tam phân
từ phải sang trái
=
+= và -=
*=, /=, và %=
<<= và >>=
&=, ^=, va |=
phép gán giá trị trực tiếp phép gán giá trị cộng thêm/trừ bớt phép gán giá trị nhân/chia/mô dul bởi phép gán bit shift
phép gán bit AND/XOR/OR
Nguồn: C Operator Precedence and Associativity
Khai báo dữ liệu
Các kiểu dữ liệu cơ bả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ớ
Kiểu nguyên viết dưới dạng 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, long và 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 và 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
Trang 11có 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ị
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
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
Bản hằng số xác định các biên của những kiểu nguyên thông thường
Đặc tả hiểu ngầm Đặc tả viết rõ ra Giá trị nhỏ nhất Giá trị lớn nhất
không viết gì hết, signed, hay
long long1 signed long long int1 LLONG_MIN2 LLONG_MAX2 unsigned long long1 unsigned long long int1 0 ULLONG_MAX2
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
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ó