Con trỏ trong Ngôn ngữ lập trình C – Phần 5 : Bài 14

Con trỏ trong Ngôn ngữ lập trình C – Phần 5 : Bài 14
access_time 10/11/2019 12:00:00 AM
person Nguyễn Mạnh Hùng

Con trỏ trong Ngôn ngữ lập trình C – Phần 5 : Bài 14

1. Con trỏ hàm

Một hàm (tập hợp các lệnh) cũng giống như dữ liệu: có tên gọi , có địa chỉ lưu trong bộ nhớ và có thể truy nhập đến hàm thông qua tên gọi hoặc địa chỉ của nó. Để truy nhập (gọi hàm) thông qua địa chỉ chúng ta phải khai báo một con trỏ chứa địa chỉ này và sau đó gọi hàm bằng cách gọi tên con trỏ.

Vậy con trỏ hàm là con trỏ trỏ đến địa chỉ của hàm. Giống như các biến con trỏ khác, con trỏ hàm cần khai báo, gán giá trị

1.1. Khai báo con trỏ hàm

Cú pháp Khai báo

return type (* < variable_name) (parameters list);

type : Giá trị trả về của biến con trỏ

variable_name: Tên biến con trỏ hàm.

parameters list: Danh sách các tham số hình thức.

Ví dụ :

Khai báo hàm int add(int a, int b);

Hàm add thực hiện tính tổng 2 số nguyên a, b;

Định nghĩa con trỏ hàm plus trỏ đến địa chỉ của hàm add; Vì con trỏ hàm fpointer mục tiêu là trỏ đến địa chỉ của hàm add do vậy nguyên mẫu của con trỏ hàm fpointer phải giống với nguyên mẫu của hàm add. Điều đó có nghĩa :

int (*fpointer)(int a, int b);

Code thực hiện chương trình


Hình số 1 : Con trỏ hàm

1.2. Khởi tạo giá trị cho con trỏ hàm

Do đặc trưng của hàm, bản thân hàm có chứa thông tin liên quan tới địa chỉ của hàm. Tên hàm chính là địa chỉ hàm khi thực thi chương trình

Ví dụ trên Nếu ta muốn in ra địa chỉ của hàm add, chúng ta thực hiện câu lệnh

printf(“Address of add function : %p”, add);

Khi đó kết quả hiển thị là : 0040149A

Vì vậy để khởi tạo giá trị của con trỏ hàm, cú pháp như sau :

variable_name = function_name;

variable_name : Tên biến con trỏ hàm;

function_name: Tên hàm;

Ví dụ trong chương trình trên chúng ta sử dụng câu lệnh

fpoiner = add;

1.3. Sử dụng con trỏ hàm

Để sử dụng con trỏ hàm ta phải gán nó với tên hàm cụ thể và sau đó bất kỳ nơi nào được phép xuất hiện tên hàm thì ta đều có thể thay nó bằng tên con trỏ.

Lưu ý con trỏ hàm không quan tâm tới nội dung thực thi của hàm mà nó chỉ quan tâm đến nguyên mẫu hàm đó khi khai báo và địa chỉ hàm khi khởi tạo giá trị.

Ví dụ như các thao tác gọi hàm, đưa hàm vào làm tham số hình thức cho một hàm khác … Sau đây là các ví dụ minh hoạ.


Hình số 2 : Con trỏ hàm trỏ đến 2 hàm

Trong ví dụ trên sử dụng con trỏ fpointer trỏ đến 2 hàm add và sub để thực hiện tính tổng 2 số nguyên và tính hiệu 2 số nguyên.

1.4. Mảng con trỏ hàm

Tương tự như biến bình thường các con trỏ hàm giống nhau có thể được gộp lại vào trong một mảng, trong khai báo ta chỉ cần thêm [n] vào sau tên mảng với n là số lượng tối đa các con trỏ. Ví dụ sau minh hoạ cách sử dụng này. Trong ví dụ chúng ta xây dựng 4 hàm cộng, trừ, nhân, chia 2 số thực. Các hàm này giống nhau về kiểu, số lượng đối, … Chúng ta có thể sử dụng 4 con trỏ hàm riêng biệt để trỏ đến các hàm này hoặc cũng có thể dùng mảng 4 con trỏ để trỏ đến các hàm này.

float add(float a, int float){

          return a + b;

}

float sub(float a, float b){

          return a – b;

}

float mul(float a, float b){

          return a * b;

}

float div(float a, float b) {

          if(b != 0){

                    return (float)(a/b);

          }

          return 0;

}

Khai báo mảng con trỏ

