Con trỏ trong C là gì? Chi tiết bản chất của biến con trỏ

Trong bài viết này, bạn sẽ được tìm hiểu các kiến thức liên quan đến biến con trỏ trong C là gì, cách sử dụng và những sai lầm thường gặp khi làm việc với biến con trỏ (lập trình máy tính) thông qua các ví dụ.

1. Biến con trỏ trong C

Các biến con trỏ trong C là một công cụ mạnh mẽ, đặc trưng của lập trình C và C++. Trước khi tìm hiểu về các biến con trỏ, bạn cần hiểu rõ về địa chỉ (address) trong C (ngôn ngữ lập trình).

con tro trong c1.1 Địa chỉ trong lập trình C

Nếu biến var Open trong lập trình của bạn thì và var sẽ biểu lộ địa chỉ của nó trong bộ nhớ .

Tôi từng nhiều lần sử dụng địa chỉ mỗi khi dùng hàm scanf().

scanf ( “ % d ”, và var ) ;
Giá trị do người dùng nhập vào sẽ được tàng trữ lại địa chỉ của biến var. Hãy xem qua ví dụ dưới đây :

#include

int main ( )
{
int var = 5 ;
printf ( “ var : % d \ n ”, var ) ;
/ / Notice the use of và before var
printf ( “ address of var : % p ”, và var ) ;
return 0 ;
}
Đầu ra
var : 5
address of var : 2686778
Tuy nhiên, bạn cũng hoàn toàn có thể nhận được một địa chỉ khác khi chạy đoạn mã trên .

1.2 Biến con trỏ trong C là gì?

Biến con trỏ trong C (còn gọi là biến pointer trong C) là các biến đặc biệt được dùng để lưu trữ địa chỉ, chứ không phải lưu trữ các giá trị bình thường.

1.3 Cách để khai báo biến con trỏ trong C

Cú pháp của biến con trỏ C : int * p ;
Trong ví dụ này, tôi đã khai báo một biến con trỏ p kiểu int .
Bạn cũng có thẻ khai báo biến con trỏ theo những cách khác như sau
int * p1 ;
int * p2 ;
Dưới đây là một ví dụ khác về cách khai báo biến pointer
int * p1, p2 ;
Trong ví dụ này, tôi đã khai báo một biến pointer p1 và một biến thường thì p2 .

1.4 Gán địa chỉ cho biến con trỏ C

Hãy xem qua ví dụ dưới đây
int * pc, c ;
c = 5 ;
pc = và c
Trong ví dụ này, 5 được gán cho biến c. Và địa chỉ của c được gán với biến pointer pc .

1.5 Nhận giá trị từ những nơi mà biến pointer trỏ vào

Để nhận giá trị từ những nơi mà biến pointer trỏ vào, tôi sử dụng toán tử *. Ví dụ :
int * pc, c ;
c = 5 ;
pc = và c
printf ( “ % d ”, * pc ) ; / / Output : 5
Trong ví dụ này, địa chỉ của c được gán cho biến pointer pc. Để nhận giá trị được tàng trữ trong địa chỉ này, tôi đã sử dụng * pc .
Tuy nhiên, pc là một biến pointer, chứ không phải là * pc. Do đó, bạn không hề và cũng không nên làm những điều tương tự như như * pc = và c
* được gọi là toán tử tham chiếu ( khi nó đi chung với những biến pointer ). Nó hoạt động giải trí cùng với những biến pointer và cung ứng giá trị được tàng trữ trong biến pointer đó .

1.6 Thay đổi giá trị mà biến pointer trỏ vào

