Con trỏ cung cấp rất nhiều khả năng cho các hàm 'C' mà chúng tôi bị giới hạn để trả về một giá trị. Với các tham số con trỏ, các hàm của chúng ta hiện có thể xử lý dữ liệu thực tế hơn là một bản sao dữ liệu.
Để sửa đổi giá trị thực của các biến, câu lệnh gọi chuyển địa chỉ tới các tham số con trỏ trong một hàm.
Trong hướng dẫn này, bạn sẽ học-
- Ví dụ về con trỏ hàm
- Các hàm với tham số mảng
- Các hàm trả về một mảng
- Con trỏ hàm
- Mảng con trỏ hàm
- Các hàm sử dụng con trỏ void
- Con trỏ hàm dưới dạng đối số
Ví dụ về con trỏ hàm
Ví dụ: chương trình tiếp theo hoán đổi hai giá trị của hai:
void swap (int *a, int *b);int main() {int m = 25;int n = 100;printf("m is %d, n is %d\n", m, n);swap(&m, &n);printf("m is %d, n is %d\n", m, n);return 0;}void swap (int *a, int *b) {int temp;temp = *a;*a = *b;*b = temp;}}
Đầu ra:
m is 25, n is 100m is 100, n is 25
Chương trình hoán đổi các giá trị biến thực tế vì hàm truy cập chúng theo địa chỉ bằng cách sử dụng con trỏ. Ở đây chúng ta sẽ thảo luận về quy trình của chương trình:
- Chúng tôi khai báo hàm chịu trách nhiệm hoán đổi hai giá trị biến, hàm này nhận hai con trỏ số nguyên làm tham số và trả về bất kỳ giá trị nào khi nó được gọi.
- Trong hàm main, chúng ta khai báo và khởi tạo hai biến số nguyên ('m' và 'n') sau đó in giá trị của chúng tương ứng.
- Chúng tôi gọi hàm swap () bằng cách chuyển địa chỉ của hai biến làm đối số bằng cách sử dụng ký hiệu và. Sau đó, chúng tôi in các giá trị hoán đổi mới của các biến.
- Ở đây chúng ta xác định nội dung hàm swap () lấy hai địa chỉ biến số nguyên làm tham số và khai báo một biến số nguyên tạm thời được sử dụng làm hộp lưu trữ thứ ba để lưu một trong các biến giá trị sẽ được đưa vào biến thứ hai.
- Lưu nội dung của biến đầu tiên được trỏ bởi 'a' trong biến tạm thời.
- Lưu biến thứ hai được trỏ bởi b trong biến đầu tiên được trỏ bởi a.
- Cập nhật biến thứ hai (được trỏ bởi b) theo giá trị của biến đầu tiên được lưu trong biến tạm thời.
Các hàm với tham số mảng
Trong C, chúng ta không thể chuyển một mảng theo giá trị cho một hàm. Trong khi, tên mảng là một con trỏ (địa chỉ), vì vậy chúng ta chỉ cần truyền một tên mảng cho một hàm có nghĩa là truyền một con trỏ đến mảng.
Ví dụ, chúng tôi xem xét chương trình sau:
int add_array (int *a, int num_elements);int main() {int Tab[5] = {100, 220, 37, 16, 98};printf("Total summation is %d\n", add_array(Tab, 5));return 0;}int add_array (int *p, int size) {int total = 0;int k;for (k = 0; k < size; k++) {total += p[k]; /* it is equivalent to total +=*p ;p++; */}return (total);}
Đầu ra:
Total summation is 471
Ở đây, chúng tôi sẽ giải thích mã chương trình với các chi tiết của nó
- Chúng ta khai báo và định nghĩa hàm add_array () lấy một địa chỉ mảng (con trỏ) với số phần tử của nó làm tham số và trả về tổng cộng tích lũy của các phần tử này. Con trỏ được sử dụng để lặp lại các phần tử của mảng (sử dụng ký hiệu p [k]) và chúng tôi tích lũy tổng trong một biến cục bộ sẽ được trả về sau khi lặp toàn bộ mảng phần tử.
- Chúng ta khai báo và khởi tạo một mảng số nguyên với năm phần tử nguyên. Chúng ta in tổng tổng bằng cách chuyển tên mảng (đóng vai trò là địa chỉ) và kích thước mảng cho hàm add_array () được gọi là các đối số.
Các hàm trả về một mảng
Trong C, chúng ta có thể trả về một con trỏ đến một mảng, như trong chương trình sau:
#includeint * build_array();int main() {int *a;a = build_array(); /* get first 5 even numbers */for (k = 0; k < 5; k++)printf("%d\n", a[k]);return 0;}int * build_array() {static int Tab[5]={1,2,3,4,5};return (Tab);}
Đầu ra:
12345
Và ở đây, chúng ta sẽ thảo luận về chi tiết chương trình
- Chúng tôi định nghĩa và khai báo một hàm trả về địa chỉ mảng có chứa giá trị số nguyên và không nhận bất kỳ đối số nào.
- Chúng tôi khai báo một con trỏ số nguyên nhận toàn bộ mảng được xây dựng sau khi hàm được gọi và chúng tôi in nội dung của nó bằng cách lặp lại toàn bộ mảng năm phần tử.
Lưu ý rằng một con trỏ, không phải một mảng, được định nghĩa để lưu trữ địa chỉ mảng được trả về bởi hàm. Cũng lưu ý rằng khi một biến cục bộ được trả về từ một hàm, chúng ta phải khai báo nó là biến tĩnh trong hàm.
Con trỏ hàm
Như chúng ta đã biết theo định nghĩa rằng con trỏ trỏ đến một địa chỉ trong bất kỳ vị trí bộ nhớ nào, chúng cũng có thể trỏ đến phần đầu của mã thực thi dưới dạng các hàm trong bộ nhớ.
Một con trỏ tới hàm được khai báo bằng dấu *, câu lệnh chung cho khai báo của nó là:
return_type (*function_name)(arguments)
Bạn phải nhớ rằng các dấu ngoặc đơn xung quanh (* function_name) là quan trọng vì nếu không có chúng, trình biên dịch sẽ nghĩ rằng function_name đang trả về một con trỏ của return_type.
Sau khi xác định con trỏ hàm, chúng ta phải gán nó cho một hàm. Ví dụ: chương trình tiếp theo khai báo một hàm thông thường, xác định một con trỏ hàm, gán con trỏ hàm cho hàm bình thường và sau đó gọi hàm thông qua con trỏ:
#includevoid Hi_function (int times); /* function */int main() {void (*function_ptr)(int); /* function pointer Declaration */function_ptr = Hi_function; /* pointer assignment */function_ptr (3); /* function call */return 0;}void Hi_function (int times) {int k;for (k = 0; k < times; k++) printf("Hi\n");}
Đầu ra:
HiHiHi
- Chúng tôi xác định và khai báo một hàm tiêu chuẩn in ra văn bản Hi k lần được chỉ ra bởi tham số lần khi hàm được gọi
- Chúng tôi định nghĩa một hàm con trỏ (với khai báo đặc biệt của nó) nhận một tham số nguyên và không trả về bất kỳ thứ gì.
- Chúng tôi khởi tạo hàm con trỏ của chúng tôi với Hi_ Chức năng có nghĩa là con trỏ trỏ đến Hi_ Chức năng ().
- Thay vì gọi hàm chuẩn bằng cách nhấn vào tên hàm với các đối số, chúng tôi chỉ gọi hàm con trỏ bằng cách chuyển số 3 làm đối số, và thế là xong!
Hãy nhớ rằng tên hàm trỏ đến địa chỉ đầu của mã thực thi giống như tên mảng trỏ đến phần tử đầu tiên của nó. Do đó, các hướng dẫn như function_ptr = & Hi_ functions và (* funptr) (3) là đúng.
LƯU Ý: Không quan trọng việc chèn toán tử địa chỉ & và toán tử điều hướng * trong quá trình gán hàm và gọi hàm.
Mảng con trỏ hàm
Một mảng con trỏ hàm có thể đóng vai trò chuyển đổi hoặc câu lệnh if để đưa ra quyết định, như trong chương trình tiếp theo:
#includeint sum(int num1, int num2);int sub(int num1, int num2);int mult(int num1, int num2);int div(int num1, int num2);int main(){ int x, y, choice, result;int (*ope[4])(int, int);ope[0] = sum;ope[1] = sub;ope[2] = mult;ope[3] = div;printf("Enter two integer numbers: ");scanf("%d%d", &x, &y);printf("Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: ");scanf("%d", &choice);result = ope[choice](x, y);printf("%d", result);return 0;}int sum(int x, int y) {return(x + y);}int sub(int x, int y) {return(x - y);}int mult(int x, int y) {return(x * y);}int div(int x, int y) {if (y != 0) return (x / y); else return 0;}
Enter two integer numbers: 13 48Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: 2624
Ở đây, chúng tôi thảo luận về chi tiết chương trình:
- Chúng tôi khai báo và định nghĩa bốn hàm nhận hai đối số nguyên và trả về một giá trị nguyên. Các hàm này cộng, trừ, nhân và chia hai đối số liên quan đến hàm nào đang được người dùng gọi.
- Chúng ta khai báo 4 số nguyên để xử lý các toán hạng, kiểu hoạt động và kết quả tương ứng. Ngoài ra, chúng tôi khai báo một mảng gồm bốn con trỏ hàm. Mỗi con trỏ hàm của phần tử mảng nhận hai tham số nguyên và trả về một giá trị nguyên.
- Chúng ta gán và khởi tạo từng phần tử mảng bằng hàm đã được khai báo. Ví dụ, phần tử thứ ba là con trỏ hàm thứ ba sẽ trỏ đến hàm hoạt động nhân.
- Chúng tôi tìm kiếm các toán hạng và kiểu hoạt động từ người dùng đã nhập bằng bàn phím.
- Chúng tôi đã gọi phần tử mảng thích hợp (Con trỏ hàm) với các đối số và chúng tôi lưu trữ kết quả được tạo bởi hàm thích hợp.
Lệnh int (* ope [4]) (int, int); định nghĩa mảng con trỏ hàm. Mỗi phần tử mảng phải có cùng tham số và kiểu trả về.
Câu lệnh result = ope [choice] (x, y); chạy hàm thích hợp theo sự lựa chọn của người dùng Hai số nguyên đã nhập là các đối số được truyền cho hàm.
Các hàm sử dụng con trỏ void
Con trỏ rỗng được sử dụng trong khi khai báo hàm. Chúng tôi sử dụng phép trả về kiểu void * để trả về bất kỳ kiểu nào. Nếu chúng ta giả sử rằng các tham số của chúng ta không thay đổi khi truyền đến một hàm, chúng ta khai báo nó là const.
Ví dụ:
void * cube (const void *);
Hãy xem xét chương trình sau:
#includevoid* cube (const void* num);int main() {int x, cube_int;x = 4;cube_int = cube (&x);printf("%d cubed is %d\n", x, cube_int);return 0;}void* cube (const void *num) {int result;result = (*(int *)num) * (*(int *)num) * (*(int *)num);return result;}
Kết quả:
4 cubed is 64
Sau đây, chúng ta sẽ thảo luận chi tiết chương trình:
- Chúng ta định nghĩa và khai báo một hàm trả về một giá trị nguyên và lấy một địa chỉ của biến không thể thay đổi mà không có kiểu dữ liệu cụ thể. Chúng tôi tính toán giá trị khối lập phương của biến nội dung (x) được trỏ bởi con trỏ num và vì nó là một con trỏ void, chúng tôi phải nhập kiểu truyền nó sang kiểu dữ liệu số nguyên bằng cách sử dụng con trỏ ký hiệu cụ thể (* datatype) và chúng tôi trả về giá trị khối lập phương.
- Chúng ta khai báo toán hạng và biến kết quả. Ngoài ra, chúng tôi khởi tạo toán hạng của mình với giá trị "4."
- Chúng tôi gọi hàm khối lập phương bằng cách truyền địa chỉ toán hạng và chúng tôi xử lý giá trị trả về trong biến kết quả
Con trỏ hàm dưới dạng đối số
Một cách khác để khai thác một con trỏ hàm bằng cách chuyển nó làm đối số cho một hàm khác đôi khi được gọi là "hàm gọi lại" vì hàm nhận "gọi lại".
Trong tệp tiêu đề stdlib.h, hàm Quicksort "qsort ()" sử dụng kỹ thuật này là một thuật toán dành riêng để sắp xếp một mảng.
void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
- void * base: con trỏ void tới mảng.
- size_t num: Số phần tử của mảng.
- size_t width Kích thước phần tử.
- int (* so sánh (const void *, const void *): con trỏ hàm bao gồm hai đối số và trả về 0 khi các đối số có cùng giá trị, <0 khi arg1 đứng trước arg2 và> 0 khi arg1 đứng sau arg2.
Chương trình sau đây sắp xếp một mảng số nguyên từ số nhỏ đến số lớn bằng cách sử dụng hàm qsort ():
#include#include int compare (const void *, const void *);int main() {int arr[5] = {52, 14, 50, 48, 13};int num, width, i;num = sizeof(arr)/sizeof(arr[0]);width = sizeof(arr[0]);qsort((void *)arr, num, width, compare);for (i = 0; i < 5; i++)printf("%d ", arr[ i ]);return 0;}int compare (const void *elem1, const void *elem2) {if ((*(int *)elem1) == (*(int *)elem2)) return 0;else if ((*(int *)elem1) < (*(int *)elem2)) return -1;else return 1;}
Kết quả:
13 14 48 50 52
Sau đây, chúng ta sẽ thảo luận chi tiết chương trình:
- Chúng tôi xác định hàm so sánh bao gồm hai đối số và trả về 0 khi các đối số có cùng giá trị, <0 khi arg1 đứng trước arg2 và> 0 khi arg1 đứng sau arg2. (số nguyên)
- Chúng tôi xác định và khởi tạo một mảng số nguyên Kích thước mảng được lưu trữ trong biến num và kích thước của mỗi phần tử mảng được lưu trữ trong biến chiều rộng bằng cách sử dụng toán tử C được xác định trước sizeof ().
- Chúng tôi gọi hàm qsort và chuyển tên mảng, kích thước, chiều rộng và hàm so sánh được người dùng xác định trước đó để sắp xếp mảng của chúng tôi theo thứ tự tăng dần. So sánh sẽ được thực hiện bằng cách lặp lại mỗi lần hai phần tử mảng cho đến khi toàn bộ mảng sẽ được sắp xếp.
- Chúng tôi in các phần tử của mảng để đảm bảo rằng mảng của chúng tôi được sắp xếp tốt bằng cách lặp lại toàn bộ mảng bằng vòng lặp for.