Chương 4. Hàm và chương trìnhTham trịTham chiếuDẫn trỏ7. Hàm và mảng dữ liệu a. Truyền mảng 1 chiều cho hàmThông thường chúng ta hay xây dựng các hàm làm việc trên mảng như vectơ hay ma trận các phần tử. Khi đó tham đối thực sự của hàm sẽ là các mảng dữ liệu này. Trong trường hợp này ta có 2 cách khai báo đối. Cách thứ nhất đối được khai báo bình thường như khai báo biến mảng nhưng không cần có số phần tử kèm theo, ví dụ: − int x[]; − float x[]; Cách thứ...
Nội dung trích xuất từ tài liệu:
Ngôn ngữ lập trình c&c++ ( Phạm Hồng Thái) P13Chương 4. Hàm và chương trình Tham trị Tham chiếu Dẫn trỏKhai báo đối void swap(int x, int y) void swap(int &x, int &y) void swap(int *x, int *y)Câu lệnh t = x; x = y; y = t; t = x; x = y; y = t; t = *x; *x = *y; *y = t;Lời gọi swap(a, b); swap(a, b); swap(&a, &b);Tác dụng a, b không thay đổi a, b có thay đổi a, b có thay đổi 7. Hàm và mảng dữ liệu a. Truyền mảng 1 chiều cho hàm Thông thường chúng ta hay xây dựng các hàm làm việc trên mảng như vectơ hayma trận các phần tử. Khi đó tham đối thực sự của hàm sẽ là các mảng dữ liệu này.Trong trường hợp này ta có 2 cách khai báo đối. Cách thứ nhất đối được khai báo bìnhthường như khai báo biến mảng nhưng không cần có số phần tử kèm theo, ví dụ: − int x[]; − float x[]; Cách thứ hai khai báo đối như một con trỏ kiểu phần tử mảng, ví dụ: • int *p; • float *p Trong lời gọi hàm tên mảng a sẽ được viết vào danh sách tham đối thực sự, vì a làđịa chỉ của phần tử đầu tiên của mảng a, nên khi hàm được gọi địa chỉ này sẽ gán chocon trỏ p. Vì vậy giá trị của phần tử thứ i của a có thể được truy cập bởi x[i] (theo khaibáo 1) hoặc *(p+i) (theo khai báo 2) và nó cũng có thể được thay đổi thực sự (do đâycũng là cách truyền theo dẫn trỏ). Sau đây là ví dụ đơn giản, nhập và in vectơ, minh hoạ cho cả 2 kiểu khai báo đối.Ví dụ 8 : Hàm nhập và in giá trị 1 vectơ void nhap(int x[], int n) // n: số phần tử { int i; for (i=0; i> x[i]; // hoặc cin >> *(x+i) } void in(int *p, int n) { 109Chương 4. Hàm và chương trình int i; for (i=0; iChương 4. Hàm và chương trìnhcủa phần tử này. Ưu điểm của cách khai báo này là ta có thể truyền mảng với kíchthước bất kỳ (số cột không cần định trước) cho hàm.Sau đây là các ví dụ minh hoạ cho 2 cách khai báo trên.Ví dụ 9 : Tính tổng các số hạng trong ma trận float tong(float x[][10], int m, int n) // hoặc float tong(float (*x)[10], int m, int n) { // m: số dòng, n: số cột float t = 0; int i, j ; for (i=0; i> na; for (i=0; iChương 4. Hàm và chương trình float min = *x; // gán phần tử đầu tiên cho min int k, kmin; for (k=1; k *(x+k)) { min = *(x+k) ; kmin = k; } cout Chương 4. Hàm và chương trình { float *t = new float[m*n]; // t là ma trận kết quả (xem như dãy số) int k, i, j ; for (k = 0; k < m*n; k++) *(t+k) = *(x+k) + *(y+k) ; inmt((float*)t, m, n); } main() { float a[8][10], b[5][7] ; int i, j, m, n; cout > m >> n; for (i=0; iChương 4. Hàm và chương trìnhmảng cũng chính là một con trỏ, vì vậy việc hàm trả lại một con trỏ trỏ đến dãy dữ liệukết quả là tương đương với việc trả lại mảng. Ngoài ra còn một cách dễ dùng hơn đốivới mảng 2 chiều là mảng kết quả được trả lại vào trong tham đối của hàm (giống nhưnghiệm của phương trình bậc 2 được trả lại vào trong các tham đối). Ở đây chúng ta sẽlần lượt xét 2 cách làm việc này. 1. Giá trị trả lại là con trỏ trỏ đến mảng kết quả. Trước hết chúng ta xét ví dụ nhỏ sau đây: int* tragiatri1() // giá trị trả lại là con trỏ trỏ đến dãy số nguyên { int kq[3] = { 1, 2, 3 }; // tạo mảng kết quả với 3 giá trị 1, 2, 3 return kq ; // trả lại địa chỉ cho con trỏ kết quả hàm } int* tragiatri2() // giá trị trả lại là con trỏ trỏ đến dãy số nguyên { int *kq = new int[4]; // cấp phát 3 ô nhớ nguyên *kq = *(kq+1) = *(kq+2) = 0 ; // tạo mảng kết quả với 3 giá trị 1, 2, 3 return kq ; // trả lại địa chỉ cho con trỏ kết quả hàm } main() { int *a, i; a = tragiatri1(); for (i=0; iChương 4. Hàm và chương trìnhchỉ của kq trước khi nó kết thúc, thế nhưng sau khi hàm thực hiện xong, toàn bộ kq sẽđược xoá khỏi bộ nhớ và vì vậy con trỏ kết quả hàm đã trỏ đến vùng nhớ không còncác giá trị như kq đã có. Từ điều này việc sử dụng hàm trả lại con trỏ là phải hết sứccẩn thận. Muốn trả lại con trỏ cho hàm thì con trỏ này phải trỏ đến dãy dữ liệu nào saocho nó không mất đi sau khi hàm kết thúc, hay nói khác hơn đó phải là những dãy dữliệu được khởi tạo bên ngoài hàm hoặc có thể sử dụng theo phương pháp trong hàmtragiatri2(). Trong tragiatri2() một mảng kết quả 3 số cũng được tạo ra nhưng bằngcách xin cấp phát vùng nhớ. Vùng nhớ được cấp phát này sẽ vẫn còn tồn tại sau khihàm kết thúc (nó chỉ bị xoá đi khi sử dụng toán tử delete). Do vậy hoạt động củatragiatri2() là chính xác. Tóm lại, ví dụ trên cho thấy nếu muốn trả lại giá trị con trỏ thì vùng dữ liệu mànó trỏ đến phải được cấp phát một cách tường minh (bằng toán tử new), chứ không đểchương trình tự động cấp phát và tự động thu hồi. Ví dụ sau minh hoạ hàm cộng 2vectơ và trả lại vectơ kết quả (thực chất là con trỏ trỏ đến vùng nhớ đặt kết quả) int* congvt(int *x, int *y, int n) // n số phần tử của vectơ { int* z = new int[n]; // xin cấp phát bộ nhớ for (int i=0; i n; // nhập số phần tử for (i=0; i> a[i] ; // nhập vectơ a for (i=0; i> b[i] ; // nhập vectơ b c = congvt(a, b, n); for (i=0; iC ...