Thứ Sáu, 17 tháng 12, 2021

1: Biến Con Trỏ (Phần 1) - Khái Niệm Chung, Các Biểu Thức Con Trỏ

 

1: Biến Con Trỏ


- Khái Niệm Chung

Một con trỏ là một biến chứa địa chỉ 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 tthì ta nói biến thứ nhất trỏ đến biến thứ hai.

Cũng như mọi biến khác, biến con trỏ cũng phải được khai báo trước khi dùng. Dạng tổng quát để khai báo một biến con trỏ là :

type *<tên biến>

Trong đó: type là bất kì kiểu dữ liệu cơ bản thích hợp nào được chấp nhận trong C và <tên biến> là tên của một biến con trỏ. Kiểu dữ liệu cơ bản xác định kiểu của những biến mà con trỏ có thể chỉ đến. Ví dụ khai báo biến con trỏ chỉ đến các biến nguyên và biến kiểu kí tự:

char *p;
int *x,*y;

Con trỏ có một trị đặc biệt gọi là NULL. Trị này có nghĩa là con trỏ chưa trỏ tới một địa chỉ hợp lệ nào cả. Để dùng được trị này chúng ta phải dùng #include <stdio.h> đầu chương trình

- Các Phép Toán Về Con Trỏ

Trong ngôn ngữ C có hai phép toán đặc biệt đối với con trỏ : * và & . Phép toán & là phép toán trả về địa chỉ trong bộ nhớ của biến sau nó. Ví dụ:

p = &a;

Sẽ đặt vào biến p địa chỉ trong bộ nhớ của biến a. Địa chỉ này không có liên quan gì đến trị số của biến a. Nói cách khác địa chỉ của biến a không liên quan gì đến nội dung của biến a.

Phép toán * là phép toán trả về trị của biến đặt tại địa chỉ được mô tả bởi biến đi sau nó. Ví dụ nếu biến a chứa địa chỉ của biến b thì

p = *a

Sẽ đặt trị số của biến b vào biến p

Chương trình mẫu Dev C:
// Pointer_1: Lập chương trình In số 100 lên màn hình
#include <stdio.h>

int main()   
{
    int *p,a,b;


    a=100;
    p=&a;
    b=*p;
    printf("%d",b);
    return 0;
}   




- Tầm Quan Trọng Của Dữ Liệu Khi Khai Báo Con Trỏ

Cần phải bảo đảm là con trỏ luôn luôn trỏ đến một kiểu đữ liệu phù hợp. Ví dụ khi khai báo con trỏ kiểu int, trình biên dịch sẽ hiểu là con trỏ bao giờ cũng chỉ đến một biến có độ dài là 2 byte .
Ta xét một chương trình như sau

Chương trình mẫu Dev C:
// Pointer_2
#include <stdio.h>

int main()   
{
    float x=10.1,y;

    int *p;
    p = &x;     
    y=*p;     
    printf("%f",y);
    
    return 0;
}   




Chương trình này nhằm gán trị của x cho biến y và in ra trị đó. Khi biên dịch chương trình báo lỗi:

[Error] cannot convert 'float*' to 'int*' in assignment

Tuy nhiên chương trình không gấn trị x cho y được. Lí do là ta khai báo một con trỏ int và cho nó trỏ tới biến float x. Như vậy trình biên dịch sẽ chỉ chuyển 2 byte thông tin cho y chứ không phải 4 byte để tạo ra một số dạng float.


- Các Biểu Thức Con Trỏ

Các phép gán con trỏ: Cũng giống như bất kì một biến nào khác , ta có thể dùng một con trỏ ở về phải của một phép gán để gắn trị của một con trỏ cho một con trỏ khác. Ví dụ ta viết

Chương trình mẫu Dev C:
// Pointer_3
#include <stdio.h>

int main()   
{
    int x;
    int *p1,*p2;
    
    p1 = &x;
    p2 = p1;
    printf(" %p",p2);

    return 0;
}   




Chương trình này hiện lên địa chỉ của biến x ở dạng hex bằng cách dùng một mã định dạng khác của hàm printf(). %p mô tả rằng sẽ hiện lên một trị chứa trong một biến con trỏ theo dạng reg:xxxx với reg là tên của một trong các thanh ghi segment của CPU còn xxxx là địa chỉ offset tính từ đầu segment.

