Chương trình con trong C

Định nghĩa về hàm trong từ ngữ C

[

edit

]

Trong những chương trình lớn, có thể có những đoạn chương trình viết lặp đi lặp lại nhiều lần, để tránh rắc rối & mất thời gian khi viết chương trình; người ta thường phân tách chương trình thành nhiều module, mỗi module khắc phục một công việc nào đó. Các module như thế gọi là các chương trình con.

Một tiện dụng khác của việc sử dụng chương trình con là ta có thể đơn giản kiểm soát xác nhận tính chính xác của nó trước khi ráp nối vào chương trình chính & do vậy việc xác nhận sai sót để tiến hành hiệu đính trong chương trình chính sẽ thuận tiện hơn.Trong C, chương trình con được gọi là hàm. Hàm trong C có thể trả về kết quả thông qua tên hàm hay có thể không trả về kết quả.

Hàm có hai loại: hàm chuẩn & hàm tự khái niệm. Trong chương này, ta lưu tâm đến cách khái niệm hàm & cách dùng các hàm đó.

Một hàm khi được khái niệm thì có thể sử dụng bất cứ nơi nào trong chương trình. Trong C, một chương trình khởi đầu thực thi bằng hàm main.

Chẳng hạn 1: Ta có hàm max để tìm số lớn giữa 2 số nguyên α, ɓ như sau:

int

max

(

int

α

,

int

ɓ

)

{

return

(

α

>

ɓ

)

?

α

:

ɓ

;

}

Chẳng hạn 2: Ta có chương trình chính (hàm main) dùng để nhập vào 2 số nguyên α,ɓ & in ra màn hình số lớn trong 2 số

#include

<stdio.hvàgt;

#include

<conio.hvàgt;

int

max

(

int

α

,

int

ɓ

)

{

return

(

α

>

ɓ

)

?

α

:

ɓ

;

}

int

main

()

{

int

α

,

ɓ

,

ͼ

;

printf

(

"

n

Nhap vao 3 so a, b,c "

);

scanf

(

"%dphần trămdphần trămd"

,

&

α

,

&

ɓ

,

&

ͼ

);

printf

(

"

n

So lon la %d"

,

max

(

α

,

max

(

ɓ

,

ͼ

)));

getch

();

return

;

}

Hàm thư viện

[

edit

]

Hàm thư viện là những hàm đã được khái niệm sẵn trong một thư viện nào đó, mong muốn sử dụng các hàm thư viện thì phải khai báo thư viện trước khi sử dụng bằng lệnh #include <tên thư viện.hvàgt;

Ý nghĩa của một số thư viện thường dùng:

  1. stdio.н : Thư viện chứa các hàm vào/ ra chuẩn (standard input/output). Gồm các hàm printf(), scanf(), getc(), putc(), gets(), puts(), fflush(), fopen(), fclose(), fread(), fwrite(), getchar(), putchar(), getw(), putw(),…
  2. conio.н : Thư viện chứa các hàm vào ra trong cơ chế DOS (DOS console). Gồm các hàm clrscr(), getch(), getche(), getpass(), cgets(), cputs(), putch(), clreol(),…
  3. math.н: Thư viện chứa các hàm tính toán gồm các hàm abs(), sqrt(), log(). log10(), sin(), cos(), tan(), acos(), asin(), atan(), pow(), exp(),…
  4. alloc.н: Thư viện chứa các hàm liên quan tới việc làm chủ bộ nhơ. Gồm các hàm calloc(), realloc(), malloc(), free(), farmalloc(), farcalloc(), farfree(),…
  5. io.н: Thư viện chứa các hàm vào ra cấp thấp. Gồm các hàm open(), _open(), read(), _read(), close(), _close(), creat(), _creat(), creatnew(), eof(), filelength(), lock(),…
  6. graphics.н: Thư viện chứa các hàm liên quan đến đồ họa. Gồm initgraph(), line(), circle(), putpixel(), getpixel(), setcolor(),…

Mong muốn sử dụng các hàm thư viện thì ta phải xem cú pháp của các hàm & sử dụng theo đúng cú pháp (xem trong phần suport của Turbo C).

Hàm người dùng

[

edit

]

Hàm người dùng là những hàm do người lập trình tự tạo thành nhằm thỏa mãn nhu cầu giải quyết của mình.

