Con trỏ trong C: Con trỏ trong lập trình C là gì? Các loại

Con trỏ trong C là gì?

Pointer trong C, là biến lưu trữ địa chỉ của biến khác. Một con trỏ cũng có thể được sử dụng để tham chiếu tới một hàm con trỏ khác. Một con trỏ có thể được tăng/giảm, tức là để trỏ đến vị trí bộ nhớ tiếp theo/trước đó. Mục đích của con trỏ là tiết kiệm không gian bộ nhớ và đạt được thời gian thực hiện nhanh hơn.

Cách sử dụng con trỏ trong C

Nếu chúng ta khai báo một biến v kiểu int, v sẽ thực sự lưu trữ một giá trị.

Cách sử dụng con trỏ trong C

v bây giờ bằng 0.

Tuy nhiên, mỗi biến, ngoài giá trị, còn có địa chỉ của nó (hoặc nói một cách đơn giản là vị trí của nó trong bộ nhớ). Địa chỉ có thể được lấy bằng cách đặt dấu và (&) trước tên biến.

Cách sử dụng con trỏ trong C

Nếu bạn in địa chỉ của một biến lên màn hình, nó sẽ trông giống như một số hoàn toàn ngẫu nhiên (hơn nữa, nó có thể khác nhau giữa các lần chạy).

Hãy thử điều này trong thực tế với con trỏ trong ví dụ C

Cách sử dụng con trỏ trong C

Đầu ra của chương trình này là -480613588.

Bây giờ, con trỏ là gì? Thay vì lưu trữ một giá trị, con trỏ sẽ lưu trữ địa chỉ của một biến.


Biến con trỏ

Int *y = &v;

BIẾN ĐỔI ĐIỂM
A giá trị được lưu trữ trong một tên địa chỉ lưu trữ/bộ nhớ A biến việc này điểm đến địa chỉ lưu trữ/bộ nhớ của khác biến

Khai báo một con trỏ

Giống như các biến, con trỏ trong lập trình C phải được khai báo trước khi chúng có thể được sử dụng trong chương trình của bạn. Con trỏ có thể được đặt tên bất cứ thứ gì bạn muốn miễn là chúng tuân theo các quy tắc đặt tên của C. Một khai báo con trỏ có dạng sau.

data_type * pointer_variable_name;

Ở đây,

  • loại dữ liệu là kiểu cơ sở của con trỏ trong các kiểu biến của C và cho biết kiểu biến mà con trỏ trỏ tới.
  • Dấu hoa thị (*: cùng dấu hoa thị được sử dụng cho phép nhân) là toán tử gián tiếp, khai báo một con trỏ.

Hãy xem một số khai báo con trỏ hợp lệ trong hướng dẫn về con trỏ C này:

int    *ptr_thing;            /* pointer to an integer */ 
int *ptr1,thing;/* ptr1 is a pointer to type integer and thing is an integer variable */
double    *ptr2;    /* pointer to a double */
float    *ptr3;      /* pointer to a float */
char    *ch1 ;       /* pointer to a character */
float  *ptr, variable;/*ptr is a pointer to type float and variable is an ordinary float variable */

Khởi tạo một con trỏ

Sau khi khai báo một con trỏ, chúng ta khởi tạo nó giống như các biến chuẩn với một địa chỉ biến. Nếu con trỏ trong lập trình C không được khởi tạo và sử dụng trong một chương trình, kết quả có thể không thể đoán trước và có khả năng gây ra thảm họa.

Để lấy địa chỉ của một biến, chúng ta sử dụng toán tử dấu thăng (&), đặt trước tên của biến có địa chỉ mà chúng ta cần. Khởi tạo con trỏ được thực hiện bằng cú pháp sau.

Cú pháp con trỏ

 pointer = &variable;

Một chương trình đơn giản để minh họa con trỏ được đưa ra dưới đây:

#include <stdio.h>
int main()
{
   int a=10;    //variable declaration
   int *p;      //pointer variable declaration
   p=&a;        //store address of variable a in pointer p
   printf("Address stored in a variable p is:%x\n",p);  //accessing the address
   printf("Value stored in a variable p is:%d\n",*p);   //accessing the value
   return 0;
}

Đầu ra:

Address stored in a variable p is:60ff08
Value stored in a variable p is:10
Operator Ý nghĩa
* Phục vụ 2 mục đích

  1. Khai báo con trỏ
  2. Trả về giá trị của biến được tham chiếu
& Chỉ phục vụ 1 mục đích

  • Trả về địa chỉ của một biến

Các loại con trỏ trong C

Sau đây là những khác biệt Các loại con trỏ trong C:

Con trỏ rỗng

Chúng ta có thể tạo một con trỏ null bằng cách gán giá trị null trong quá trình khai báo con trỏ. Phương pháp này hữu ích khi bạn không có bất kỳ địa chỉ nào được gán cho con trỏ. Con trỏ null luôn chứa giá trị 0.

Chương trình sau minh họa cách sử dụng con trỏ null:

