0.2. Hàm số trong ngôn ngữ C

Estimated reading: 35 minutes 285 views

Tiếp theo, chúng ta sẽ xem xét hàm (function), vốn là chủ thể xử lý của chương trình C.
Nói chung, trong các doanh nghiệp lớn, một phần công việc sẽ được giao cho các công ty thầu phụ quy mô trung bình; các công ty thầu phụ này lại tiếp tục giao một phần công việc cho các công ty thầu phụ nhỏ hơn. Lý do là vì những công việc phức tạp nếu được chia nhỏ và giao từng phần cho các chuyên gia thì sẽ được xử lý một cách hiệu quả hơn.

Tương tự như vậy, trong ngôn ngữ C, các hàm sẽ giao công việc cho những hàm khác (đóng vai trò như các “nhà thầu phụ”), nhận lại kết quả từ chúng, và từ đó chương trình được thực thi. Việc giao công việc cho một hàm khác được gọi là “gọi hàm (call)”. Nói cách khác, ngôn ngữ C hoạt động thông qua chuỗi gọi hàm (function call chain).

Trong một chương trình C, có một hàm đặc biệt mang tên main, đây là hàm được thực thi đầu tiên. Từ bên trong hàm main, chương trình sẽ tiếp tục gọi các hàm khác.

Định nghĩa hàm

Cơ chế của hàm

Các hàm (function) trong ngôn ngữ C được xây dựng theo mô hình của hàm toán học (mathematical function).

Trong toán học, ví dụ, một hàm bậc hai (quadratic function) có nội dung “lấy giá trị bình phương rồi cộng thêm 1” có thể được định nghĩa như sau:

f(x) = x² + 1

Khi giá trị của biến x là 3.0, giá trị của hàm này được biểu diễn bằng:

f(3.0)

Giá trị thực tế của biểu thức trên là kết quả của việc thay x bằng 3.0 vào công thức toán học và thực hiện phép tính:

3.0² + 1 = 10.0

Ở đây, ftên hàm (function name), còn x là một biến đặc biệt gọi là tham số (hình thức) (仮引数, parameter). Tham số là biến dùng để nhận dữ liệu đầu vào (input) của hàm. Giá trị thực tế được truyền vào hàm được gọi là đối số (thực) (実引数, argument), và khi gọi hàm thì giá trị này sẽ được gán cho tham số. Ví dụ, khi viết:

f(3.0)

thì đối số (argument)3.0, và giá trị 3.0 này sẽ được gán cho tham số (parameter)x. Phần biểu thức “x² + 1” chính là nội dung xác định hành vi của hàm (function body theo nghĩa toán học), và biểu thức này được mô tả bằng cách sử dụng tham số.

Dựa theo mô tả này, phép tính sẽ được thực hiện bằng cách sử dụng giá trị đã được gán cho tham số. Sau đó, giá trị được tính toán sẽ được xuất ra (output) như là giá trị của hàm. Lưu ý rằng, trong ngôn ngữ C, giá trị của hàm còn được gọi là giá trị mà hàm trả về, và thường được gọi là giá trị trả về (return value, 返却値 / 返値).

Cách định nghĩa hàm

Trong ngôn ngữ C, hàm được định nghĩa như bên dưới. Phần đầu tiên,

Kiểu_giá_trị_trả_về Tên_hàm(Danh_sách_tham_số)

được gọi là “phần đầu của định nghĩa hàm” (関数定義の頭部, function header), và biểu thị khái quát đặc tả của hàm. Danh sách tham số là một dãy các khai báo tham số, trong đó mỗi tham số có dạng

Kiểu_tham_số Tên_tham_số 

và các tham số được liệt kê, ngăn cách bằng dấu phẩy (,).

Sau phần đầu của định nghĩa hàm là phần được bao bởi { } (được gọi là “thân định nghĩa hàm” (関数定義の本体, function body)), trong đó mô tả xử lý thực tế.
Phần bên trong { } có thể được chia thành hai phần. Phần mở đầu được gọi là “phần khai báo” (宣言部, declaration part), nơi thực hiện việc định nghĩa kiểu dữ liệu, khai báo biến và khai báo hằng const. Các biến được khai báo trong danh sách tham số hình thức và trong phần khai báo của thân hàm chỉ có thể sử dụng bên trong hàm đó, vì vậy chúng được gọi là biến cục bộ (ローカル変数, local variable). Các kiểu dữ liệu được định nghĩa trong phần khai báo này cũng tương tự, chỉ có thể sử dụng bên trong hàm đó. Ngoài ra, cần lưu ý rằng trong phần khai báo này không thể định nghĩa hàm (tức là không thể định nghĩa thêm một hàm bên trong hàm).

