Con trỏ (pointer) từ cơ bản tới nâng cao phần 2


Phần 2 rồi, và nó sẽ khó hơn một chút. Học càng chậm lại nhé!

Kiểu dữ liệu của con trỏ

Lấy một ví dụ đơn giản thế này:

int *p; // kiểu dữ liệu của con trỏ p này là int*

Hiểu kiểu dữ liệu của con trỏ chưa nào? quá dễ hiểu! Vậy xem ví dụ sau:

void *p; // thằng p này kiểu gì???

Các phép toán trên con trỏ

1. Phép gán
– Tất cả con trỏ đều có phép gán.
– Như phần khởi tạo, phép gán yêu cầu vế trái là một con trỏ, và vế phải là một địa chỉ.
– Phép gán yêu cầu sự tương xứng về kiểu dữ liệu, nếu không chúng ta phải ép kiểu, các bạn biết ép kiểu không?
– Phép gán với con trỏ kiểu void không cần phải tương xứng kiểu dữ liệu.
Xem ví dụ:

int a = 100;
int *p;
p = (int*)0x0060FF08; // 0x0060FF08 giả sử là địa chỉ của biến a, 
                      // đây là ép kiểu, 
                      // vì 0x0060FF08 chỉ là một số nguyên,
                      // phải ép sang kiểu int* mới gán được cho con trỏ.                     
printf("*p: %d\n", *p); // kết quả của *p là 100, vì lúc này p trỏ tới a

2. Phép so sánh
– Phép so sánh ngang bằng dùng để kiểm tra 2 con trỏ có trỏ vào cùng 1 vùng nhớ hay không, hoặc kiểm tra 1 con trỏ có phải là đang trỏ vào NULL hay không (trong trường hợp cấp phát động, mở file, mở resource, vv).
– Phép so sánh lớn hơn nhỏ hơn: >, =, <= sử dụng để kiểm tra về độ thấp cao giữa 2 địa chỉ. Con trỏ nào nhỏ hơn thì trỏ vào địa chỉ thấp hơn.
– Được quyền so sánh mọi con trỏ với 0, vì 0 chính là NULL.

int a=197,*p=&a;
double *x;
p == &a;   // kết quả = 1 
main == 0; // kết quả bằng 0 
p == 0;    // kết quả bằng 0
x == 0;    // kết quả bằng 0

Lưu ý: chúng ta sẽ đi sâu về phép so sáng này sau, vì nâng cao nó khá khó!

3. Phép cộng trừ và phép tăng giảm
Bản chất của việc tăng giảm con trỏ là di chuyển con trỏ đi lên hoặc đi xuống:
– Đương nhiên không phải di chuyển sang ô nhớ kế tiếp (byte kế tiếp), mà phụ thuộc vào kiểu dữ liệu của vùng nhớ con trỏ trỏ tới.
Ví dụ:

int a = 100, *p = &a; // giả sử lúc này p đang trỏ tới địa chỉ 0x0060FF08 (địa chỉ của a)
p++;                  // p lúc này sẽ trỏ tới địa chỉ 0x0060FF0C (tăng lên 4 ô nhớ)
                      // bởi vì kiểu int lúc này có 4 byte
p -= 2;               // p sẽ trỏ tới địa chỉ 0x0060FF04

Chú ý:
– Không có phép tăng giảm con trỏ void và con trỏ hàm.
– Không có phép cộng 2 con trỏ với nhau.
– Phép trừ 2 con trỏ trả về độ lệch pha giữa 2 con trỏ

Một số ví dụ hay và đương nhiên…khó!

Chương trình viết hoa một chuỗi được nhập vào từ bàn phím

#include <stdio.h>
#include <conio.h>
#include <ctype.h>
int main()
{
    char xau[100];
    char *p = &xau;
    printf("Nhap xau: ");
    scanf("%[a-zA-Z]",xau); // lệnh này giới hạn kí tự nhập từ a-z và A-Z thôi
    /*while-loop style*/
    while(*p)
    {
        printf("%c",toupper(*p));
        p++;
    }
    return 0;
}

Chương trình in ra chuỗi bị đảo ngược

#include <stdio.h>
#include <conio.h>
#include <ctype.h>
int main()
{
    char xau[100];
    char *p;
    printf("Nhap xau: ");
    scanf("%[a-zA-Z]",xau); 

    p = xau + strlen(xau) -1; // đưa con trỏ về cuối xâu
    /* for-loop style */
    for(; p >= xau; p--)
        printf("%c", *p);
    return 0;
}

Chương trình con lấy độ dài một chuỗi (khi học nhúng thì bạn nên viết chương trình con ra như vậy thay vì dùng hàm có sẵn, bạn phải khai báo thư viện, tốn bộ nhớ

int strlen(char *p)
{
    int temp = 0; // biến đếm độ dài
    while(*p)     // chừng nào gặp kí tự kết thúc chuỗi thì dừng (mã ASCII = 0)
    {
        temp++;
        p++;
    }
    return temp;
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s