SLIDE1

Tuesday, September 23, 2014

hàm trong ngôn ngữ lập trình c (part 2)


1.Truyền tham số theo tham số giá trị hay tham số biến
Cho đến nay, trong tất cả các hàm chúng ta đã biết, tất cả các tham số truyền cho hàm đều được truyền theo giá trị. Điều này có nghĩa là khi chúng ta gọi hàm với các tham số, những gì chúng ta truyền cho hàm là các giá trị chứ không phải bản thân các biến. Ví dụ, giả sử chúng ta gọi hàm tich như sau:
int x=5, y=3, z; 
z=tich(x,y);
Trong trường hợp này khi chúng ta gọi hàm addition thì các giá trị 5 and 3 được truyền cho hàm, không phải là bản thân các biến.
#include<stdio.h>
#include<conio.h>
void nhandoi(int&a, int&b)
{
a *= 2;
b *= 2;
}
void main()
{
int x, y;
printf("nhap vao a,b:\n");
scanf_s("%d%d", &x, &y);
nhandoi(x, y);
printf(" %d  %d ", x, y);
_getch();
}
Điều đầu tiên làm bạn chú ý là trong khai báo của duplicate theo sau tên kiểu của mỗi tham số đều là dấu và (&), để báo hiệu rằng các tham số này được truyền theo tham số biến chứ không phải tham số giá trị.

Khi truyền tham số dưới dạng tham số biến chúng ta đang truyền bản thân biến đó và bất kì sự thay đổi nào mà chúng ta thực hiện với tham số đó bên trong hàm sẽ ảnh hưởng trực tiếp đến biến đó.
Truyền tham số dưới dạng tham số biến cho phép một hàm trả về nhiều hơn một giá trị. Ví dụ, đây là một hàm trả về số liền trước và liền sau của tham số đầu tiên
#include<stdio.h>
#include<conio.h>
void tinh(int x, int&next, int&prev)
{
next = x + 1;
prev = x - 1;
}
void main()
{
int x,y,z;
printf("nhap vao 1 so");
scanf_s("%d", &x);
tinh(x, y, z);
printf("%d %d %d", z,x,y);
_getch();
}
2.Giá trị mặc định của tham số
Khi định nghĩa một hàm chúng ta có thể chỉ định những giá trị mặc định sẽ được truyền cho các đối số trong trường hợp chúng bị bỏ qua khi hàm được gọi. Để làm việc này đơn giản chỉ cần gán một giá trị cho đối số khi khai báo hàm. Nếu giá trị của tham số đó vẫn được chỉ định khi gọi hàm thì giá trị mặc định sẽ bị bỏ qua. Ví dụ
#include<stdio.h>
#include<conio.h>
int tinh(int x,int y=2)
{
int chia;
chia = x / y;
return(chia);
}
void main()
{
printf("thieu 1 tham so =%d \n", tinh(12));
printf("du tham so=%d\n", tinh(20, 4));
_getch();
}
có hai tham số, bởi vậy giá trị mặc định sẽ được bỏ qua. Kết quả của hàm sẽ là 5 (20/4)
3.Quá tải các hàm
Hai hàm có thể có cũng tên nếu khai báo tham số của chúng khác nhau, điều này có nghĩa là bạn có thể đặt cùng một tên cho nhiều hàm nếu chúng có số tham số khác nhau hay kiểu dữ liệu của các tham số khác nhau (hay thậm chí là kiểu dữ liệu trả về khác nhau).
4.đệ quy
Các hàm có thể gọi chính nó. Điều này có thể có ích với một số tác vụ như là một số phương pháp sắp xếp hay tính giai thừa của một số. Ví dụ, để tính giai thừa của một số (n), công thức toán học của nó như sau:
n! = n * (n-1) * (n-2) * (n-3) ... * 1 
và một hàm đệ qui để tính toán sẽ như sau:
#include<stdio.h>
#include<conio.h>
unsigned long giaithua(unsigned long x)
{
if (x>1) return(x*giaithua(x-1));
else return(1);

}

void main()
{
unsigned long a;
printf("nhap vao so can tinh giai thua\n");
scanf_s("%lu", &a);
printf("%lu! = %lu", a, giaithua(a));
_getch();
}
5.Khai báo mẫu cho hàm
Cho đến giờ chúng ta hoàn toàn phải định nghĩa hàm trước lệnh gọi đầu tiên đến nó, mà thường là trong main, vì vậy hàm main luôn phải nằm cuối chương trình. Nếu bạn thử lặp lại một vài ví dụ về hàm trước đây nhưng thử đặt hàm main trước bất kì một hàm được gọi từ nó, bạn gần như chắc chắn sẽ nhận được thông báo lỗi. Nguyên nhân là một hàm phải được khai báo trước khi nó được gọi như nhưnggx gì chúng ta đã làm trng tất cả các ví dụ
Nhưng có một cách khác để tránh phải viết tất cả mã chương trình trước khi chúng có thể được dùng trong main hay bất kì một hàm nào khác. Đó chính là khai báo mẫu cho hàm. Cách này bao gồm việc khai báo hàm một cách ngắn gọn nhưng đủ để cho trình dịch có thể biết các tham số và kiểu dữ liệu trả về của hàm.
Dạng của nó như sau:
type name ( argument_type1, argument_type2, ...); 
Đây chính là phần đầu của định nghĩa hàm, ngoại trừ:
  • Nó không có bất kì lệnh nào cho hàm. Điều này có nghĩa là nó không bao gồm thân hàm với tất cả các lệnh thường được bọc trong cặp ngoặc nhọn { }
  • Nó kết thúc bằng dấu chấm phẩy (;). 
  • Trong phần liệt kê các tham số chỉ cần viết kiểu của chúng là đủ. Việc viết tên của các tham số trong phần khai báo mẫu là không bắt buộc
Ví dụ:
#include<stdio.h>
#include<conio.h>
unsigned long giaithua(unsigned long);
void main()
{
unsigned long a;
printf("nhap vao so can tinh giai thua\n");
scanf_s("%lu", &a);
printf("%lu! = %lu", a, giaithua(a));
_getch();
}
unsigned long giaithua(unsigned long x)
{
if (x>1) return(x*giaithua(x - 1));
else return(1);

}

Ví dụ này rõ ràng không phải là một ví dụ về sự hiệu quả. Tôi chắc chắn rằng các bạn có thể nhận được kết quả như trên chỉ với một nửa số dòng lệnh. Tuy nhiên nó giúp cho chúng ta thấy được việc khai báo mẫu các hàm là như thế nào
Rất nhiều lập trình viên kinh nghiệm khuyên rằng tất cả các hàm nên được khai báo mẫu. Đó cũng là lời khuyên của tôi, nhất là trong trường hợp có nhiều hàm hoặc chúng rất dài, khi đó việc khai báo tất cả các hàm ở cùng một chỗ cho phép chúng ta biết phải gọi các hàm như thế nào, vì vậy tiết kiệm được thời gian