Các phép toán số học của con trỏ: Trong C, ta chỉ có thể dùng hai phép toán số học tác động lên con trỏ là phép + và - . Để hiểu được cái gì sẽ xảy ra khi thực hiện một phép toán số học lên con trỏ ta giả sử p1 là một con trỏ chỉ đến một số nguyên có địa chỉ là 2000. Sau khi thực hiện biểu thức

p1++;

con trỏ sẽ chỉ đến số nguyên nằm ở địa chỉ 2002 vì mỗi khi tăng con trỏ lên 1 nó sẽ chỉ đến số nguyên kế tiếp mà mỗi số nguyên lại có độ dài 2 byte . Điều này cũng đúng khi giảm. Ví dụ:

p1--;

sẽ trỏ tới số nguyên ở địa chỉ 1998 . Như vậy mỗi khi con trỏ tăng lên 1, nó sẽ chỉ đến dữ liệu kế tiếp tại địa chỉ nào đó tuỳ theo độ dài của kiểu dữ liệu. C còn cho phép cộng hay trừ một số nguyên với một con trỏ. Biểu thức:

p1=p1+9:

sẽ làm cho con trỏ chỉ tới phần tử thứ 9 có kiểu là kiểu mà pI trỏ tới và nằm sau phân tử hiện thời nó đang trỏ đến. Ngoài các phép toán trên, con trỏ không chấp nhận một phép toán nào khác.

So sánh các con trỏ: Chúng ta có thể so sánh 2 con trỏ trong một biểu thức quan hệ . Ví dụ cho hai p và q, phát biểu sau đây là hợp lệ :

if (p<q)
{
    printf("p tro den mot vi tri bo nho thap hon q\n");
}

Tuy nhiên cần nhớ rằng phép toán trên là so sánh hai địa chỉ chứa trong p và q chứ không phải nội dung của hai biến mà p và q trỏ tới .

- Các ví dụ về việc dùng con trỏ:

Chương trình mẫu Dev C:
// Pointer_4
#include <stdio.h>

int main()   
{
    int i, j, * p;

    i = 5;
    p = &i;
    j = * p;* p = j + 2;

    return 0;
}   




Trong chương trình trên ta khai báo hai biến nguyên là i và j và một biến con trỏ p trỏ tới một số nguyên. Chương trình sẽ phân phối bộ nhớ cho 3 biến này ví dụ tại các địa chỉ 100, 102 và 104 vì mỗi số nguyên dài 2 byte và con trỏ mặc nhiên cũng được mã hoá bằng 2 byte.

100 i
102 j
104 p

lệnh i = 5 cho trị số của biến i là 5

100 5 i
102 j
104 p

lệnh p = &i làm cho con trỏ chỉ tới biến i nghĩa là con trỏ p chứa địa chỉ của biến i. Bây giờ p chỉ đến biến i.

100 5 i
102 j
104 100 p

lệnh j = *p đặt nội dung của biến do p chỉ tới (biến i) vào biến j nghĩa là gấn 5 cho j

100 5 i
102 5 j
104 100 p

Một trong những vấn đề lí thú khi dùng con trỏ là xem nội dung bộ nhớ của máy tính. Chương trình sau đây cho phép ta vào địa chỉ bắt đầu của RAM mà ta muốn khảo sát và sau đó hiện lên nội dung mỗi byte ở dạng số hex. Trong chương trình có từ khoá far dùng để tham khảo đến các vị trí không nằm trong cùng một segmeit.

Chương trình mẫu Dev C:
// Pointer_5
#include <stdio.h>

int main()   
{
    unsigned long int start;
    char *p;
    int t;

    printf("Nhap vao dia chi bat dau ma ban muon xem: ");
    scanf("%lu", &start);
    
    p = (char far *) start;
    for (t = 0;; t++, p++)
    {
        if (!(t % 16))
        {
            printf("%2x\n", * p);
        }
    }

    return 0;
}   




Trong chương trình ta dùng định dạng %x trong hầm printfQ để in ra số dạng hex. Dòng p = (char far *) start; dùng biến đổi số nhập vào thành một con trỏ.

Tuy nhiên đối với các trình biên dịch mới hiện nay không còn hỗ trợ từ khóa này nữa bởi vì từ khóa far chỉ hỗ trợ 16-bit. Và trên trang chủ của Dev C cũng thông báo về vấn đề này

Far pointers are only really used in 16-bit "real mode" code. Dev-Cpp creates 32-bit "(flat) protected mode" code, where far pointers are not used.



0 bình luận:

Đăng nhận xét