Hãy xem xét ví dụ dưới đây :
int * pc, c ;
c = 5 ;
pc = và c
c = 1 ;
printf ( “ % d ”, c ) ; / / Output : 1
printf ( “ % d ”, * pc ) ; / / Ouptut : 1
Tôi đã gán địa chỉ của c vào biến pointer pc .
Sau đó, tôi đã đổi khác giá trị của c thành 1. Vì pc và địa chỉ của c là giống nhau nên đầu ra của * pc cũng sẽ là 1 .
Hãy xem xét một ví dụ khác
int * pc, c ;
c = 5 ;
pc = và c
* pc = 1 ;
printf ( “ % d ”, * pc ) ; / / Ouptut : 1
printf ( “ % d ”, c ) ; / / Output : 1
Tôi đã gàn địa chỉ của c vào biến pointer pc .
Sau đó, tôi đổi khác * pc thành 1 bằng cách sử dụng * pc = 1 ;. Vì pc và địa chỉ của c là giống nhau nên c cũng sẽ bằng 1 .
Thêm một ví dụ nữa .
int * pc, c, d ;
c = 5 ;
d = – 15 ;
pc = và c printf ( “ % d ”, * pc ) ; / / Output : 5
pc = và d printf ( “ % d ”, * pc ) ; / / Ouptut : – 15
Ban đầu, địa chỉ của c được gán với biến pointer pc bằng cách sử dụng pc = và c. Bởi vì c bằng 5 nên pc cũng sẽ bằng 5 .
Sau đó, địa chỉ của d được gán với biến pointer pc bằng cách sử dụng pc = và d. Bởi vì d bằng – 15 nên * pc cũng sẽ là – 15 .

1.7 Ví dụ: Cách thức hoạt động của biến pointer trong C

Cùng xem qua ví dụ dưới đây

#include

int main ( )
{
int * pc, c ;
c = 22 ;
printf ( “ Address of c : % p \ n ”, và c ) ;
printf ( “ Value of c : % d \ n \ n ”, c ) ; / / 22
pc = và c
printf ( “ Address of pointer pc : % p \ n ”, pc ) ;
printf ( “ Content of pointer pc : % d \ n \ n ”, * pc ) ; / / 22
c = 11 ;
printf ( “ Address of pointer pc : % p \ n ”, pc ) ;
printf ( “ Content of pointer pc : % d \ n \ n ”, * pc ) ; / / 11
* pc = 2 ;
printf ( “ Address of c : % p \ n ”, và c ) ;
printf ( “ Value of c : % d \ n \ n ”, c ) ; / / 2
return 0 ;
}
Đầu ra
Address of c : 2686784
Value of c : 22
Address of pointer pc : 2686784
Content of pointer pc : 22
Address of pointer pc : 2686784
Content of pointer pc : 11
Address of c : 2686784
Value of c : 2
Giải thích lập trình trên

  1. int* pc, c;

Một biến pointer pc và một biến thông c thường được tạo ra. Cả 2 biến này đều là kiểu int .
Vì lúc đầu, pc và c không được khởi tạo, nên biến pointer pc sẽ không trỏ tới bất kể địa chỉ nào hoặc trỏ đến một địa chỉ ngẫu nhiên. Và, biến c sẽ có địa chỉ nhưng chứa những giá trị rác ngẫu nhiên .

  1.  c = 22;

Gán 22 cho biến c đồng nghĩa tương quan với việc 22 sẽ được lưu trong vị trí bộ nhớ của biến c .

  1.  pc = & c;

Địa chỉ của biến c được gán cho biến pointer pc .

  1. c = 11;

Gán 11 cho biến c .

  1. *pc = 2;

Điều này làm biến hóa giá trị tại vị trí bộ nhớ mà biến pointer pc trỏ vào thành 2 .

2. Những lỗi thường gặp khi làm việc với biến pointer

Giả sử bạn muốn biến pointer pc trỏ đến địa chỉ của c .
int c, * pc ;
/ / pc is address but c is not
pc = c ; / / Error
/ / và c is address but * pc is not
* pc = và c / / Error
/ / both và c and pc are addresses
pc = và c
/ / both c and * pc values
* pc = c ;
Đây là một ví dụ về cú pháp của biến pointer dành cho người mới mở màn học về lập trình C .

