Con trỏ hàm trong lập trình C kèm ví dụ

Con trỏ mang lại nhiều khả năng cho các hàm 'C' mà chúng ta 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 giờ đây có thể xử lý dữ liệu thực tế thay vì bản sao dữ liệu.

Để sửa đổi giá trị thực của biến, câu lệnh gọi chuyển địa chỉ tới tham số con trỏ trong hàm.

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 100
m is 100, n is 25

Ví dụ về con trỏ hàm

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ề quá trình chương trình:

  1. Chúng ta khai báo hàm chịu trách nhiệm hoán đổi hai giá trị biến, hàm này lấy 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.
  2. 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 đó chúng ta in giá trị của chúng tương ứng.
  3. Chúng ta gọi hàm swap() bằng cách chuyển địa chỉ của hai biến làm đối số bằng ký hiệu dấu và. Sau đó, chúng tôi in các giá trị hoán đổi mới của các biến.
  4. Tại đây chúng ta định nghĩa 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 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.
  5. Lưu nội dung của biến đầu tiên được trỏ bởi 'a' trong biến tạm thời.
  6. Lưu biến thứ hai được trỏ bởi b vào biến đầu tiên được trỏ bởi a.
  7. 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 có tham số mảng

Trong C, chúng ta không thể truyền mảng theo giá trị cho hàm. Trong khi đó, tên mảng là một con trỏ (địa chỉ) nên chúng ta chỉ truyền tên mảng cho hàm, tức là truyền con trỏ tới mảng.

Ví dụ, chúng ta 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ó

Các hàm có tham số mảng

  1. 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 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ử mảng (sử dụng ký hiệu p[k]) và chúng ta tích lũy tổng trong một biến cục bộ sẽ được trả về sau khi lặp lại toàn bộ mảng phần tử.
  2. Chúng ta khai báo và khởi tạo một mảng số nguyên có năm phần tử số nguyên. Chúng tôi 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 add_array()gọi hàm làm đố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ỏ tới một mảng, như trong chương trình sau:

#include <stdio.h>
int * 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:

1
2
3
4
5

Và ở đây, chúng ta sẽ thảo luận về các chi tiết của chương trình

Các hàm trả về một mảng

  1. Chúng ta định nghĩa và khai báo một hàm trả về một địa chỉ mảng chứa một giá trị số nguyên và không nhận bất kỳ đối số nào.
  2. Chúng ta khai báo một con trỏ số nguyên nhận mảng hoàn chỉnh được tạo sau khi hàm được gọi và chúng ta 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 xác định để lưu trữ địa chỉ mảng được hàm trả về. 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ỉ ở 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 * , câu lệnh chung của 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) rất quan trọng vì nếu không có chúng, trình biên dịch sẽ cho rằng function_name đang trả về một con trỏ kiểu return_type.
Sau khi định nghĩa 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, định nghĩa một con trỏ hàm, gán con trỏ hàm cho hàm thông thường và sau đó gọi hàm thông qua con trỏ:

#include <stdio.h>
void 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:

Hi
Hi
Hi

Con trỏ hàm trong C

  1. Chúng ta định nghĩa và khai báo một hàm tiêu chuẩn in ra văn bản Hi k lần được biểu thị bằng tham số lần khi hàm được gọi
  2. Chúng tôi xác định một hàm con trỏ (với khai báo đặc biệt) nhận tham số nguyên và không trả về bất cứ thứ gì.
  3. Chúng ta khởi tạo hàm con trỏ bằng Hi_function, nghĩa là con trỏ trỏ tới Hi_function().
  4. Thay vì gọi hàm tiêu chuẩn bằng cách gắn các đối số vào tên hàm, chúng ta chỉ gọi hàm con trỏ bằng cách chuyển số 3 làm đối số, thế là xong!

Hãy nhớ rằng tên hàm trỏ đến địa chỉ bắt đầ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_function và (*funptr)(3) là chính xác.
LƯU Ý: Việc chèn toán tử địa chỉ & và toán tử gián tiếp * trong quá trình gán chức năng và gọi hàm là không quan trọng.

Mảng con trỏ hàm

Một mảng các 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:

#include <stdio.h>
int 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 48
Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: 2
624

Ở đây, chúng tôi thảo luận về các chi tiết của chương trình:

Mảng con trỏ hàm

  1. Chúng tôi tuyên bố và xác định bốn chức năng 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.
  2. Chúng ta khai báo 4 số nguyên để xử lý các toán hạng, loại phép toán và kết quả tương ứng. Ngoài ra, chúng ta 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ố số nguyên và trả về một giá trị số nguyên.
  3. Chúng ta gán và khởi tạo từng phần tử mảng với 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 thực hiện phép nhân.
  4. Chúng tôi tìm kiếm toán hạng và loại thao tác từ người dùng gõ bằng bàn phím.
  5. Chúng tôi gọi phần tử mảng thích hợp (Con trỏ hàm) bằng các đối số và chúng tôi lưu trữ kết quả do hàm thích hợp tạo ra.

Lệnh int (*ope[4])(int, int); định nghĩa mảng các 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 được 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ỏ void được sử dụng trong quá trình khai báo hàm. Chúng tôi sử dụng giấy phép loại trả về void * để trả về bất kỳ loại 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 vào một hàm thì 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:

#include <stdio.h>
void* 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

Ở đây, chúng ta sẽ thảo luận về chi tiết chương trình:

Các hàm sử dụng con trỏ void

  1. Chúng ta định nghĩa và khai báo một hàm trả về một giá trị số nguyên và lấy đị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 giá trị khối của biến nội dung (x) được trỏ bởi con trỏ num và vì nó là một con trỏ void nên chúng tôi phải nhập kiểu dữ liệu số nguyên bằng cách sử dụng một con trỏ ký hiệu cụ thể (* kiểu dữ liệu) và chúng tôi trả về giá trị khối.
  2. 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.”
  3. Chúng ta gọi hàm khối bằng cách truyền địa chỉ toán hạng và xử lý giá trị trả về trong biến kết quả

Con trỏ hàm làm đố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 nó”.
Trong tệp tiêu đề stdlib.h, hàm Quicksort “qsort()” sử dụng kỹ thuật này, đâ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 : làm trống con trỏ tới mảng.
  • size_t num : Số phần tử mảng.
  • size_t width Kích thước phần tử.
  • int (*so sánh (const void *, const void *) : con trỏ hàm 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 hàm qsort():

#include <stdio.h>
#include <stdlib.h>
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

Ở đây, chúng ta sẽ thảo luận về chi tiết chương trình:

Con trỏ hàm làm đối số

  1. Chúng ta xác định hàm so sánh 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. Các tham số là kiểu con trỏ void được truyền tới kiểu dữ liệu mảng thích hợp (số nguyên)
  2. Chúng ta định nghĩa và khởi tạo một mảng số nguyên Kích thước mảng được lưu trữ trong num biến và kích thước của từng phần tử mảng được lưu trữ trong biến chiều rộng bằng cách sử dụng sizeof() được xác định trước Toán tử C.
  3. Chúng tôi gọi là qsort và truyề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 ta theo thứ tự tăng dần. Việc so sánh sẽ được thực hiện bằng cách lấy vào mỗi lần lặp hai phần tử mảng cho đến khi toàn bộ mảng được sắp xếp.
  4. Chúng tôi in các phần tử 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 cách sử dụng vòng lặp for.

Bản tin Guru99 hàng ngày

Bắt đầu ngày mới của bạn với những tin tức AI mới nhất và quan trọng nhất hiện nay.