Tải bản đầy đủ (.pdf) (19 trang)

tìm hiểu về macro giới thiệu một sôố hàm cầốp phát bộ nhớ động trong c

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (660.5 KB, 19 trang )

<span class="text_page_counter">Trang 1</span><div class="page_container" data-page="1">

<b>Mục lục: </b>

Phầần 1: Tìm hi u vềầ Macro trong ngơn ng l p trình Cểữ ậ...2

I. Macro...2

II. Hàm(Function)...4

III. Hàm inline (Inline function)...5

Phầần 2: Gi i thi u m t sôố hàm cầốp phát b nh đ ng trong ớệộộớ ộC...8

</div><span class="text_page_counter">Trang 2</span><div class="page_container" data-page="2">

<b>Phần 1: Tìm hiểu về Macro trong ngơn ngữ lập trình CI.Macro</b>

<b>Macro được dùng để chỉ những hàm được viết ở phần Preprocessor (là các </b>

directive (chỉ thị), cung cấp chỉ lệnh tới bộ biên dịch để tiền xử lý thông tin trước khi bắt đầu biên dịch thực sự) thay vì đặt nó vào trong phần thực thi của file nguồn,mặt khác khi nói đến macro có 1 nghĩa khác nữa, ám chỉ đến tất cả những phần định nghĩa được viết trong phần Preprocessor. Trong quá trình tiền xử lí (pre-processor), các macro được sử dụng trong chương trình được thay thế bởi các khối câu lệnh tương ứng. Ta có thể định nghĩa macro bằng lệnh #define.

<i><b>Ví dụ:</b></i>

</div><span class="text_page_counter">Trang 3</span><div class="page_container" data-page="3">

<small>// C program to illustrate macros</small>

<small> // Print the value of macro defined</small>

<small> printf("The value of LIMIT" " is %d",</small>

<small> LIMIT); return0;}</small>

Ta có đầu ra: Giá trị của LIMIT là 5.

Các macro giống như một đối tượng với một văn bản thay thế: Khi bộ tiền xử lý gặp lệnh này, bất kỳ sự xuất hiện nào nữa của định danh macro sẽ được thay thế bằng một đoạn văn bản mà chúng ta đã định nghĩa trước đó. Thơng thường thì tên định danh của macro sẽ được ghi bằng các chữ in hoa, sử dụng dấu gạch dưới để thể hiện khoảng trắng.

<i><b>Ví dụ:</b></i>

</div><span class="text_page_counter">Trang 4</span><div class="page_container" data-page="4">

<i><b>Chương trình chạy in ra: My name is: Alex</b></i>

Các macro cũng có thể giống như đối tượng mà khơng có văn bản thay thếVí dụ:

Hàm(Function) là 1 khối lệnh thực hiện một chức năng nào đó.

<i><b>Ví dụ: Hàm tìm giá trị lớn nhất của 1 số nguyên</b></i>

</div><span class="text_page_counter">Trang 5</span><div class="page_container" data-page="5">

<b>Ta có kết quả đầu ra: </b>

<i><b>Ưu nhược điểm của Macro và Hàm: </b></i>

Việc định nghĩa đơn giản hơn

khơng thể debug tìm lỗi của macro

trong thời gian thực thi. <sup>Debug đơn giản, dễ bắt lỗi</sup>Macro không cần quan tâm kiểu dữ

liệu của tham số và kiểu trả về. Như ví dụ trên, chúng ta có thể truyền kiểu int,float.

Phải chỉ rõ kiểu dữ liệu của tham số và giá trị trả về

Macro tạo ra các inline code, thời gian xử lí inline code ngắn hơn thời gian gọihàm

Chương trình mất thời gian dịch từ vùng nhớ hàm được lưu trữ sang vùng nhớ goi hàm.

Giả sử macro được gọi 20 lần trong chương trình, 20 dịng code sẽ được chèn vào chương trình trong q trình tiền xử lí. Điều này làm cho kích thướccủa chương trình (.EXE, .DLL, .LIB,…) phình to ra.

Giả sử 1 hàm được gọi 20 lần, sẽ chỉ có1 bản copy của hàm trong chương trình. Kích thước chương trình nhỏ hơnsử dụng macro.

