Con trỏ trong lập trình C

81
Con trỏ trong lập trình C

Mục tiêu bài học

  • Tìm hiểu về con trỏ và khi nào thì sử dụng con trỏ
  • Cách sử dụng biến con trỏ và các toán tử con trỏ
  • Gán giá trị cho con trỏ
  • So sánh con trỏ
  • Con trỏ và mảng một chiều
  • Tìm hiểu cách cấp phát bộ nhớ

Con trỏ là gì?

  • Con trỏ là một biến, nó chứa địa chỉ ô nhớ của một biến khác
  • Nếu một biến chứa địa chỉ của một biến khác, thì biến này được gọi là con trỏ trỏ đến biến thứ hai
  • Con trỏ cung cấp phương thức truy xuất gián tiếp đến giá trị của một phần tử dữ liệu
  • Các con trỏ có thể trỏ đến các biến có kiểu dữ liệu cơ bản như int, char, double, hay dữ liệu tập hợp như mảng hoặc cấu trúc.

Biến con trỏ

  • Khai báo con trỏ: chỉ ra một kiểu cơ sở và một tên biến được đặt trước bởi dấu *
  • Cú pháp khai báo tổng quát:
type *name;

Ví dụ:

int *var2;

Các toán tử con trỏ

  • Hai toán tử đặc biệt được sử dụng với con trỏ:
& và *
  • & là toán tử một ngôi và nó trả về địa chỉ ô nhớ của toán hạng
var2 = &var1;
  • Toán tử * là phần bổ sung của toán tử &. Đây là toán tử một ngôi và nó trả về giá trị chứa trong vùng nhớ được trỏ đến bởi biến con trỏ
temp = *var2;

Gán trị đối với con trỏ

  • Các giá trị có thể được gán cho con trỏ thông qua toán tử &.
ptr_var = &var;
  • Ở đây địa chỉ của var được lưu vào biến ptr_var.
  • Cũng có thể gán giá trị cho con trỏ thông qua một biến con trỏ khác trỏ có cùng kiểu.
ptr_var = &var;
ptr_var2 = ptr_var;

Gán giá trị cho con trỏ

Con trỏ trong lập trình C - 01

  • Có thể gán giá trị cho các biến thông qua con trỏ
*ptr_var = 10;
  • Câu lệnh trên gán giá trị 10 cho biến var nếu ptr_var đang trỏ đến var


So sánh con trỏ

  • Hai con trỏ có thể được so sánh trong một biểu thức quan hệ nếu chúng trỏ đến các biến có cùng kiểu dữ liệu
  • Giả sử ptr_a và ptr_b là hai biến con trỏ trỏ đến các phần tử dữ liệu a và b. Trong trường hợp này, các phép so sánh sau là có thể:

Con trỏ trong lập trình C - 02

Con trỏ và mảng một chiều

  • Địa chỉ của một phần tử mảng có thể được biểu diễn theo hai cách:
    + Sử dụng ký hiệu & trước một phần tử mảng.
    + Sử dụng một biểu thức trong đó chỉ số của phần tử được cộng vào tên của mảng.
int A[100] ;

Con trỏ trong lập trình C - 03

Con trỏ và chuỗi

#include <stdio.h>
#include <string.h>
void main (){
char a, str[81], *ptr;
printf(“\nEnter a sentence:”);
gets(str);
printf(“\nEnter character to search for:”);
a = getche();
ptr = strchr(str,a);
/* return pointer to char*/
printf( “\nString starts at address: %u”,str);
printf(“\nFirst occurrence of the character is at address: %u ”,ptr);
printf(“\n Position of first occurrence(starting from 0)is: % d”, ptr_str);
}

Con trỏ trong lập trình C - 04

Cấp phát bộ nhớ

  • Hàm malloc() là một trong các hàm được sử dụng thường xuyên nhất để thực hiện việc cấp phát bộ nhớ từ vùng nhớ còn tự do.
  • Tham số của hàm malloc() là một số nguyên xác định số bytes cần cấp phát.
