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
Trang 1Mục lục:
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
I S d ng hàm malloc() ử ụ 9
II S d ng hàm calloc() ử ụ 9
III S d ng hàm free() ử ụ 11
IV S d ng hàm realloc() ử ụ 12
V L i kềốt: ờ 13
Phầần 3: Bài t p l p trình ậ ậ 14
Trang 2Phần 1: Tìm hiểu về Macro trong ngôn ngữ lập trình C
I Macro
Macro được dùng để chỉ những hàm được viết ở phần Preprocessor (là các
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
Ví dụ:
Trang 3// C program to illustrate macros
#include <stdio.h>
// Macro definition
#define LIMIT 5
// Driver Code
int main()
{
// Print the value of macro defined
printf("The value of LIMIT"
" is %d",
LIMIT);
return 0;
}
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
Ví dụ:
Trang 4Chương trình chạy in ra: My name is: Alex
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ụ:
define USE_YEN
Các macro của biểu mẫu này hoạt động như sau: mọi sự xuất hiện tiếp theo của định danh macro này(USE_YEN) sẽ bị xóa và được thay thế bởi không có gì
Phạm vi định nghĩa của macro: Các chỉ thị sẽ được xử lý trước khi biên dịch,
từ trên xuống dưới của từng file Khi bộ tiền xử lý kết thúc, tất cả các định danh được định nghĩa từ file đó sẽ bị loại bỏ Điều này có nghĩa là các chỉ thị chỉ có giá trị từ điểm định nghĩa đến cuối file mà chúng được định nghĩa Các chỉ thị được định nghĩa trong một file code không có tác động đến các file code khác trong cùng một dự án
II Hàm(Function)
Hàm(Function) là 1 khối lệnh thực hiện một chức năng nào đó
Ví dụ: Hàm tìm giá trị lớn nhất của 1 số nguyên
Trang 5Ta có kết quả đầu ra:
Ưu nhược điểm của Macro và Hàm:
Việc định nghĩa macro khó hơn định
nghĩa hàm
Nếu không chú ý, chúng ta dễ bị side
effect
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 Debug đơn giản, dễ bắt lỗi
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ọi
hà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 quá trình
tiền xử lí Điều này làm cho kích thước
củ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ơn
sử dụng macro
III Hàm inline (Inline function)
Trang 6Hàm inline là một hàm bình thường được định nghĩa bởi từ khoá inline 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ừ khoá 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ụ:
Trang 7Đầu ra:
Max (100, 1000): 1000
Max (20, 0): 20
Sự khác nhau giữu Inline và Macro trong C++:
Một hàm nội tuyến được xác định bời
từ khoá inline
Trong khi các macro được xác định bời
từ khoá #define Thông qua chức năng nội tuyến, các
thành viên dữ liệu của lớp có thể được
truy cập
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 Trong khi trong trường hợp macro, 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 Trong khi trong trường hợp macro, các đối số được đánh giá mọi lúc bất cứ khi
nà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 Macro được sử dụng rộng rãi. Inline không được sử dụng trong lập
trình cạnh tranh Macro được sử dụng rất nhiều trong 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
Trang 8Phần 2: Giới thiệu một số hàm cấp phát bộ nhớ động trong C
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ệc cấ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ớ cho mả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ều khiển được việc sử dụng bộ nhớ của chương trình
Sự giống và khác nhau của việc cấp phát động và cấp phát tĩnh Cấp phát bộ nhớ tĩnh Cấp phát bộ nhớ động
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 quá trình chạy chương trình
Trang 9Khô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 Vùng nhớ được cấp phát và tồn tại cho đến khi kết thúc chương trình
Chươ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
I Sử dụng hàm malloc()
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
Cú pháp của hàm malloc(): ptr = (castType*) malloc(size);
Trong ví dụ trên, hàm calloc() thực hiện cấp phát 100 ô nhớ liên
tiế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
Trang 10II Sử dụng hàm calloc()
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ớ
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ử)
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:
#include <stdio.h>
#include <stdlib.h>
int main()
{
i n int , ;
int * ;
printf("Nhap so phan tu: \n");
scanf("%d",& ); n
a = ( *) int calloc n, ( sizeof int ( ));
printf("Nhap %d so: \n",n);
for ( =0 i i n i ; < ; ++ )
{
scanf( "%d" ,& a[i]);
}
printf("Cac so vua nhap la: \n");
Trang 11printf("%d ",a i [ ]);
}
return ( ); 0
}
Ta có kết quả chạy:
III Sử dụng hàm free()
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ớ
Cú pháp: free(ptr); // ptr là con trỏ
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()
0
1
2
3
4
5
6
7
8
9
1
#include <stdio.h>
// Thư viện này cần để cấp phát bộ nhớ động
#include <stdlib.h>
int main()
{
int n, i, * ptr, sum = 0 ;
printf( "Nhap so luong phan tu: " );
scanf( "%d" , &n);
ptr = ( int * )malloc(n * sizeof( int ));
Trang 121
2
1
3
1
4
1
5
1
6
1
7
1
8
1
9
2
0
2
1
2
2
2
3
2
4
2
5
2
6
2
7
2
8
2
9
3
0
3
// hàm malloc sẽ trả về con trỏ NULL
if (ptr == NULL )
{
printf( "Co loi! khong the cap phat bo nho." );
exit( 0 );
}
printf( "Nhap cac gia tri: " );
for (i = 0 ; i n; < ++ i)
{
scanf( "%d" , ptr + i);
sum += * (ptr + i);
}
printf( "Tong = %d" , sum);
// Giải phóng vùng nhớ cho con trỏ
free(ptr);
return 0 ;
}
Trang 13IV Sử dụng hàm realloc()
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()
Cú pháp của realloc(): ptr = realloc(ptr, n);
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 luôn 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ư malloc() và calloc().
Ví dụ:
0
1
2
3
4
5
6
7
8
9
1
0
1
1
1
#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));
Trang 141
3
1
4
1
5
1
6
1
7
1
8
1
9
2
0
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:
V Lời kết:
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
Phần 3: Bài tập lập trình
Trang 15Bài 1: Sử dụng mảng cấp phát động để thực hiện phép nhân ma trận
#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;
}
Trang 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++) {
Trang 17D[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++){
// for(int j=0; j < N;j++){
// for (int k =0; k < N; i++){
// D[i*N+j]+= A[i*N+k] * B[k*N+j];
int main()
{
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! ");
Trang 18return 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;
}
Trang 19printf("\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;
}