#include

int main ( ) {
int c = 5 ;
int * p = và c
printf ( “ % d ”, * p ) ; / / 5
return 0 ;
}
Tại sao khi sử dụng int * p = và c ? lại xảy ra lỗi ?
Bởi vì
int * p = và c
tương tự với
int * p :
p = và c
Trong cả 2 trường hợp trên, tôi đang tạo ra một biến pointer p ( chứ không phải * p ) và gán và c cho nó .
Để tránh gây ra nhầm lẫn, tôi sử dụng câu lệnh như sau :
int * p = và c
Chắc hẳn sau khi xem qua những ví dụ nêu trên, bạn đã hiểu rõ về biến con trỏ trong C. Tiếp theo, bạn sẽ được khám phá về yếu tố những biến pointer trong C tương quan như thế nào đến những mảng .

3. Biến pointer trong C và mảng

Trong phần này, bạn sẽ được khám phá về mối quan hệ giữa mảng và biến pointer trong C. Ngoài ra, bạn cũng sẽ được tìm hiểu và khám phá cách truy vấn vào những thành phần mảng trải qua việc sử dụng biến pointer .
Tuy nhiên, trước khi tìm hiểu và khám phá về mối quan hệ giữa mảng và con trỏ, bạn cần tìm hiểu và khám phá kỹ 2 chủ đề sau :

  • Mảng trong C
  • Biến pointer trong C

3.1 Mối quan hệ giữa mảng và các biến pointer trong C

Mảng trong C là một khối tài liệu tuần tự. Dưới đây là lập trình in ra địa chỉ của những thành phần mảng .

#include

int main ( ) {
int x [ 4 ] ;
int i ;
for ( i = 0 ; i < 4 ; + + i ) { printf ( “ và x [ % d ] = % p \ n ”, i, và x [ i ] ) ; } printf ( “ Address of array x : % p ”, x ) ; return 0 ; } Đầu ra và x [ 0 ] = 1450734448 và x [ 1 ] = 1450734452 và x [ 2 ] = 1450734456 và x [ 3 ] = 1450734460 Address of array x : 1450734448 Các thành phần liên tục của mảng x chênh nhau 4 byte. Bởi vì size của biến int là 4 byte . Địa chỉ của và x [ 0 ] và x là giống nhau. Bởi vì tên của biến x trỏ vào thành phần tiên phong của mảng . Trong ví dụ trên, rõ ràng là và x [ 0 ] tương tự với x. Và x [ 0 ] tương tự với * x . Tương tự ,

  • &x[1] tương đương với x+1 và x[1] tương đương với *(x+1).
  • &x[2] tương đương với x+2 và x[2] tương đương với *(x+2).
  • Về cơ bản, &x[i] tương đương với x+i và x[i] tương đương với *(x+i).

Ví dụ 1: Biến con trỏ trong C và Mảng

#include

int main ( ) {
int i, x [ 6 ], sum = 0 ;
printf ( “ Enter 6 numbers : “ ) ;
for ( i = 0 ; i < 6 ; + + i ) { / / Equivalent to scanf ( “ % d ”, và x [ i ] ) ; scanf ( “ % d ”, x + i ) ; / / Equivalent to sum + = x [ i ]sum + = * ( x + i ) ; } printf ( “ Sum = % d ”, sum ) ; return 0 ; } Khi bạn chạy lập trình này, đầu ra sẽ là Enter 6 numbers : 2 3 4 4 12 4 Sum = 29 Tôi đã khai báo một mảng x gồm 6 thành phần. Để truy vấn vào những thành phần của mảng này, bạn cần sử dụng biến pointer . Trong hầu hết những ngữ cảnh, tên mảng sẽ phân rã thành biến con trỏ. Nói cách khác, tên mảng được quy đổi thành biến pointer .