<b>III.Hàm inline (Inline function)</b>

</div><span class="text_page_counter">Trang 6</span><div class="page_container" data-page="6">

Hàm inline là một hàm bình thường được định nghĩa bởi từ khoá <b>inline. </b>Hàm nội tuyến (inline) là một hàm ngắn được mở rộng bởi trình biên dịch. Và các đối sốcủa nó chỉ được đánh giá một lần. Hàm nội tuyến là các hàm có độ dài ngắn được tự động tạo thành các hàm nội tuyến mà khơng cần sử dụng từ khố nội tuyến bên trong lớp.

Cú pháp một hàm inline:

Inline return_type function_name (tham số){

//mã hàm nội tuyến}

Ví dụ:

</div><span class="text_page_counter">Trang 7</span><div class="page_container" data-page="7">

<b>Đầu ra: </b>

Max (100, 1000): 1000Max (20, 0): 20

<b>Sự khác nhau giữu Inline và Macro trong C++:</b>

Trong khi macro không thể truy cập các thành viên dữ liệu của lớp.

Trong trường hợp hàm nội tuyến,

chương trình có thể dễ dàng gỡ lỗi. <sup>Trong khi trong trường hợp macro, </sup>chương trình khơng thể dễ dàng gỡ lỗi.Trong trường hợp nội dòng, các đối số

chỉ được đánh giá một lần. <sup>Trong khi trong trường hợp macro, các </sup>đối số được đánh giá mọi lúc bất cứ khinào macro được sử dụng trong chương trình.

Trong C ++, nội tuyến có thể được định nghĩa bên trong lớp hoặc bên ngoài lớp.

Trong khi macro là tất cả thời gian được xác định ở đầu chương trình.Trong C ++, bên trong lớp, các hàm có

độ dài ngắn sẽ tự động trở thành các hàm nội tuyến.

Macro được xác định cụ thể.Inline không được sử dụng rộng rãi

như macro. <sup>Macro được sử dụng rộng rãi.</sup>Inline khơng được sử dụng trong lập

trình cạnh tranh. <sup>Macro được sử dụng rất nhiều trong </sup>lập trình cạnh tranh.Hàm inline được kết thúc bằng dấu

ngoặc nhọn ở cuối

Trong khi macro không được kết thúc bằng bất kỳ ký hiệu nào, nó được kết thúc bằng một dòng mới.

</div><span class="text_page_counter">Trang 8</span><div class="page_container" data-page="8">

<b>Phần 2: Giới thiệu một số hàm cấp phát bộ nhớ động trong C</b>

Mỗi khi tạo ra một biến nào đó, trình biên dịch sẽ đưa ra 1 địa chỉ để lưu giữbiến đó. Khi chúng ta sử dụng biến có thể truy cập bằng tên biến hoặc con trỏ. Việccấp phát như vậy gọi là cấp phát tĩnh. Khi cấp phát tĩnh, ô nhớ đó sẽ tồn tại từ khi chương trình hoạt động tới khi chương trình kết thúc. Khi phải khai báo 1 mảng màchưa rõ phải sử dụng kích thước là bao nhiêu. Vậy thì nếu cấp phát tĩnh bộ nhớ chomảng đó sẽ xảy ra 2 vấn đề: Thiếu kích thước dẫn tới lưu thiếu bộ nhớ hoặc thừa kích thước dẫn tới lãng phí bộ nhớ Vậy nên chúng ta cần phải sử dụng cấp phát bộ nhớ động trong trường hợp này. Cấp phát động bộ nhớ chính là việc cấp phát/giải phóng, thay đổi kích thước bộ nhớ một cách linh hoạt. Giúp chúng ta điềukhiển được việc sử dụng bộ nhớ của chương trình.

<b>Sự giống và khác nhau của việc cấp phát động và cấp phát tĩnh</b>

<b>Cấp phát bộ nhớ tĩnhCấp phát bộ nhớ động</b>

Bộ nhớ được cấp phát trước khi chạy chương trình (trong quá trình biên dịch)

Bộ nhớ được cấp phát trong q trình chạy chương trình.

</div><span class="text_page_counter">Trang 9</span><div class="page_container" data-page="9">

Khơng thể cấp phát hay phân bổ lại bộ nhớ trong khi chạy chương trình

Cho phép quản lý, phân bổ hay giải phóng bộ nhớ trong khi chạy chương trình

Vùng nhớ được cấp phát và tồn tại cho

đến khi kết thúc chương trình <sup>Vùng nhớ được cấp phát và tồn tại cho </sup>đến khi kết thúc chương trìnhChương trình chạy nhanh hơn so với

cấp phát động

Chương trình chạy chậm hơn so với cấp phát tĩnh

Tốn nhiều không gian bộ nhớ hơn Tốn nhiều không gian bộ nhớ hơn

Để cấp phát vùng nhớ động cho biến con trỏ trong ngôn ngữ C, bạn có thể sử dụng hàm malloc() hoặc hàm calloc(). Sử dụng hàm feee() để giải phóng bộ nhớ đãcấp phát khi không cần sử dụng, sử dụng realloc() để thay đổi (phân bổ lại) kích thước bộ nhớ đã cấp phát trong khi chạy chương trình.

<b>I.Sử dụng hàm malloc()</b>

Từ malloc là đại diện cho cụm từ memory allocation (dịch: cấp phát bộ nhớ).Hàm malloc() thực hiện cấp phát bộ nhớ bằng cách chỉ định số byte cần cấp phát. Hàm này trả về con trỏ kiểu void cho phép chúng ta có thể ép kiểu về bất cứ kiểu dữ liệu nào.

<b>Cú pháp của hàm malloc(): ptr = (castType*) malloc(size);</b>

Trong ví dụ trên, hàm calloc() thực hiện cấp phát 100 ô nhớ liêntiếp và mỗi ô nhớ có kích thước là số byte của kiểu int, như vậy ở ví dụ trên thì hàm cấp phát cho con trỏ kiểu int, với kích thước là 100*4 = 400byte, vì 1 int có kích thước là 4 byte. Hàm này cũng trả về con trỏ chứa giá trị là địa chỉ của byte đầu tiên trong khối bộ nhớ vừa cấp phát.Trong trường hợp không thể cấp phát bộ nhớ, nó sẽ trả về một con trỏ NULL.

</div><span class="text_page_counter">Trang 10</span><div class="page_container" data-page="10">

<b>II.Sử dụng hàm calloc()</b>

Từ calloc đại diện cho cụm từ contiguous allocation (dịch: cấp phát liên tục).Hàm malloc() khi cấp phát bộ nhớ thì vùng nhớ cấp phát đó khơng được khởi tạo giá trị ban đầu. Trong khi đó, hàm calloc() thực hiện cấp phát bộ nhớ và khởi tạo tất cả các ơ nhớ có giá trị bằng 0. Vì thế nên hàm calloc sẽ cần thời gian thực thi lâu hợn malloc()

Hàm calloc() nhận vào 2 tham số là số ô nhớ muốn khởi tạo và kích thước của 1ơ nhớ.

<b>Cú pháp của hàm calloc(): ptr = (castType*)calloc(n, size); (với n là số lượng phần tử, size là kích thước mỗi phần tử)</b>

Hàm calloc được định nghĩa như sau: void* ICACHE_RAM_ATTR calloc(size_t count, size_t size)

Giá trị trả về là con trỏ void, tham số truyền vào là số lượng phần tử và kích thước của phần tử.

VD: <small>#include <stdio.h></small>

<small>#include <stdlib.h></small>

<small>int main(){ i nint, ; int *;</small>

<small> printf("Nhap so phan tu: \n"); scanf("%d",& );n a = ( *)intcalloc n,(sizeof int( )); printf("Nhap %d so: \n",n); for( =0 i i n i; < ; ++ ) {</small>

<small> scanf("%d",&a[i]); }</small>

<small> printf("Cac so vua nhap la: \n");</small>

</div><span class="text_page_counter">Trang 11</span><div class="page_container" data-page="11">

<small> printf("%d ",a i[ ]); }</small>

<small> return( );0}</small>