#include<stdio.h>
#include<malloc.h>
void main()
{
int *p,n,i,j,temp;
printf("\n Enter number of elements in the array:");
scanf("%d",&n);
p=(int*)malloc(n*sizeof(int));
for(i=0;i<n;++i) {
printf("\nEnter element no. %d:",i+1);
scanf("%d",p+i); }
for(i=0;i<n-1;++i)
for(j=i+1;j<n;++j)
if(*(p+i)>*(p+j)) {
temp=*(p+i);
*(p+i)=*(p+j);
*(p+j)=temp; }
for(i=0;i<n;++i)
printf("%d\n",*(p+i));
}

Hàm free()

  • Hàm free() được sử dụng để giải phóng bộ nhớ khi nó không cần dùng nữa.
  • Cú pháp:
void free(void*ptr);
  • Hàm này giải phóng không gian được trỏ bởi ptr, để dùng cho tương lai.
  • ptr phải được dùng trước đó với lời gọi hàm malloc(), calloc(), hoặc realloc().
#include <stdio.h>
#include <stdlib.h>
/*required for the malloc and free functions*/
int main(){
int number;
int *ptr;
int i;
printf("How many ints would you like store? ");
scanf("%d", &number);
ptr = (int *) malloc (number*sizeof(int));
/*allocate memory */
if(ptr!=NULL) {
for(i=0 ; i<number ; i++){
*(ptr+i) = i;
}
for(i=number ; i>0 ; i--) {
printf("%d\n",*(ptr+(i-1)));
/* print out in reverse order */
}
free(ptr); /* free allocated memory */
return 0;
}
else {
printf("\nMemory allocation failed - not enough memory.\n");
return 1;
}
}
  • calloc tương tự như malloc, nhưng điểm khác biệt chính là mặc nhiên giá trị 0 được lưu vào không gian bộ nhớ vừa cấp phát
  • calloc yêu cầu hai tham số
    + Tham số thứ nhất là số lượng các biến cần cấp phát bộ nhớ
    + Tham số thứ hai là kích thước của mỗi biến
  • Cú pháp:
void *calloc( size_t num, size_t size );
#include <stdio.h>
#include <stdlib.h>
int main() {
float *calloc1, *calloc2;
int i;
calloc1 = (float *) calloc(3, sizeof(float));
calloc2 = (float *)calloc(3, sizeof(float));
if(calloc1!=NULL && calloc2!=NULL){
for(i=0 ; i<3 ; i++){
printf("calloc1[%d] holds %05.5f ",i, calloc1[i]);
printf("\ncalloc2[%d] holds %05.5f", i,*(calloc2+i));
}
free(calloc1);
free(calloc2);
return 0;
}
else{
printf("Not enough memory\n");
return 1;
}
}

Hàm realloc()

  • Có thể cấp phát lại cho một vùng đã được cấp (thêm/bớt số bytes) bằng cách sử dụng hàm realloc, mà không làm mất dữ liệu.
  • realloc nhận hai tham số
    + Tham số thứ nhất là con trỏ tham chiếu đến bộ nhớ
    + Tham số thứ hai là tổng số byte muốn cấp phát
  • Cú pháp:
void *realloc( void *ptr, size_t size );
#include<stdio.h>
#include <stdlib.h>
int main(){
int *ptr;
int i;
ptr = (int *)calloc(5, sizeof(int *));
if(ptr!=NULL) {
*ptr = 1; *(ptr+1) = 2;
ptr[2] = 4; ptr[3] = 8; ptr[4] = 16;
ptr = (int *)realloc(ptr, 7*sizeof(int));
if(ptr!=NULL){
printf("Now allocating more memory...\n");
ptr[5] = 32; /* now it's legal! */
ptr[6] = 64;
for(i=0;i<7;i++) {
printf("ptr[%d] holds %d\n", i, ptr[i]);
}
realloc(ptr,0);
/* same as free(ptr); - just fancier! */
return 0;
}
else {
printf("Not enough memory-realloc failed.\n");
return 1;
}
}
else {
printf("Not enough memory-calloc failed.\n");
return 1;
}
}