Truyền đối số cho hàm:

Một phần của tài liệu Bài giảng: ngôn ngữ lập trình C pdf (Trang 55 - 58)

VI. Các lệnh rẽ nhánh không điều kiện: break, continue, goto:

I.7.Truyền đối số cho hàm:

Ch−¬ng VI HAÌM, MẢNG VAÌ CON TRỎ

I.7.Truyền đối số cho hàm:

Truyền đối số cho hàm tức là cung cấp các giá trị đầu vào thực sự cho hàm phù hợp với kiểu và trật tự của các tham số đã khai báo. Đối số có thể là biến,hằng,biểu thức.

Việc truyền đối số cho hàm được thực hiện theo một kiểu duy nhất : truyền theo giá trị: hàm không làm thay đổi giá trị của các biến dùng làm đối số vì hàm chỉ làm việc trên các bản sao của các đối số mà thôi.

Ví dụ :

#include <stdio.h> #include <conio.h> void hoanvi(int a,int b); main()

{ int x=2; int y=3;

printf(“\nTruoc khi goi ham : x = %d , y = %d “,x,y); hoanvi(x,y);

printf(“\nSau khi goi ham : x = %d , y = %d “,x,y); getch();

return 0; }

void hoanvi(int a,int b) { int tam;

printf(“\nTruoc khi hoan vi: a = %d , b = %d “,a,b); tam=a;

a=b; b=tam;

printf(“\nSau khi hoan vi: a = %d , b = %d “,a,b); }

Kết quả :

Truoc khi goi ham : x = 2 , y = 3 Truoc khi hoan vi : a = 2 , b = 3 Sau khi hoan vi : a = 3 , b = 2 Sau khi goi ham : x = 2 , y = 3

Như vậy hàm hoanvi đã hoán vị 2 số x và y, nhưng khi quay trở lại chương trình chính thì giá trị của x và y vẫn không thay đổi.

Để tránh điều này ta có thể sử dụng cách truyền địa chỉ hoặc sử dụng biến toàn cục, nhưng sử dụng biến toàn cục gặp một số vấn đề như bộ nhớ, giá trị của nó có thể bị thay đổi ở mọi nơi trong chương trình.

Chương trình sử dụng cách truyền địa chỉ : #include <stdio.h>

#include <conio.h>

void hoanvi(int *px,int *py); main()

{ int x=2; int y=3; hoanvi(&x,&y); printf(“\nx=%d,y=%d”,x,y); getch(); return 0; }

void hoanvi(int *px,int *py) { int tam; tam=*px; *px=*py; *py=tam; } I.8. Phạm vi biến:

Phạm vi biến là khả năng truy xuất biến đó ở các phần khác nhau trong chương trình. Các biến chỉ được sử dụng kể từ vị trí biến được khai báo trở xuống.

• Biến toàn cục : biến được khai báo ở ngoài các hàm (kể cả hàm main), được sử dụng ở mọi nơi trong chương trình. Trình biên dịch sẽ tự động gán cho biến này giá trị 0 nếu ta không khởi tạo giá trị cho nó.

Thực ra đối với chương trình trải trên nhiều tập tin, để sử dụng các biến toàn cục trong các modul khác ta phải sử dụng từ khóa extern. Khi đó trình biên dịch không cấp phát ô nhớ nào cho biến đó, lệnh này chỉ nhằm mục đích báo rằng biến đó được khai báo đâu đó trong chương trình (trong modul nào đó của chương trình).

• Biến địa phương : biến được khai báo bên trong hàm và chỉ sử dụng bên trong hàm trong thời gian hàm đó hoạt động (hàm được gọi). Nếu biến địa phương trùng tên với biến toàn cục thì trong phạm vi hàm đó, biến toàn cục không có tác dụng.

Các biến toàn cục có thể được thay đổi trong bất kì hàm nào, đây cũng chính là nhược điểm của chúng: ta khó kiểm soát hơn, nhất là trong chương trình lớn. Vì vậy cần hạn chế sử dụng biến toàn cục khi nó không cần thiết cho hầu hết các hàm. Ta thường dùng biến toàn cục để giải quyết việc phải truyền các biến đó cho nhiều hàm khác nhau.