Đó là lý do vì sao bạn nên sử dụng biến pointer trong C để truy cập vào các phần tử của mảng. Tuy nhiên, bạn cần lưu ý rằng mảng và biến con trỏ C không hề giống nhau.

Có vài trường hợp tên mảng không phân rã thành biến con trỏ C. Xem thông tin cụ thể tại : Khi nào tên mảng không phân rã thành biến pointer ?

Ví dụ 2: Mảng và biến pointer trong C

#include

int main ( ) {
int x [ 5 ] = { 1, 2, 3, 4, 5 } ;
int * ptr ;
/ / ptr is assigned the address of the third element
ptr = và x [ 2 ] ;
printf ( “ * ptr = % d \ n ”, * ptr ) ; / / 3
printf ( “ * ( ptr + 1 ) = % d \ n ”, * ( ptr + 1 ) ) ; / / 4
printf ( “ * ( ptr-1 ) = % d ”, * ( ptr-1 ) ) ; / / 2
return 0 ;
}
Khi bạn chạy lập trình này, đầu ra sẽ là
* ptr = 3
* ( ptr + 1 ) = 4
* ( ptr-1 ) = 2
Trong ví dụ này, và x [ 2 ] là địa chỉ của thành phần thứ 3 được gán với biến pointer ptr. Do đó, khi in * ptr thì đầu ra cũng sẽ hiển thị 3 trên màn hình hiển thị luôn .
Và nếu in * ( ptr + 1 ) thì tôi sẽ nhận được thành phần thứ 4. Tương tự, nếu in * ( ptr-1 ) thì tôi sẽ nhận được thành phần thứ 2 .

4. Biến con trỏ trong C và hàm

4.1 Truyền địa chỉ và biến con trỏ C

Trong phần này, bạn sẽ học được cách truyền địa chỉ và biến pointer dưới dạng đối số vào những hàm trải qua những ví dụ .
Trong lập trình C, bạn hoàn toàn có thể truyền địa chỉ dưới dạng đối số vào những hàm .
Thông qua những biến con trỏ, những địa chỉ này sẽ được đồng ý vào định nghĩa hàm. Bởi vì biến con trỏ trong C được sử dụng để tàng trữ địa chỉ. Cùng xem qua những ví dụ dưới đây :

Ví dụ: Truyền địa chỉ vào hàm

#include

void swap ( int * n1, int * n2 ) ;
int main ( )
{
int num1 = 5, num2 = 10 ;
/ / address of num1 and num2 is passed
swap ( và num1, và num2 ) ;
printf ( “ num1 = % d \ n ”, num1 ) ;
printf ( “ num2 = % d ”, num2 ) ;
return 0 ;
}
void swap ( int * n1, int * n2 )
{
int temp ;
temp = * n1 ;
* n1 = * n2 ;
* n2 = temp ;
}
Khi chạy lập trình, đầu ra sẽ là :
num1 = 10
num2 = 5
Địa chỉ của num1 và num2 được truyền vào hàm swap ( ) bằng cách sử dụng swap ( và num1, và num2 ) ; .
Biến pointer n1 và n2 đồng ý truyền những đối số này vào định nghĩa hàm .
void swap ( int * n1, int * n2 ) {
… ..
}
Khi bạn đổi khác * n1 và * n2 trong hàm swap ( ), num1 và num2 trong hàm main ( ) cũng đổi khác theo .
Trong hàm swap ( ), * n1 và * n2 được hoán đổi. Do đó, num1 và num2 cũng được hoán đổi theo .
Lưu ý, hàm swap ( ) sẽ không trả về bất kỳ giá trị nào bởi kiểu trả về của nó là void .

Ví dụ 2: Truyền biến pointer trong C vào các hàm

#include