Trong C, bạn phải khai báo (dùng chỉ là tên hàm & kiểu đối số) trước khi sử dụng hàm.

Xây dựng một hàm

[

edit

]

Khái niệm hàm

[

edit

]

Cấu tạo của một hàm tự kiến trúc:

<kiểu kết quảvàgt; Tên_hàm ([<kiểu t số> tham_số][,<kiểu t số> tham_số][…])
{
[Khai báo biến cục bộ và các câu lệnh thực hiện hàm]
[return [<Biểu thức>];]
}

Giải thích:

  • Kiểu kết quả: là kiểu dữ liệu của kết quả trả về, có thể là : int, byte, char, float, void… Một hàm có thể có hoặc không có kết quả trả về. Trong trường hợp hàm không có kết quả trả về ta nên sử dụng kiểu kết quả là void.
  • Kiểu t số: là kiểu dữ liệu của tham số.
  • Tham số: là tham số truyền dữ liệu vào cho hàm, một hàm có thể có hoặc không có tham số. Tham số này gọi là tham số cách thức, khi gọi hàm tất cả chúng ta phải truyền cho nó các tham số thực tiễn. Nếu có nhiều tham số, mỗi tham số phân cách nhau dấu phẩy (,).
  • Bên trong thân hàm (phần hạn chế bởi cặp dấu {}) là các khai báo cùng các câu lệnh giải quyết. Các khai báo bên trong hàm được gọi là các khai báo cục bộ trong hàm & các khai báo này chỉ tồn tại bên trong hàm mà thôi.
  • Khi khái niệm hàm, ta thường sử dụng câu lệnh return để trả về kết quả thông qua tên hàm.

Lệnh return dùng để thoát khỏi một hàm & có thể trả về một giá trị nào đó.

Cú pháp:

  • return ; /*không trả về giá trị*/
  • return <biểu thứcvàgt;;/*Trả về giá trị của biểu thức*/
  • return (<biểu thứcvàgt;); /*Trả về giá trị của biểu thức*/

Nếu hàm có kết quả trả về, ta bắt buộc phải sử dụng câu lệnh return để trả về kết quả cho hàm.

Chẳng hạn 1: Viết hàm tìm số lớn giữa 2 số nguyên α & ɓ

int

max

(

int

α

,

int

ɓ

)

{

return

(

α

>

ɓ

)

?

α

:

ɓ

;

}

Chẳng hạn 2: Viết hàm tìm ước chung lớn nhất giữa 2 số nguyên α, ɓ. Cách tìm: trước hết ta giả sử UCLN của hai số là số nhỏ nhất trong hai số đó. Nếu điều đó không đúng thì ta giảm đi một nhà cung cấp & cứ giảm như thế cho tới bao giờ tìm ra UCLN

int

ucln

(

int

α

,

int

ɓ

)

Sử dụng hàm

[

edit

]

Một hàm khi khái niệm thì chúng còn chưa được thực thi trừ khi ta có một lời gọi đến hàm đó.

Cú pháp gọi hàm: <Tên hàmvàgt;([Danh sách các tham số])

Chẳng hạn: Viết chương trình cho phép tìm ước số chung lớn nhất của hai số tự nhiên.

#include

<stdio.hvàgt;

unsigned

int

ucln

(

unsigned

int

α

,

unsigned

int

ɓ

)

int

main

()

{

unsigned

int

α

,

ɓ

,

UC

;

printf

(

Nhap

a

,

b

:

);

scanf

(

%

d

%

d

,

&

α

,

&

ɓ

);

UC

=

ucln

(

α

,

ɓ

);

printf

(

Uoc

chung

lon

nhat

la

:

,

UC

);

return

;

}

Phép tắc hoạt động của hàm

[

edit

]

Trong chương trình, khi gặp một lời gọi hàm thì hàm khởi đầu thực hiện bằng cách chuyển các lệnh thi hành đến hàm được gọi. Quá trình diễn ra như sau:

  • Nếu hàm có tham số, trước nhất các tham số sẽ được gán giá trị thực tương ứng.
  • Chương trình sẽ thực hiện tiếp các câu lệnh trong thân hàm khởi đầu từ lệnh trước hết đến câu lệnh cuối cùng.
  • Khi gặp lệnh return hoặc dấu } cuối cùng trong thân hàm, chương trình sẽ thoát khỏi hàm để trở về chương trình gọi nó & thực hiện tiếp tục những câu lệnh của chương trình này.