float (*p[4])(float, float);

Thiết lập giá trị cho mảng con trỏ

p[0] = add; p[1] = sub; p[2] = mul; p[3] = div;

Sử dụng vòng lặp for để in ra kết quả

for(i = 0; i <= 3; i++){

          printf(“Result : %0.2f \n”, (*p[i])(10.0, 5.0));

{

Vậy sử dụng mảng để lưu các con trỏ hàm là rất đơn giản.

2. Cấp phát bộ nhớ động

Khi tiến hành chạy chương trình, chương trình dịch sẽ bố trí các ô nhớ cụ thể cho các biến được khai báo trong chương trình. Vị trí cũng như số lượng các ô nhớ này tồn tại và cố định trong suốt thời gian chạy chương trình, chúng xem như đã bị chiếm dụng và sẽ không được sử dụng vào mục đích khác và chỉ được giải phóng sau khi chấm dứt chương trình.

Việc phân bổ bộ nhớ như vậy được gọi là cấp phát tĩnh (vì được cấp sẵn trước khi chạy chương trình và không thể thay đổi tăng, giảm kích thước hoặc vị trí trong suốt quá trình chạy chương trình).

Ví dụ nếu ta khai báo một mảng nguyên chứa 1000 số thì trong bộ nhớ sẽ có một vùng nhớ liên tục 2000 bytes hoặc 4000 bytes để chứa dữ liệu của mảng này. Khi đó dù trong chương trình ta chỉ nhập vào mảng và làm việc với một vài số thì phần mảng rỗi còn lại vẫn không được sử dụng vào việc khác. Đây là hạn chế thứ nhất của kiểu mảng. Ở một hướng khác, một lần nào đó chạy chương trình ta lại cần làm việc với hơn 1000 số nguyên. Khi đó vùng nhớ mà chương trình dịch đã dành cho mảng là không đủ để sử dụng. Đây chính là hạn chế thứ hai của mảng được khai báo trước.

Để khắc phục vấn đề nêu trên chúng ta sử dụng hàm malloc() hoặc calloc() để thực hiện cấp phát bộ nhớ động.

Cú pháp các hàm cấp phát bộ nhớ động :

2.1. Hàm malloc

Nguyên mẫu hàm malloc

void* malloc(size_t size)

hàm malloc trả về một con trỏ trỏ đến vùng bộ nhớ có kích cỡ (size_t) trên head. Trong trường hợp lỗi hàm malloc sẽ trả về con trỏ NULL;

Lưu ý : theo nguyên mẫu con trỏ trả về của hàm malloc là void vì vậy có thể áp dụng cho việc cấp phát động bộ nhớ với các kiểu dữ liệu khác nhau mà C hỗ trợ.

Ví dụ :

Sử dụng hàm malloc để cấp phát bộ nhớ cho con trỏ ip có kiểu int;

int *ip;

*ip =(int *) malloc(sizeof(int));

if(ip == NULL) {

 printf(“out of memory\n”);

 exit(0);         /* ‘return’ may be used*/

}

Sử dụng ip == NULL để kiểm tra quá trình cấp phát bộ nhớ cho con trỏ ip.

Sử dụng sizeof(type); để lấy thông tin kích cỡ của dữ liệu tương ứng có kiểu dữ liệu của con trỏ.

2.2. Hàm calloc

Mục đích hàm calloc cũng là hàm cấp phát bộ nhớ động.

Cú pháp hàm calloc

void * calloc(size_t count, size_t eltsize);

trong đó :

count : Số lượng các phần tử cần cấp phát.

eltsize: Kích cỡ của phần tử.

Hàm trả về con trỏ trỏ đến địa chỉ bộ nhớ được cấp phát.

Ví dụ :

int *ptr = calloc(10, sizeof(int));

2.3. Hàm free

Hàm được sử dụng để giải phóng bỗ nhớ đã được cấp phát.

Cú pháp hàm free

void free(void *ptr);

*ptr : Tham số truyền vào của hàm free, ptr trỏ đến vùng bộ nhớ đã được cấp phát bằng hàm malloc và calloc.

Ví dụ : Sử dụng hàm malloc và free phục vụ việc cấp phát bộ nhớ của mảng int với 10 phần tử :


Hình số 3 : malloc và free

Ví dụ sử dụng calloc và free

Sử dụng hàm calloc để cấp phát bộ nhớ mảng số nguyên 10 phần tử, tính tổng mảng số nguyên. Sau đó giải phóng bộ nhớ đã cấp phát.

Code minh họa


Hình số 4 : calloc và free


vertical_align_top
share
Chat...