Phần còn lại của thân định nghĩa hàm được gọi là “phần thực thi” (実行部, execution part), trong đó mô tả các xử lý thực tế bằng cách sử dụng các biến đã được khai báo ở phần khai báo. Trong phần thực thi này, bằng cách sử dụng một câu lệnh đặc biệt gọi là câu lệnh return, ta trả về giá trị trả về của hàm theo dạng:

return Giá_trị_trả_về;

Trong hình dưới, câu lệnh return được viết ở phần cuối của phần thực thi, nhưng trên thực tế, có thể viết câu lệnh return ở bất kỳ vị trí nào trong phần thực thi. Trong trường hợp đó, khi câu lệnh return được thực thi, xử lý của hàm sẽ kết thúc ngay tại thời điểm đó.

Ví dụ, một hàm tính giá trị trung bình có thể được định nghĩa như sau:

double average(double x, double y)
{
           double temp = 0.0;
           temp = (x + y) / 2.0;
           return temp;
}

Khi muốn gọi hàm này, ta viết trong một biểu thức, truyền các đối số (実引数, argument) như sau: average(5.0, 10.0).
Ví dụ, xử lý “gán cho c giá trị bằng trung bình của a và b cộng thêm 5” có thể được viết như sau:

c = average(a, b) + 5;

Cách định nghĩa hàm

Kiểu_trả_về Tên_hàm ( danh_sách_tham_số )
{
           Phần_khai_báo
           Phần_thực_thi
           return giá_trị_trả_về;
}
double average ( double x, double y )
{
           double result;
           result = ( x + y ) / 2.0;
           return result;
}

※ Danh sách tham số là dãy các khai báo theo dạng, “Kiểu_tham_số Tên_tham_số ”, được phân cách bằng dấu phẩy (,)

※ Các tham số và các biến được khai báo bên trong hàm chỉ có hiệu lực
trong nội bộ của hàm đó, và được gọi là biến cục bộ (local variable)

※ Phần khai báo là nơi thực hiện việc định nghĩa kiểu và khai báo biến,
còn phần thực thi là nơi mô tả xử lý thực tế