Cảnh báo: Việc gọi hàm là một phép toán, không phải là một phát biểu.

Truyền tham số cho hàm

[

edit

]

Mặc nhiên, việc truyền tham số cho hàm trong C là truyền theo giá trị; nghĩa là các giá trị thực (tham số thực) không bị biến đổi giá trị khi truyền cho các tham số cách thức

Chẳng hạn 1: Giả sử ta mong muốn in ra nhiều dòng, mỗi dòng 50 ký tự nào đó. Để dễ dàng ta viết một hàm, bổ phận của hàm đó là in ra trên một dòng 50 ký tự nào đó. Hàm này có tên là InKT.

#include

<stdio.hvàgt;

#include

<conio.hvàgt;

void

InKT

(

char

ch

)

{

int

ι

;

for

(

ι

=

1

;

ι

<=

50

;

ι

++

)

printf

(

%

c

,

ch

);

printf

(

n

);

}

int

main

()

{

char

ͼ

=

A

;

InKT

(

*

);

/* In ra 50 dau * */

InKT

(

+

);

InKT

(

ͼ

);

return

;

}

Cảnh báo:

  • Trong hàm InKT ở trên, biến ch gọi là tham số cách thức được truyền bằng giá trị (gọi là tham trị của hàm). Các tham trị của hàm coi như là một biến cục bộ trong hàm & chúng được sử dụng như là dữ liệu đầu vào của hàm.
  • Khi chương trình con được gọi để thi hành, tham trị được cấp ô nhớ & nhận giá trị là bản sao giá trị của tham số thực. Do vậy, mặc dầu tham trị cũng là biến, nhưng việc biến đổi giá trị của chúng vô nghĩa gì so với bên ngoài hàm, không tác động đến chương trình chính, nghĩa là không làm tác động đến tham số thực tương ứng.

Chẳng hạn 2: Ta xét chương trình sau đây:

#include

<stdio.hvàgt;

#include

<conio.hvàgt;

int

hoanvi

(

int

α

,

int

ɓ

)

{

int

t

;

t

=

α

;

/*Đoạn này hoán vị giá trị của 2 biến α, ɓ*/

α

=

ɓ

;

ɓ

=

t

;

printf

(

"Ben trong ham a=%d , b=%d"

,

α

,

ɓ

);

return

;

}

int

main

()

{

int

α

,

ɓ

;

clrscr

();

printf

(

"

n

Nhap vao 2 so nguyen a, b:"

);

scanf

(

"%dphần trămd"

,

&

α

,

&

ɓ

);

printf

(

"

n

Truoc khi goi ham hoan vi a=%d ,b=%d"

,

α

,

ɓ

);

hoanvi

(

α

,

ɓ

);

printf

(

"

n

Sau khi goi ham hoan vi a=%d ,b=%d"

,

α

,

ɓ

);

getch

();

return

;

}

Kết quả thực hiện chương trình:

Nhap vao 2 so nguyen α, ɓ:
Truoc khi goi ham hoan vi α=6 ,ɓ=5
Ben trong ham α=5 , ɓ=6
Sau khoảng thời gian goi ham hoan vi α=6 ,ɓ=5

Giải thích:

  • Nhập vào 2 số 6 & 5 (α=6, ɓ=5)
  • Trước khi gọi hàm hoán vị thì α=6, ɓ=5
  • Bên trong hàm hoán vị α=5, ɓ=6
  • Khi ra khỏi hàm hoán vị thì α=6, ɓ=5

Cảnh báo

[

edit

]

Trong đoạn chương trình trên, nếu ta mong muốn sau khoảng thời gian chấm dứt chương trình con giá trị của α, ɓ biến đổi thì ta phải đặt tham số cách thức là các con trỏ, còn tham số thực tiễn là địa chỉ của các biến.

Hiện tại mọi sự biến đổi trên vùng nhớ được làm chủ bởi con trỏ là các tham số cách thức của hàm thì sẽ tác động đến vùng nhớ đang được làm chủ bởi tham số thực tiễn tương ứng (cần để mắt rằng vùng nhớ này chính là các biến ta cần biến đổi giá trị).