Ta có kết quả chạy:

<b>III.Sử dụng hàm free()</b>

Việc cấp phát bộ nhớ động trong C dù sử dụng malloc() hay calloc() thì chúng cũng đều khơng thể tự giải phóng bộ nhớ. Bạn cần sử dụng hàm free() để giải phóng vùng nhớ.

<b>Cú pháp: free(ptr); // ptr là con trỏ</b>

Lệnh này sẽ giải phóng vùng nhớ mà con trỏ ptr đã được cấp phát. Giải phóng ởđây có nghĩa là trả lại vùng nhớ đó cho hệ điều hành và hệ điều hành có thể sử dụng vùng nhớ đó vào việc khác nếu cần. Nếu khơng giải phóng nó thì nó sẽ tồn tại cho tới khi chương trình kết thúc. Điều này sẽ rất nguy hiểm nếu chương trình liên tục cấp phát các vùng nhớ mới và sẽ gây ra hiện tượng tràn bộ nhớ.

Ví dụ sử dụng hàm malloc() và free()

<small>#include <stdio.h></small>

<small>// Thư viện này cần để cấp phát bộ nhớ động</small>

<small>#include <stdlib.h></small>

<small>intmain(){</small>

</div><span class="text_page_counter">Trang 12</span><div class="page_container" data-page="12">

<small> // hàm malloc sẽ trả về con trỏ NULL</small>

<small> printf("Tong = %d",sum);</small>

<small> // Giải phóng vùng nhớ cho con trỏ</small>

<small> free(ptr);</small>

<small> return0;}</small>