#include <stdio.h>
int main()
{
	int *p = NULL; 	//null pointer
	printf(“The value inside variable p is:\n%x”,p);
	return 0;
}

Đầu ra:

The value inside variable p is:
0

Con trỏ trống

In lập trình C, con trỏ void cũng được gọi là con trỏ chung. Nó không có bất kỳ kiểu dữ liệu tiêu chuẩn nào. Một con trỏ void được tạo bằng cách sử dụng từ khóa void. Nó có thể được sử dụng để lưu trữ địa chỉ của bất kỳ biến nào.

Chương trình sau minh họa cách sử dụng con trỏ void:

#include <stdio.h>
int main()
{
void *p = NULL; 	//void pointer
printf("The size of pointer is:%d\n",sizeof(p));
return 0;
}

Đầu ra:

The size of pointer is:4

Con trỏ hoang dã

Một con trỏ được gọi là con trỏ hoang dã nếu nó không được khởi tạo thành bất kỳ thứ gì. Những loại con trỏ C này không hiệu quả vì chúng có thể trỏ đến một số vị trí bộ nhớ không xác định, điều này có thể gây ra sự cố trong chương trình của chúng ta và có thể dẫn đến chương trình bị treo. Người ta phải luôn cẩn thận khi làm việc với con trỏ hoang dã.

Chương trình sau minh họa cách sử dụng con trỏ hoang dã:

#include <stdio.h>
int main()
{
int *p; 	//wild pointer
printf("\n%d",*p);
return 0;
}

Đầu ra:

timeout: the monitored command dumped core
sh: line 1: 95298 Segmentation fault      timeout 10s main

Các loại con trỏ khác trong 'c' như sau:

  • Con trỏ lơ lửng
  • Con trỏ phức hợp
  • Gần con trỏ
  • Con trỏ xa
  • Con trỏ khổng lồ

Con trỏ truy cập trực tiếp và gián tiếp

Trong C, có hai cách tương đương để truy cập và thao tác một nội dung biến

  • Truy cập trực tiếp: chúng tôi sử dụng trực tiếp tên biến
  • Truy cập gián tiếp: chúng tôi sử dụng một con trỏ tới biến

Hãy hiểu điều này với sự trợ giúp của chương trình dưới đây

#include <stdio.h>
/* Declare and initialize an int variable */
int var = 1;
/* Declare a pointer to int */
int *ptr;
int main( void )
{
/* Initialize ptr to point to var */
ptr = &var;
/* Access var directly and indirectly */
printf("\nDirect access, var = %d", var);
printf("\nIndirect access, var = %d", *ptr);
/* Display the address of var two ways */
printf("\n\nThe address of var = %d", &var);
printf("\nThe address of var = %d\n", ptr);
/*change the content of var through the pointer*/
*ptr=48;
printf("\nIndirect access, var = %d", *ptr);
return 0;}

Sau khi biên dịch chương trình không có lỗi, kết quả là:

Direct access, var = 1
Indirect access, var = 1

The address of var = 4202496
The address of var = 4202496

Indirect access, var = 48

Số học con trỏ trong C

Các hoạt động của con trỏ được tóm tắt trong hình sau

Số học con trỏ trong C
Pointer Operations

Hoạt động ưu tiên (ưu tiên)

Khi làm việc với con trỏ C, chúng ta phải tuân thủ các quy tắc ưu tiên sau:

  • Các toán tử * và & có cùng mức độ ưu tiên như các toán tử một ngôi (phủ định!, tăng++, giảm–).
  • Trong cùng một biểu thức, các toán tử một ngôi *, &,!, ++, – được đánh giá từ phải sang trái.

Nếu con trỏ P trỏ tới một biến X thì * P có thể được sử dụng ở bất cứ nơi nào X có thể được ghi.

Các biểu thức sau đây là tương đương:

int X =10
int *P = &Y;
For the above code, below expressions are true
Biểu hiện Biểu thức tương đương
Y=*P+1

*P=*P+10

*P+=2

++*P

(*P)++

Y=X+1

X=X+10

X+=2

++X

X ++

Trong trường hợp sau, cần có dấu ngoặc đơn: vì các toán tử một ngôi * và ++ được đánh giá từ phải sang trái, nếu không có dấu ngoặc đơn thì con trỏ P sẽ tăng lên, chứ không phải đối tượng mà P trỏ vào.

Bảng dưới đây cho thấy phép toán số học và cơ bản có thể được sử dụng khi xử lý con trỏ C

Operasản xuất Giải thích
Chuyển nhượng int *P1,*P2
P1=P2;
P1 và P2 trỏ đến cùng một biến số nguyên
Tăng và giảm Int *P1;
P1++;P1– ;
Thêm phần bù (Không đổi) Điều này cho phép con trỏ di chuyển N phần tử trong bảng.
Con trỏ sẽ được tăng hoặc giảm N lần số byte của loại biến.
P1+5;

Con trỏ và mảng C kèm ví dụ