void addOne ( int * ptr ) {
( * ptr ) + + ; / / adding 1 to * ptr
}
int main ( )
{
int * p, i = 10 ;
p = và i
addOne ( p ) ;
printf ( “ % d ”, * p ) ; / / 11
return 0 ;
}
Ban đầu, giá trị được tàng trữ tại p, * p là 10 .
Sau đó, tôi truyền biến pointer p vào hàm addOne ( ). Biến pointer ptr sẽ nhận địa chỉ trong hàm addOne ( ) .
Bên trong hàm này, tôi tăng giá trị tàng trữ tại ptr thêm 1 bằng cách sử dụng ( * ptr ) + + ;. Bởi vì biến pointer ptr và p có chung địa chỉ nên * p bên trong hàm main ( ) cũng sẽ là 11 .

5. Cấp phát bộ nhớ trong lập trình C

Trong phần này, bạn sẽ được học cách cấp phép tự động hóa bộ nhớ trong lập trình C bằng cách sử dụng những hàm thư viện tiêu chuẩn : malloc ( ), calloc ( ), không lấy phí ( ) và realloc ( ) .
Như tất cả chúng ta đã biết, mảng là tập hợp của 1 số ít giá trị cố định và thắt chặt. Sau khi khai báo xong mảng, bạn không hề biến hóa kích cỡ của nó .
Đôi khi kích cỡ mảng mà bạn khai báo hoàn toàn có thể không đủ. Để xử lý yếu tố này, bạn hoàn toàn có thể cấp phép bộ nhớ một cách bằng tay thủ công trong thời hạn chạy lập trình. Cách thức này được xem là cấp phép bộ nhớ động trong lập trình C .

Để cấp phát bộ nhớ động, bạn sử dụng các hàm thư viện malloc(), calloc(), realloc() và free(). Các hàm này được xác định trong tệp tiêu đề .

5.1 Hàm malloc() trong lập trình C

Tên “ malloc ” là từ viết tắt của memory allocation ( cấp phép bộ nhớ )
Hàm malloc ( ) dự trữ một khối bộ nhớ chứa một lượng byte nhất định. Và, nó sẽ trả về biến pointer void. Biến này hoàn toàn có thể được chuyển thành biến con trỏ dưới bất kỳ hình thức nào .

Cú pháp của hàm malloc()

ptr = ( castType * ) malloc ( size ) ;
Ví dụ ,
ptr = ( float * ) malloc ( 100 * sizeof ( float ) ) ;
Câu lệnh trên cấp phép 400 byte của bộ nhớ. Bởi vì kích cỡ của float là 4 byte. Và biến pointer ptr giữ địa chỉ của byte tiên phong trong bộ nhớ được cấp phép .
Nếu không hề cấp phép bộ nhớ thì biểu thức này sẽ có tác dụng là biến pointer NULL .

5.2 Hàm calloc() trong lập trình C

Tên “ calloc ” tượng trưng cho contiguous allocation ( cấp phép liên tục )
Hàm malloc cấp phép bộ nhớ và để bộ nhớ ở trạng thái chưa khởi tạo. Trong khi đó, hàm calloc ( ) cấp phép bộ nhớ và khởi tạo tổng thể những bit từ 0 .

Cú pháp của hàm calloc()

ptr = ( castType * ) calloc ( n, size ) ;
Ví dụ :
ptr = ( float * ) calloc ( 25, sizeof ( float ) ) ;
Câu lệnh trên phân chia khoảng trống liền kề trong bộ nhớ cho 25 thành phần kiểu float .

5.3 Hàm free() trong lập trình C

Bộ nhớ được cấp phát động được tạo bằng hàm calloc ( ) hoặc malloc ( ) sẽ không tự giải phóng được. Bạn phải sử dụng hàm không tính tiền ( ) để giải phóng khoảng trống .

Cú pháp của hàm free()

không tính tiền ( ptr ) ;
Câu lệnh này giúp giải phóng khoảng trống được cấp phép trong bộ nhớ được chỉ định bởi biến pointer ptr .