</div><span class="text_page_counter">Trang 13</span><div class="page_container" data-page="13">

<b>IV.Sử dụng hàm realloc()</b>

Nếu việc cấp phát bộ nhớ động không đủ hoặc cần nhiều hơn mức đã cấp phát, bạn có thể thay đổi kích thước của bộ nhớ đã được cấp phát trước đó bằng cách sử dụng hàm realloc().

<b>Cú pháp của realloc(): ptr = realloc(ptr, n); </b>

Hàm này thực hiện cấp phát vùng nhớ mới cho con trỏ ptr. Vùng nhớ mới đó sẽ có kích thước mới là n bytes.

Hàm này cũng trả về con trỏ chứa giá trị là địa chỉ của byte đầu tiên trong vùng nhớ mới. Hàm này sẽ cố gắng mở rộng số ơ nhớ ra phía sau nếu có thể để giữ nguyên giá trị của con trỏ ban đầu. Trong trường hợp phải đổi sang một vùng nhớ khác, hàm realloc() cũng sẽ mang theo giá trị đã có ở vùng nhớ cũ sang vùng nhớ mới và giải phóng ln vùng nhớ cũ (đọc thêm tài liệu số 2). Trong trường hợp khơng thể, nó sẽ trả về con trỏ NULL giống như <b>malloc()</b> và <b>calloc().</b>

<b>Ví dụ: </b>

#include <stdio.h>#include <stdlib.h>

int main(){

int *ptr, i n1,n2;

printf("Nhap so luong phan tu: "); scanf("%d", &n1);

ptr=(int*)malloc(n1 *sizeof(int));

printf("Dia chi cua vung nho vua cap phat: %u",ptr);

printf("\nNhap lai so luong phan tu: "); scanf("%d", &n2);

// phân bổ lại vùng nhớ

ptr=(int*)realloc(ptr,n2 *sizeof(int));

</div><span class="text_page_counter">Trang 14</span><div class="page_container" data-page="14">

printf("Dia chi cua vung nho duoc cap phat lai: %u",ptr); // giải phóng

free(ptr); return0;}

Kết quả chạy chương trình:

<b>V.Lời kết: </b>

Việc sử dụng phương thức calloc sẽ an toàn hơn malloc trong lập trình vì vùng nhớ cấp phát động sẽ được gán giá trị bằng 0 thay vì giá trị rác như calloc. Tuy nhiên việc thêm 1 bước gán giá trị các ô nhớ bằng 0 này cũng sẽ khiến nó bị chậm hơn so với malloc do phải thực hiện thêm thao tác.

Sử dụng realloc và free một cách linh hoạt sẽ giúp các bạn điều khiển sự tăng giảm của bộ nhớ 1 cách dễ dàng

<b>Phần 3: Bài tập lập trình</b>