Theo truyền thống, chúng ta truy cập các phần tử mảng bằng cách sử dụng chỉ mục của nó, nhưng phương pháp này có thể được loại bỏ bằng cách sử dụng con trỏ. Con trỏ giúp dễ dàng truy cập từng phần tử mảng.

#include <stdio.h>
int main()
{
    int a[5]={1,2,3,4,5};   //array initialization
    int *p;     //pointer declaration
               /*the ptr points to the first element of the array*/

    p=a; /*We can also type simply ptr==&a[0] */
    
    printf("Printing the array elements using pointer\n");
    for(int i=0;i<5;i++)    //loop for traversing array elements
    {
        	printf("\n%x",*p);  //printing array elements
        	p++;    //incrementing to the next element, you can also write p=p+1
    }
    return 0;
}

Đầu ra:

1
2
3
4
5

Thêm một số cụ thể vào một con trỏ sẽ di chuyển vị trí con trỏ đến giá trị thu được bằng phép toán cộng. Giả sử p là một con trỏ hiện đang trỏ đến vị trí bộ nhớ 0 nếu chúng ta thực hiện phép toán cộng sau, p+1 thì nó sẽ thực thi theo cách này:

Con trỏ & Mảng C

Thêm/Tăng con trỏ

Vì p hiện trỏ đến vị trí 0 sau khi thêm 1, giá trị sẽ trở thành 1 và do đó con trỏ sẽ trỏ đến vị trí bộ nhớ 1.

Con trỏ và chuỗi C có ví dụ

Chuỗi là một mảng các đối tượng char, kết thúc bằng ký tự null '\ 0'. Chúng ta có thể thao tác chuỗi bằng cách sử dụng con trỏ. Con trỏ này trong ví dụ C giải thích phần này

#include <stdio.h>
#include <string.h>
int main()
{
char str[]="Hello Guru99!";
char *p;
p=str;
printf("First character is:%c\n",*p);
p =p+1;
printf("Next character is:%c\n",*p);
printf("Printing all the characters in a string\n");
p=str;  //reset the pointer
for(int i=0;i<strlen(str);i++)
{
printf("%c\n",*p);
p++;
}
return 0;
}

Đầu ra:

First character is:H
Next character is:e
Printing all the characters in a string
H
e
l
l
o

G
u
r
u
9
9
!

Một cách khác để xử lý chuỗi là sử dụng mảng con trỏ như trong chương trình sau:

#include <stdio.h>
int main(){
char *materials[ ] = {  "iron",  "copper",  "gold"};
printf("Please remember these materials :\n");
int i ;
for (i = 0; i < 3; i++) {
  printf("%s\n", materials[ i ]);}
  return 0;}

Đầu ra:

Please remember these materials:
iron
copper
gold

Ưu điểm của con trỏ trong C

  • Con trỏ rất hữu ích cho việc truy cập các vị trí bộ nhớ.
  • Con trỏ cung cấp một cách hiệu quả để truy cập các phần tử của cấu trúc mảng.
  • Con trỏ được sử dụng để phân bổ bộ nhớ động cũng như phân bổ bộ nhớ.
  • Con trỏ được sử dụng để tạo thành các cấu trúc dữ liệu phức tạp như danh sách liên kết, đồ thị, cây, v.v.

Nhược điểm của con trỏ trong C

  • Con trỏ có vẻ hơi phức tạp để hiểu.
  • Con trỏ có thể dẫn đến nhiều lỗi khác nhau như lỗi phân đoạn hoặc có thể truy cập vào một vị trí bộ nhớ không cần thiết.
  • Nếu một giá trị không chính xác được cung cấp cho một con trỏ, nó có thể gây ra hỏng bộ nhớ.
  • Con trỏ cũng chịu trách nhiệm rò rỉ bộ nhớ.
  • Con trỏ tương đối chậm hơn so với các biến.
  • Các lập trình viên cảm thấy rất khó khăn khi làm việc với con trỏ; do đó trách nhiệm của người lập trình là phải thao tác con trỏ một cách cẩn thận.

Tổng kết

  • Con trỏ không là gì ngoài một vị trí bộ nhớ nơi lưu trữ dữ liệu.
  • Một con trỏ được sử dụng để truy cập vị trí bộ nhớ.
  • Có nhiều loại con trỏ khác nhau như con trỏ null, con trỏ hoang dã, con trỏ void và các loại con trỏ khác.
  • Con trỏ có thể được sử dụng với mảng và chuỗi để truy cập các phần tử hiệu quả hơn.
  • Chúng ta có thể tạo các con trỏ hàm để gọi hàm một cách linh hoạt.
  • Các phép toán số học có thể được thực hiện trên một con trỏ được gọi là số học con trỏ.
  • Con trỏ cũng có thể trỏ tới hàm, giúp dễ dàng gọi các hàm khác nhau trong trường hợp xác định một mảng con trỏ.
  • Khi bạn muốn xử lý các kiểu dữ liệu biến khác nhau, bạn có thể sử dụng con trỏ void kiểu chữ.