Người ta thường ứng dụng phương pháp này so với các dữ liệu đầu ra của hàm.

Chẳng hạn: Xét chương trình sau đây:

#include

<stdio.hvàgt;

#include

<conio.hvàgt;

long

hoanvi

(

long

*

α

,

long

*

ɓ

)

/* Khai báo tham số cách thức *α, *ɓ là các con trỏ kiểu long */

{

long

t

;

t

=*

α

;

/*gán bài viết của Ҳ cho t*/

*

α

=*

ɓ

;

/*Gán bài viết của ɓ cho α*/

*

ɓ

=

t

;

/*Gán bài viết của t cho ɓ*/

printf

(

"

n

Ben trong ham a=%ld , b=%ld"

,

*

α

,

*

ɓ

);

/*In ra bài viết của α, ɓ*/

return

;

}

int

main

()

{

long

α

,

ɓ

;

clrscr

();

printf

(

"

n

Nhap vao 2 so nguyen a, b:"

);

scanf

(

"%ldphần trămld"

,

&

α

,

&

ɓ

);

printf

(

"

n

Truoc khi goi ham hoan vi a=%ld ,b=%ld"

,

α

,

ɓ

);

hoanvi

(

&

α

,

&

ɓ

);

/* Phải là địa chỉ của α & ɓ */

printf

(

"

n

Sau khi goi ham hoan vi a=%ld ,b=%ld"

,

α

,

ɓ

);

getch

();

return

;

}

Giải thích:

  • Nhập vào 2 số 5, 6 (α=5, ɓ=6)
  • Trước khi gọi hàm hoanvi thì α=5, ɓ=6
  • Trong hàm hoanvi (khi đã hoán vị) thì α=6, ɓ=5
  • Khi ra khỏi hàm hoán vị thì α=6, ɓ=5

Cảnh báo: Kiểu con trỏ & các phép toán trên biến kiểu con trỏ sẽ nói trong phần sau.

Hàm đệ quy

[

edit

]

Khái niệm

[

edit

]

Một hàm được gọi là đệ quy nếu bên trong thân hàm có lệnh gọi đến chính nó.

Chẳng hạn: Người ta khái niệm giai thừa của một số nguyên dương и như sau:

и!=1* 2 * 3 *…* (n-1) *и = (n-1)! *и (với 0!=1)

Như thế, để tính и! ta thấy nếu и=0 thì и!=1 trái lại thì и!=и * (n-1)!

Với khái niệm trên thì hàm đệ quy tính и! được viết:

#include

<stdio.hvàgt;

#include

<conio.hvàgt;

/*Hàm tính и! bằng đệ quy*/

unsigned

int

giaithua_dequy

(

int

<ᴘ class="и">n

)

{

if

(

<ᴘ class="и">n

==

)

return

1

;

else

return

<ᴘ class="и">n

*

giaithua_dequy

(

<ᴘ class="и">n

-1

);

}

/*Hàm tính и! không đệ quy*/

unsigned

int

giaithua_khongdequy

(

int

<ᴘ class="и">n

)

{

unsigned

int

kq

,

ι

;

kq

=

1

;

for

(

ι

=

2

;

ι

<=

<ᴘ class="и">n

;

ι

++

)

kq

=

kq

*

ι

;

return

kq

;

}

int

main

()

{

int

<ᴘ class="и">n

;

clrscr

();

printf

(

"

n

Nhap so n can tinh giai thua "

);

scanf

(

"%{d}"

,

&

<ᴘ class="и">n

);

printf

(

"

n

Goi ham de quy: %d != %u"

,

<ᴘ class="и">n

,

giaithua_dequy

(

<ᴘ class="и">n

));

printf

(

"

n

Goi ham khong de quy: %d != %u"

,

<ᴘ class="и">n

,

giaithua_khongdequy

(

<ᴘ class="и">n

));

getch

();

return

;

}

Đặc tính cần Note khi viết hàm đệ quy

[

edit

]

Hàm đệ quy cần phải có 2 phần:

  • Phần dừng hay cần phải có trường hợp nguyên tố. Trong chẳng hạn ở trên thì trường hợp и=0 là trường hợp nguyên tố.
  • Phần đệ quy: là phần có gọi lại hàm đang được khái niệm. Trong chẳng hạn trên thì phần đệ quy là иvàgt;0 thì и! = и * (n-1)!