※ Việc khai báo các biến cục bộ (ngoài các tham số) phải được thực hiện
ngay sau dấu {

※ Giá trị trả về được chỉ định bằng câu lệnh return

※ Kiểu giá trị trả về của hàm main() bắt buộc phải là int

Hàm không nhận tham số

Hàm không nhận tham số được khai báo bằng cách ghi void trong danh sách tham số.
Ngoài ra, hàm không trả về giá trị thì kiểu trả về được chỉ định là void.
Trong trường hợp đó, câu lệnh return không chỉ định giá trị trả về, mà được viết đơn lẻ như sau:

return;
Chi tiết:
Kiểu_trả_về Tên_hàm ( void )
{
           Phần_khai_báo
           Phần_thực_thi
           return giá_trị_trả_về;
}

{
           Phần_khai_báo
           Phần_thực_thi
           return;
}

Giá trị trả về của hàm  main

Kiểu giá trị trả về của các hàm do người dùng tự định nghĩa thì về cơ bản có thể được định nghĩa một cách tự do, nhưng riêng hàm main() thì có những quy định khác.

Hàm main() được quy định phải trả về kiểu int, và đối với giá trị trả về đó:

  • 0 là một giá trị đặc biệt biểu thị rằng “chương trình đã kết thúc bình thường (normal termination)”,
  • còn các giá trị khác 0 thì biểu thị rằng “chương trình đã kết thúc bất thường (abnormal termination)”.

Mô tả xử lý

Các loại câu lệnh

Trong phần thực thi của hàm, chương trình được mô tả bằng cách sắp xếp các câu lệnh (statement) theo thứ tự.
Các loại câu lệnh được trình bày trong Hình dưới.

Các loại câu lệnh (Types of statements / 文の種類)

  • Câu lệnh (Statement / 文)

    • Câu lệnh biểu thức (Expression statement / 式文)

    • Câu lệnh có nhãn (Labeled statement / ラベル付き文)

    • Câu lệnh phức (Compound statement / 複合文)

    • Câu lệnh điều khiển (Control statement / 制御文)

      • Câu lệnh lựa chọn (Selection statement / 選択文)

        • Lệnh if (if文)

        • Lệnh if-else (if-else文)

        • Lệnh switch (switch文)

      • Câu lệnh lặp (Iteration statement / 繰り返し文)

        • Lệnh while (while文)

        • Lệnh do (do文)

        • Lệnh for (for文)

      • Câu lệnh nhảy (Jump statement / ジャンプ文)

        • Lệnh return (return文)

        • Lệnh break (break文)

        • Lệnh continue (continue文)

        • Lệnh goto (goto文)

Câu lệnh biểu thức là loại câu lệnh đã được giải thích trong phần Khai báo biến và biểu thức toán tử.

Đây là dạng câu lệnh trong đó một biểu thức được theo sau bởi dấu chấm phẩy (;). Dạng tổng quát của câu lệnh này là:

biểu_thức;

Trong phần thực thi của hàm, để đánh dấu một câu lệnh cụ thể, ở đầu câu lệnh, 

Tên_nhãn: Câu_lệnh

như trên, bạn có thể gắn nhãn cho nó. Vì tên nhãn là một loại định danh, nên bạn có thể đặt bất kỳ tên nào ngoại trừ các từ khóa. Những câu lệnh như vậy được gọi là câu lệnh có nhãn.

Ngoài ra, có một phương pháp để gộp nhiều câu lệnh thành một câu lệnh duy nhất bằng cách bao quanh các câu lệnh đó bằng dấu {}. Các câu lệnh được bao quanh bởi {} được gọi là câu lệnh phức (compound statement) hoặc khối (block). Ví dụ:

{ a; a + b; 1 * c; }

là một khối gộp 3 câu lệnh thành 1. Phần đầu tiên của khối (tương tự như phần thân của định nghĩa hàm) là phần khai báo, nơi có thể định nghĩa các biến cục bộ hoặc định nghĩa kiểu dữ liệu. Ví dụ:

{ int a = 1; a++; }

trong trường hợp này, biến số nguyên a kiểu int được khai báo và khởi tạo giá trị là 1, sau đó giá trị của nó được tăng thêm 1.

Phạm vi của biến

Các biến cục bộ được khai báo, các kiểu được định nghĩa, hay các nhãn bên trong một khối không thể được tham chiếu từ bên ngoài khối đó. Tức là, các định danh mới được đưa vào bên trong khối sẽ không nhìn thấy được từ bên ngoài khối đó. Phạm vi hiệu lực của các định danh như vậy được gọi là phạm vi (scope).

Phạm vi của định danh được đưa vào trong khối được gọi là phạm vi khối (block scope). Định danh được đưa vào trong khối sẽ che khuất (name hiding) định danh có cùng tên được đưa vào ở bên ngoài nó. Tức là, trong trường hợp sau đây, biến a kiểu double sẽ “che khuất” biến a kiểu int ở bên ngoài.

				
					{
    int a = 0;
    {
        double a = 1.0;
        {
            ++a;
        }
        a++;
    }
    --a;
}				
			

Điều này có nghĩa là cả 2 biến a đang được tăng (increment) đều là kiểu double, còn biến a đang bị giảm (decrement) là kiểu int.

Biến cục bộ (local variable / 局所変数) được khai báo ở đầu khối sẽ được cấp phát vùng nhớ trên bộ nhớ ngay trước khi phần khối được thực thi, và nếu có chỉ định giá trị khởi tạo, nó sẽ được khởi tạo theo giá trị đó. Và khi việc thực thi phần khối kết thúc, vùng nhớ đó sẽ bị giải phóng (abandoned / 放棄). Vì vậy, nếu định lưu số lần hàm được gọi vào biến số nguyên i như sau:

				
					void f( void ) { int i = 0; i++; }				
			

thì dù làm vậy, i được sinh ra mỗi lần hàm được gọi và được khởi tạo về 0, nên dự tính này sẽ thất bại.

Tuy nhiên, bằng cách làm cho việc sinh ra và khởi tạo biến cục bộ chỉ diễn ra duy nhất 1 lần, ta có thể giải quyết vấn đề này. Phương pháp là thêm từ khóa dự trữ (reserved word / 予約語) static,

static Tên_kiểu Biến_cục_bộ;

và khai báo biến như vậy. Vì thế,

				
					void f( void ) { static int i = 0; i++; }				
			

nếu làm thế này, i chỉ được sinh ra và khởi tạo khi f() được gọi lần đầu tiên, nên sau khi gọi f() 30 lần, i sẽ mang giá trị 30.

Như vậy, biến cục bộ được khai báo với static được gọi là biến cục bộ tĩnh (static local variable / 静的局所変数). Ở đây, từ “tĩnh (static / 静的)” bao hàm sắc thái “tồn tại liên tục mãi”, trái ngược với việc biến cục bộ thông thường lặp đi lặp lại việc được sinh ra và bị giải phóng.

Giá trị chân lý (Truth value / 真理値)

Để thực hiện xử lý cao cấp trong chương trình, cần phải rẽ nhánh xử lý tùy theo điều kiện hoặc thực hiện xử lý lặp lại. Các câu lệnh thực hiện rẽ nhánh hay lặp lại là câu lệnh điều khiển (control statement / 制御文). Trước khi giải thích về câu lệnh điều khiển, trước tiên để biểu diễn cái gọi là “điều kiện”, hãy cùng giải thích về giá trị chân lý và phép toán logic (logical operation / 論理演算).

Giá trị chân lý là giá trị đúng (true / 真) hoặc sai (false / 偽), trong ngôn ngữ C nó được biểu diễn bằng số nguyên, giá trị số nguyên khác 0 được coi là đúng (true), giá trị số nguyên 0 được coi là sai (false).

Điều kiện chắc chắn nhận giá trị đúng hoặc sai được gọi là mệnh đề (proposition / 命題). Ví dụ:

“Số nguyên i nhỏ hơn 2”

vì nó nhận giá trị đúng hoặc sai, nên có thể nói đó là một mệnh đề. Trong ngôn ngữ C, các toán tử được chuẩn bị sẵn để so sánh mối quan hệ lớn nhỏ đối với các kiểu vô hướng (scalar type / スカラ型), hoặc kiểm tra xem có bằng hay không bằng, và kết quả tính toán bằng các toán tử này sẽ trả về giá trị chân lý.

Bảng toán tử quan hệ・đẳng thức・bất đẳng thức

Tên gọiKý hiệuVí dụÝ nghĩa
Toán tử quan hệ<a < bNếu a nhỏ hơn b là Đúng (True), nếu không phải là Sai (False)
Toán tử quan hệ<=a <= bNếu a nhỏ hơn hoặc bằng b là Đúng, nếu không phải là Sai
Toán tử quan hệ>=a >= bNếu a lớn hơn hoặc bằng b là Đúng, nếu không phải là Sai
Toán tử quan hệ>a > bNếu a lớn hơn b là Đúng, nếu không phải là Sai
Toán tử đẳng thức==a == bNếu a và b bằng nhau là Đúng, nếu không phải là Sai
Toán tử bất đẳng thức!=a != bNếu a và b không bằng nhau là Đúng, nếu không phải là Sai

Phép toán logic (Logical Operation / 論理演算)

Bằng cách kết hợp các mệnh đề với “và”, “hoặc”, “không phải ~”, ta có thể biểu diễn các mệnh đề phức tạp hơn. Giả sử A và B là các mệnh đề,

A VÀ B (A AND B)

sẽ là Đúng (True) khi cả A và B đều Đúng, và sẽ là Sai (False) khi không phải vậy.

A HOẶC B (A OR B)

sẽ là Đúng nếu ít nhất một trong hai A hoặc B là Đúng, và chỉ trở thành Sai khi cả A và B đều Sai.

KHÔNG PHẢI A (NOT A)

sẽ là Sai khi A là Đúng, và sẽ là Đúng khi A là Sai.

Như vậy, phép toán kết hợp các mệnh đề được gọi là phép toán logic (logical operation / 論理演算). Trong ngôn ngữ C, các toán tử thực hiện phép toán logic được cung cấp như trong bảng dưới, nhờ đó mệnh đề “(Số nguyên i lớn hơn hoặc bằng 0) VÀ (Số nguyên i nhỏ hơn hoặc bằng 5)” sẽ được viết là: 

( 0 <= i ) && ( i <= 5 )

khi đó nếu số i nằm trong khoảng từ 0 đến 5 thì kết quả biểu thức là Đúng, không phải vậy thì kết quả là Sai.

Bảng Toán tử logic (Logical Operators / 論理演算子)

Toán tử logicKý hiệuVí dụÝ nghĩa

Toán tử tích logic

(Logical AND / 論理積演算子)

(“~và~”)

&&A && BNếu cả A và B đều Đúng thì toàn bộ biểu thức là Đúng, nếu không thì là Sai

Toán tử tổng logic

(Logical OR / 論理和演算子)

(“~hoặc~”)

||A || BA hoặc B Đúng thì biểu thức đúng, không phải vậy thì là Sai

Toán tử phủ định logic

(Logical NOT / 論理否定演算子)

(“không phải ~”)

!!ANếu A là Đúng thì toàn bộ biểu thức là Sai, nếu A là Sai thì toàn bộ biểu thức là Đúng

※ A và B là các biểu thức có giá trị chân lý

※ Kiểu của toàn bộ biểu thức là kiểu int, giá trị sẽ là Đúng (1) hoặc Sai (0)

Dạng tổng quát của câu lệnh điều khiển (Control statement / 制御文)

Dạng tổng quát của câu lệnh điều khiển như sau. Biểu thức điều khiển (control expression / 制御式) là biểu thức dùng làm manh mối để phán đoán điều kiện.

Từ_khóa ( Biểu_thức_điều_khiển ) Câu_lệnh

Câu lệnh điều khiển được chia thành câu lệnh lựa chọn (selection statement / 選択文), câu lệnh lặp (iteration statement / 繰り返し文), và câu lệnh nhảy (jump statement / ジャンプ文). Câu lệnh lựa chọn là câu lệnh rẽ nhánh xử lý tùy theo giá trị của biểu thức điều khiển. Câu lệnh lặp là câu lệnh thực hiện xử lý lặp lại đúng như tên gọi, câu lệnh nhảy là câu lệnh bắt buộc rẽ nhánh xử lý.

Câu lệnh lựa chọn (if, if-else) (Selection statement / 選択文)

Tôi sẽ giải thích lần lượt từ câu lệnh lựa chọn. Câu lệnh if (if statement / if文) có thể nói là câu lệnh điều khiển cơ bản nhất. Chỉ khi giá trị của biểu thức điều khiển là Đúng (True), nó mới cho phép thực thi một câu lệnh cụ thể. Ví dụ, xử lý “khi giá trị số x khác 0, thì chia a và b cho x” được viết là:

if ( x != 0 ) { a / x; b / x; }

(Sơ đồ mô tả: Kiểm tra Biểu thức điều khiển. Nếu Đúng -> Thực hiện Câu lệnh 1. Nếu Sai -> Bỏ qua)

※ Chỉ thực thi Câu lệnh 1 khi giá trị của biểu thức điều khiển là Đúng, trường hợp là Sai thì không thực thi gì cả.

if ( Biểu_thức_điều_khiển ) Câu_lệnh_1

Để thực thi một xử lý nào đó khi điều kiện cụ thể là Đúng, và thực thi một xử lý khác khi là Sai, ta có thể sử dụng câu lệnh if-else (if-else statement / if-else文). Ví dụ, “hàm IsNegative() kiểm tra xem giá trị thực x có âm hay không” có thể được định nghĩa như sau: 

int IsNegative( double x )
{
    if ( x < 0.0 ) return 1;
    else return 0;
}

Nếu đặt thêm một câu lệnh if-else vào sau else, ta có thể thực hiện phân loại nhiều trường hợp loại trừ lẫn nhau (mutually exclusive / 排他的). Ví dụ, việc phân loại số dương・số 0・số âm là phân loại loại trừ lẫn nhau, nên “hàm Sign() trả về dấu của giá trị thực x” có thể được định nghĩa như sau:

				
					double Sign( double x )
{
    if ( x < 0.0 ) {
        return -1; // Trả về -1 nếu là số âm
    }else if ( 0.0 < x ) {
        return 1;  // Trả về 1 nếu là số dương
    }else return 0; // Trả về 0 nếu là số 0
}				
			

Câu lệnh lựa chọn (lệnh switch) (Selection statement / 選択文)

Lệnh switch (switch statement / switch文) là câu lệnh điều khiển kiểm tra giá trị số nguyên để rẽ nhánh sang nhiều xử lý khác nhau. Lệnh switch có dạng:

switch ( Biểu_thức_điều_khiển ) Câu_lệnh_phức

như trên, và bên trong câu lệnh phức (block {}),

case Giá_trị_nguyên:

ta có thể viết các nhãn đặc biệt có gắn giá trị số nguyên gọi là nhãn case (case label).

Biểu thức điều khiển của lệnh switch được so sánh lần lượt với giá trị nguyên của các nhãn case như một số nguyên. Trong trường hợp giá trị của biểu thức điều khiển trùng khớp với giá trị số nguyên, quá trình xử lý sẽ rẽ nhánh đến câu lệnh có gắn nhãn case tương ứng, và thực thi tuần tự các câu lệnh sau đó. Trong trường hợp đã so sánh với giá trị nguyên của tất cả các nhãn case mà vẫn không tìm thấy giá trị nào trùng khớp, thì quá trình xử lý sẽ rẽ nhánh đến câu lệnh có gắn nhãn default ở cuối (nhãn default này có thể lược bỏ).

				
					    // Dạng cơ bản
    switch(Biểu_thức_điều_khiển)
    {
        case Value 1:
            Xử lý 1;
        case Value 2:
            Xử lý 2;
        ...
        case Value n:
            Xử lý n;
        default:
            Xử lý default;
    }
    
    // Dạng có break
    switch(Biểu_thức_điều_khiển)
    {
        case Value 1:
            Xử lý 1;
        case Value 2:
            Xử lý 2;
            break;
        ...
        case Value n:
            Xử lý n;
        default:
            Xử lý default;
    }
    
    // Dạng có break hay dùng
    switch(Biểu_thức_điều_khiển)
    {
        case Value 1:
            Xử lý 1;
            break;
        case Value 2:
            Xử lý 2;
            break;
        ...
        case Value n:
            Xử lý n;
            break;
        default:
            Xử lý default;
    }				
			

  • Sơ đồ bên trái không có break: Kiểm tra xem biểu thức điều khiển có bằng với hằng số nguyên được chỉ định bởi nhãn case hay không, trường hợp bằng nhau thì thực thi tất cả các câu lệnh từ nhãn case đó trở về sau. Ở cuối cùng có thể viết một chuỗi câu lệnh được gắn nhãn bởi từ khóa dự trữ default, chuỗi câu lệnh này nhất định sẽ được thực thi (phần này có thể lược bỏ).

  • Sơ đồ bên phải có break: Nhờ câu lệnh break, có thể chuyển xử lý xuống cuối câu lệnh switch.

Vì vậy, đối với giá trị nguyên i, trong trường hợp như sau:

switch ( i )
{
    case 1 : f();
    case 2 : g();
    case 3 : h();
    default: d();
}

nếu i là 1 thì việc xử lý sẽ rẽ nhánh ngay sau nhãn “case 1:”, tức là ngay trước khi gọi hàm f(), và các hàm sẽ được gọi theo thứ tự:

f() → g() → h() → d()

Tương tự, nếu i là 3 thì việc xử lý sẽ rẽ nhánh ngay sau “case 3:”, và các hàm sẽ được gọi theo thứ tự:

h() → d()

Và, khi i không phải là 1, cũng không phải 2 hay 3, thì chỉ có hàm d() được gọi.

Giả sử, nếu bạn muốn thực hiện xử lý một cách loại trừ (mutually exclusive / 排他的) như “khi i là 1 thì chỉ gọi hàm f(), khi i là 2 thì chỉ gọi hàm g(), khi i là 3 thì chỉ gọi hàm h(), khi không phải các trường hợp trên thì gọi hàm d()“, thì ta sử dụng câu lệnh break (break statement / break文). Câu lệnh break là câu lệnh có hình thức:

break;

và khi câu lệnh break được thực thi bên trong câu lệnh phức của lệnh switch, việc xử lý của lệnh switch sẽ kết thúc ngay lập tức. Vì vậy, ví dụ trên có thể được viết lại như sau:

switch ( i ) {
    case 1 : f(); break;
    case 2 : g(); break;
    case 3 : h(); break;
    default: d();
}

Ví dụ, trong trường hợp i là 1, việc xử lý sẽ rẽ nhánh ngay sau “case 1:”, sau khi thực thi hàm f() xong thì câu lệnh break được thực thi, và lệnh switch này sẽ kết thúc tại đó.

Câu lệnh lặp (lệnh for) (Iteration statement / 繰り返し文 (for文))

Lệnh for (for statement / for文) có dạng như sau: 

for( Biểu_thức_1; Biểu_thức_điều_khiển; Biểu_thức_2 ) Câu_lệnh_1

Đầu tiên, nó thực thi Biểu thức 1 như là bước tiền xử lý, sau đó kiểm tra Biểu thức điều khiển, nếu giá trị đó là Đúng (True) thì thực thi Câu lệnh 1Biểu thức 2. Lệnh for rất tiện lợi trong trường hợp bạn muốn sử dụng chính số lần lặp trong quá trình xử lý.

Ví dụ, xử lý “gán 0 cho tất cả các phần tử của mảng kiểu số nguyên (số phần tử là 5)” đã được đưa ra làm ví dụ trong phần giải thích lệnh while, có thể được viết một cách ngắn gọn như sau:

{
    int a[5]; int i;
    for(i = 0; i < 5; i++) a[i] = 0;
}

Tại Biểu thức 1, ta khởi tạo biến dùng làm chỉ số i về 0 như một bước chuẩn bị trước, sau khi thực thi Câu lệnh 1, thì tại Biểu thức 2, ta tăng i lên (increment).

Trong lệnh for, Biểu thức 1, Biểu thức điều khiển, và Biểu thức 2 có thể được lược bỏ. Trong trường hợp Biểu thức điều khiển bị lược bỏ, giá trị đó được hiểu là Đúng (True), và vòng lặp sẽ “tiếp tục quay” (lặp vô hạn). Ngoài ra, lệnh for cũng có thể sử dụng lệnh break và lệnh continue để kiểm soát cách thức lặp.

Câu lệnh lặp (whiledo) (Iteration statement / 繰り返し文)

Câu lệnh lặp là câu lệnh thực hiện xử lý lặp lại (loop). Trong câu lệnh lặp bao gồm lệnh while (while statement / while文), lệnh do (do statement / do文), và lệnh for (for statement / for文).

Lệnh while là câu lệnh thực hiện lặp đi lặp lại một câu lệnh cụ thể trong khi giá trị của biểu thức điều khiển là Đúng (True). Ví dụ, xử lý “gán 0 cho tất cả các phần tử của mảng kiểu số nguyên (số phần tử là 5)” có thể được viết như sau:

{
    int a[ 5 ]; int i = 0;
    while ( i < 5 ) { a[ i ] = 0; i++; }
}

Nếu lệnh break (break statement / break文) nằm trong câu lệnh đang được thực thi lặp lại được chạy, lệnh while sẽ kết thúc ngay lập tức. Ngoài ra,

continue;

khi lệnh continue (continue statement / continue文) có hình thức như trên được thực thi, quyền điều khiển sẽ bị bắt buộc quay trở lại phần đầu của quá trình xử lý lặp.

Lệnh do  giống với lệnh while, nhưng khác ở điểm câu lệnh chắc chắn sẽ được thực thi ít nhất 1 lần. Giống như lệnh while, trong lệnh do ta cũng có thể sử dụng lệnh break và lệnh continue để kiểm soát cách thức lặp.

Câu lệnh lặp (lệnh for) (Iteration statement / 繰り返し文 (for文))

Lệnh for (for statement / for文) có dạng như sau:

for( Biểu_thức_1; Biểu_thức_điều_khiển; Biểu_thức_2 ) Câu_lệnh_1

Đầu tiên, nó thực thi Biểu thức 1 như là bước tiền xử lý, sau đó kiểm tra Biểu thức điều khiển, nếu giá trị đó là Đúng (True) thì thực thi Câu lệnh 1 và Biểu thức 2. Lệnh for rất tiện lợi trong trường hợp bạn muốn sử dụng chính số lần lặp trong quá trình xử lý.

※ Đầu tiên thực hiện Biểu thức 1 như là bước chuẩn bị trước, và thực hiện lặp lại Câu lệnh 1Biểu thức 2 chỉ trong khi giá trị của Biểu thức điều khiển là Đúng (True).

Biểu thức 1, Biểu thức điều khiển, Biểu thức 2 có thể lược bỏ. Trường hợp Biểu thức điều khiển bị lược bỏ, giá trị đó được coi là luôn Đúng.

※ Có thể chuyển xử lý về đầu vòng lặp bằng lệnh continue, hoặc kết thúc vòng lặp bằng lệnh break.

Ví dụ, xử lý “gán 0 cho tất cả các phần tử của mảng kiểu số nguyên (số phần tử là 5)” đã được đưa ra làm ví dụ trong phần giải thích lệnh while, có thể được viết một cách ngắn gọn như sau:

{
    int a[5]; int i;
    for(i = 0; i < 5; i++) a[i] = 0;
}

Tại Biểu thức 1, ta khởi tạo biến dùng làm chỉ số i về 0 như một bước chuẩn bị trước, sau khi thực thi Câu lệnh 1, thì tại Biểu thức 2, ta tăng i lên (increment).

Trong lệnh for, Biểu thức 1, Biểu thức điều khiển, và Biểu thức 2 có thể được lược bỏ. Trong trường hợp Biểu thức điều khiển bị lược bỏ, giá trị đó được hiểu là Đúng (True), và vòng lặp sẽ “tiếp tục quay” (lặp vô hạn). Ngoài ra, lệnh for cũng có thể sử dụng lệnh break và lệnh continue để kiểm soát cách thức lặp.

Câu lệnh nhảy (Jump statement / ジャンプ文)

Câu lệnh nhảy bao gồm lệnh returnbreakcontinuegoto. Cái duy nhất chưa được giải thích là lệnh gotoLệnh goto thực hiện việc:

goto Tên_nhãn;

bằng cách làm như trên, nó sẽ bắt buộc rẽ nhánh quyền điều khiển đến câu lệnh có gắn tên nhãn tương ứng.

Tuy nhiên, nếu sử dụng lệnh goto, chương trình sẽ trở nên cực kỳ khó hiểu, vì vậy bạn nên cố gắng không sử dụng nó (hầu hết các xử lý đều có thể viết được bằng các câu lệnh lựa chọn và câu lệnh lặp đã giải thích trước đó). Trường hợp duy nhất mà việc sử dụng lệnh goto có thể được chấp nhận là khi muốn hợp nhất việc xử lý lỗi trong hàm về một mối. Ví dụ, nếu thực hiện kiểm tra lỗi một cách chặt chẽ, hàm sẽ trở nên phức tạp như sau:

				
					void f ( void )
{
    /* ... Xử lý 1 ... */
    if ( Bình_thường_không? )
    {
        /* ... Xử lý 2 ... */
        if ( Bình_thường_không? )
        {
            /* ... Xử lý 3 ... */
        }
        else
        { /* Xử lý lỗi */ return; }
    }
    else
    { /* Xử lý lỗi */ return; }
    /* ... */
}				
			

Nếu có thể hợp nhất xử lý lỗi, ta có thể viết lại theo hình thức dễ nhìn hơn như sau:

				
					void f( void )
{
    /* ... Xử lý 1 ... */
    if ( Có_lỗi_không?) goto error_handler;
    /* ... Xử lý 2 ... */
    if ( Có_lỗi_không?) goto error_handler;
    /* ... Xử lý 3 ... */
    return;
    error_handler : /* Xử lý lỗi */
    return;
}				
			

Leave a Comment

Chia sẻ:

0.2. Hàm số trong ngôn ngữ C

Or copy link

CONTENTS