Ví dụ 1: hàm malloc() và free()

/ / Program to calculate the sum of n numbers entered by the user

#include

#include

int main ( )
{
int n, i, * ptr, sum = 0 ;
printf ( “ Enter number of elements : “ ) ;
scanf ( “ % d ”, và n ) ;
ptr = ( int * ) malloc ( n * sizeof ( int ) ) ;
/ / if memory cannot be allocated
if ( ptr = = NULL )
{
printf ( “ Error ! memory not allocated. ” ) ;
exit ( 0 ) ;
}
printf ( “ Enter elements : “ ) ;
for ( i = 0 ; i < n ; + + i ) { scanf ( “ % d ”, ptr + i ) ; sum + = * ( ptr + i ) ; } printf ( “ Sum = % d ”, sum ) ; / / deallocating the memory không lấy phí ( ptr ) ; return 0 ; } Trong ví dụ này, tôi đã cấp phép bộ nhớ cho n số int .

Ví dụ 2: hàm calloc() và free()

/ / Program to calculate the sum of n numbers entered by the user

#include

#include

int main ( )
{
int n, i, * ptr, sum = 0 ;
printf ( “ Enter number of elements : “ ) ;
scanf ( “ % d ”, và n ) ;
ptr = ( int * ) calloc ( n, sizeof ( int ) ) ;
if ( ptr = = NULL )
{
printf ( “ Error ! memory not allocated. ” ) ;
exit ( 0 ) ;
}
printf ( “ Enter elements : “ ) ;
for ( i = 0 ; i < n ; + + i ) { scanf ( “ % d ”, ptr + i ) ; sum + = * ( ptr + i ) ; } printf ( “ Sum = % d ”, sum ) ; không lấy phí ( ptr ) ; return 0 ; }

5.4 Hàm realloc() trong lập trình C

Nếu bộ nhớ được cấp phát động không đủ hoặc nhiều hơn nhu yếu, bạn hoàn toàn có thể biến hóa size của bộ nhớ được cấp phép trước đó bằng cách sử dụng hàm realloc ( ) .

Cú pháp của hàm realloc()

ptr = realloc ( ptr, x ) ;
ptr được phân chia lại với size mới là x .

Ví dụ 3: hàm realloc()

#include

#include

int main ( )
{
int * ptr, i, n1, n2 ;
printf ( “ Enter size : “ ) ;
scanf ( “ % d ”, và n1 ) ;
ptr = ( int * ) malloc ( n1 * sizeof ( int ) ) ;
printf ( “ Addresses of previously allocated memory : “ ) ;
for ( i = 0 ; i < n1 ; + + i ) printf ( “ % u \ n ”, ptr + i ) ; printf ( “ \ nEnter the new size : “ ) ; scanf ( “ % d ”, và n2 ) ; / / rellocating the memory ptr = realloc ( ptr, n2 * sizeof ( int ) ) ; printf ( “ Addresses of newly allocated memory : “ ) ; for ( i = 0 ; i < n2 ; + + i ) printf ( “ % u \ n ”, ptr + i ) ; không tính tiền ( ptr ) ; return 0 ; } Khi chạy, lập trình này sẽ trả ra hiệu quả : Enter size : 2 Addresses of previously allocated memory : 26855472 26855476 Enter the new size : 4 Addresses of newly allocated memory : 26855472 26855476

26855480

26855484

Trên đây là tất cả những thông tin bản chất của biến con trỏ trong C là gì. Hy vọng qua bài viết, bạn đã hiểu rõ được con trỏ là gì, biến con trỏ hay biến pointer trong C khởi tạo, khai báo như thế nào,..

Nếu bạn còn nhiều vướng mắc, hãy liên hệ tới Ironhack để giải đáp sâu xa bởi những chuyên viên IT tay nghề cao. Chúc bạn thành công xuất sắc !