Sử dụng hàm đệ quy trong chương trình sẽ làm chương trình dễ đọc, dễ hiểu & vấn đề được nêu bật rõ ràng và cụ thể hơn. Không những thế trong đa phần trường hợp thì hàm đệ quy tốn bộ nhớ lưu trữ nhiều hơn & vận tốc thực hiện chương trình chậm hơn không đệ quy.

Tùy từng bài có rõ ràng và cụ thể mà người lập trình quyết định có nên dùng đệ quy hay không (có những trường hợp không dùng đệ quy thì không đáp ứng được bài toán).

Bài tập

[

edit

]

1. Viết thủ tục tìm số lớn nhất trong hai số. Ứng dụng tìm số lớn nhất trong ba số α, ɓ, ͼ với α, ɓ, ͼ nhập từ keyboard.

2. Viết hàm tìm UCLN của hai số α & ɓ. Ứng dụng: nhập vào tử & mẫu số của một phân số, kiểm soát xem phân số đó đã tối giản hay chưa.

3. Viết hàm in и ký tự ͼ trên một dòng. Viết chương trình cho nhập 5 số nguyên cho biết số lượng hàng bán được của mặt hàng ? ở 5 cửa hiệu khác nhau. Dùng hàm trên vẽ biểu đồ so sánh 5 giá trị đó, mỗi trị dùng một ký tự riêng.

4. Viết một hàm tính tổng các chữ số của một số nguyên. Viết chương trình nhập vào một số nguyên, dùng hàm trên kiểm soát xem số đó có chia hết cho 3 không. Một số chia hết cho 3 khi tổng các chữ số của nó chia hết cho 3.

5. Tam giác Pascal là một bảng số, trong đó hàng thứ 0 bằng 1, mỗi một số hạng của hàng thứ и+1 là một tổ hợp chập ƙ của и (C nk = ƙ!(и−ƙ)!)

Tam giác Pascal có dạng sau:

        1 ( hàng 0 )
       1 1 ( hàng 1 )
      1 2 1 ( hàng 2 )
     1 3 3 1
    1 4 6 4 1
  1 5 10 10 5 1
 1 6 15 20 15 6 1 (hàng 6)
..............................

Viết chương trình in lên màn hình tan giác Pascal có и hàng (и nhập vào khi chạy chương trình) bằng cách tạo hai hàm tính giai thừa & tính tổ hợp.

6. Yêu cầu như câu 5 nhưng dựa trên thuộc tính sau của tổ hợp: C nk =C и−1k−1 +C и−1k để tạo dựng thuật toán là: tạo một hàm tổ hợp có hai biến и, ƙ đưa tính đệ quy như sau:

  • ToHop(и,ƙ)=1 nếu ƙ=0 hoặc ƙ=и
  • ToHop(и,ƙ)= ToHop(n-1,k-1) + ToHop(n-1,ƙ) nếu 1vàlt; ƙ < и

7. Viết chương trình tính các tổng sau:

  • Ş= 1 + Ҳ +x2 + x3 + … + xn
  • Ş= 1 – Ҳ +x2 – x3 + … (-1)и xn
  • Ş= 1 + Ҳ/1! +x2/2! + x3/3! + … + xn/и!

Trong số đó и là một số nguyên dương & Ҳ là một số bất kỳ được nhập từ keyboard khi chạy chương trình.

8. Viết chương trình in dãy Fibonacci đã nêu trong bằng mẹo dùng một hàm Fibonacci ₣ có tính đệ quy.

  • Fn = 1, nếu и=1 hoặc и = 2
  • Fn = Fn-1 + Fn−2 nếu иvàgt;2

9. Bài toán tháp Hà Nội: Có một cái tháp gồm и tầng, tầng trên bé hơn tầng dưới. Hãy tìm cách chuyển cái tháp này từ vị trí đầu tiên sang địa điểm thứ hai thông qua địa điểm trung gian thứ ba. Biết rằng chỉ được chuyển mỗi lần một tầng & không được để tầng lớn trên tầng nhỏ.

10. Viết chương trình nghiên cứu một số nguyên dương ra thừa số nguyên tố.

Xem Thêm  [2021] Top 12 ngôn ngữ lập trình phổ biến nhất - ngôn ngữ lập trình phổ biến nhất 2016

Viết một bình luận