Ví dụ : int x; main() { int y; ... } int z; ham1() { ... }

ham2() {... }

biến z có tác dụng đối với ham1 và ham2, biến x có tác dùng đối với cả 3 hàm, biến y chỉ có tác dụng đối với hàm main().

Các biến cục bộ trong khối lệnh:

Ta có thể khai báo các biến ở ngay đầu khối lệnh, các biến này là biến cục bộ. (adsbygoogle = window.adsbygoogle || []).push({});

Khi máy bắt đầu làm việc với 1 khối lệnh thì các biến và các mảng khai báo bên trong khối lệnh đó mới được hình thành và được cấp phát bộ nhớ. Các biến này chỉ tồn tại trong thời gian máy làm việc với khối lệnh và chúng sẽ được giải phóng khi kết thúc khối lệnh. Vì vậy :

• Giá trị của biến hoặc mảng khai báo bên trong khối lệnh không thể đưa ra sử dụng bên ngoài khối lệnh đó.

• Ở ngoài khối lệnh không thể can thiệp các biến và mảng khai báo trong khối lệnh.

• Ở trong khối lệnh có thể sử dụng biến khai báo ngoài khối lệnh nếu biến đó không trùng tên với các biến khai báo trong khối lệnh.

I.9. Cấp phát bộ nhớ :

Biến toàn cục được cấp phát bộ nhớ tĩnh nên gọi là biến tĩnh và cấp phát tĩnh. Biến địa phương có các loại :

• Cấp phát động : khi kết thúc hàm biến được giải phóng, không lưu kết quả cho lần sau.

• Cấp phát tĩnh : khi kết thúc hàm không giải phóng biến, lưu kết quả cho lần sau ( dùng từ khóa static trước dòng khai báo) .

Các biến địa phương được ngầm định là cấp phát động. Nếu thích, ta có thể thêm vào từ khóa auto vào đầu dòng khai báo : auto int x; Các biến auto được cấp phát ở stack.

Ví dụ : sự khác nhau giữa biến cấp phát động và biến cấp phát tĩnh. #include <stdio.h> #include <conio.h> void ham(void); main() { int dem; for(dem=0;dem<=3;dem++)

{ printf(“\nLan goi thu %d”,dem); ham(); } getch(); return 0; } void ham(void)

{ static int x; // có thể thay bằng static int x=0; int y=0;

printf(“ x = %d , y = %d “,x++,y++); }

Kết quả :

Lan goi thu 0 : x = 0 , y = 0 Lan goi thu 1 : x = 1 , y = 0 Lan goi thu 2 : x = 2 , y = 0 Lan goi thu 3 : x = 3 , y = 0

Câu lệnh static int x; chỉ được thực hiện khi biên dịch, khi ham() được gọi lần đầu tiên nó cũng không được thực hiện. Câu lệnh int y=0 luôn được thực hiện mỗi khi gọi hàm.

Và theo ví dụ trên ta thấy các biến địa phương tĩnh được khởi tạo giá trị đầu bằng 0 khi dùng lần đầu (nếu ta không khởi tạo).

Nếu chương trình được viết trên nhiều file và biến a được khai báo ở ngoài các hàm như sau: static int a; thì biến a chỉ được biết đến trong modul hiện tại, không được biết đến trong các modul khác.

• Biến địa phương thanh ghi : register int x;

Dùng để yêu cầu trình biên dịch nếu có thể thì đặt biến đó vào thanh ghi thay vì đặt vào một ô nhớ thông thường. Khi đó ta có thể truy xuất đến biến này rất nhanh chóng. Ta thường sử dụng cách khai báo này với các biến đếm của vòng lặp. Nếu trình biên dịch không thể cấp phát ở register thì biến đó là auto.

Từ khóa register không được dùng với các biến tĩnh hay biến ngoài. Ta không thể định nghĩa một con trỏ tới biến thanh ghi.

Một phần của tài liệu Bài giảng: ngôn ngữ lập trình C pdf (Trang 55 - 58)