</div><span class="text_page_counter">Trang 15</span><div class="page_container" data-page="15">

<i><b>Bài 1: Sử dụng mảng cấp phát động để thực hiện phép nhân ma trận</b></i>

#include <conio.h>#include<stdio.h>

//C?ng hai ma tr?n

void AddMatrix(int *A,int *B,int*C,int M,int N) {

for(int I=0;I<M*N;++I) C[I] = A[I] + B[I]; }

//C?p phát vùng nh? cho ma tr?n

int AllocMatrix(int **A,int M,int N) // chú ý : ** {

*A = new int [M*N]; if (*A == NULL) return 0; return 1; }

//Gi?i phóng vùng nh? void FreeMatrix(int *A) {

if (A!=NULL) delete [] A; }

</div><span class="text_page_counter">Trang 16</span><div class="page_container" data-page="16">

//Nh?p các giá tr? c?a ma tr?n

void InputMatrix(int *A,int M,int N,char Symbol) {

for(int I=0;I<M;++I) for(int J=0;J<N;++J){

printf("\n %c [%d][%d] = ", Symbol,I,J);scanf("%d",&A[I*N+J]);

//Hi?n th? ma tr?n

void DisplayMatrix(int *A,int M,int N) {

for(int I=0;I<M;++I) {

for(int J=0;J<N;++J) printf("%7d",A[I*N+J]); printf("\n");

} }

void NhanMT(int *A, int *B, int *D, int hang, int cot) {for (int i = 0; i < hang; i++) {

for (int j = 0; j < cot; j++) {D[i * cot + j] = 0;

for (int k = 0; k < cot; k++) {

</div><span class="text_page_counter">Trang 17</span><div class="page_container" data-page="17">

D[i * cot + j] += A[i * cot + k] * B[k * cot + j];}

// void NhanMT (int *A, int *B, int *D, int M, int N){// for (int i=0 ; i< M; i++){

int M,N;

int *A = NULL,*B = NULL,*C = NULL, *D=NULL; printf("\n Nhap so dong cua ma tran: "); scanf("%d",&M); printf("\n Nhap so cot cua ma tran: "); scanf("%d",&N);//C?p phát vùng nh? cho ma tr?n A

if (!AllocMatrix(&A,M,N)) {

printf("\n Khong con du bo nho! ");

</div><span class="text_page_counter">Trang 18</span><div class="page_container" data-page="18">

return 1; }

//C?p phát vùng nh? cho ma tr?n B if (!AllocMatrix(&B,M,N)) {

printf("\n Khong con du bo nho! "); FreeMatrix(A);//Gi?i phóng vùng nh? A return 1;

}

//C?p phát vùng nh? cho ma tr?n C if (!AllocMatrix(&C,M,N))

{

printf("\n Khong con du bo nho! "); FreeMatrix(A);//Gi?i phóng vùng nh? A FreeMatrix(B);//Gi?i phóng vùng nh? B return 1;

}

if (!AllocMatrix(&D,5,5)) {

printf("\n Khong con du bo nho! "); FreeMatrix(A);//Gi?i phóng vùng nh? A FreeMatrix(B);//Gi?i phóng vùng nh? B FreeMatrix(C);//Gi?i phóng vùng nh? C return 1;

}

</div><span class="text_page_counter">Trang 19</span><div class="page_container" data-page="19">

printf("\n Nhap ma tran thu 1 "); InputMatrix(A,M,N,'A'); printf("\n Nhap ma tran thu 2 "); InputMatrix(B,M,N,'B'); printf("\n Ma tran thu 1\n"); DisplayMatrix(A,M,N); printf("\n Ma tran thu 2\n"); DisplayMatrix(B,M,N); AddMatrix(A,B,C,M,N); printf("\n Tong hai ma tran\n"); DisplayMatrix(C,M,N); NhanMT(A,B,D,M,N); printf("\nTich hai ma tran\n"); DisplayMatrix(D,M,N);

FreeMatrix(A);//Gi?i phóng vùng nh? A FreeMatrix(B);//Gi?i phóng vùng nh? B FreeMatrix(C);//Gi?i phóng vùng nh? C

FreeMatrix(D);//Gi?i phóng vùng nh? C return 0;

}

</div>

×