Thứ Ba, 21 tháng 12, 2021

1: Biến Con Trỏ (Phần 3) - Con Trỏ Và Chuỗi, Con Trỏ Trỏ Đến Con Trỏ

 

1: Biến Con Trỏ


- Con Trỏ và Chuỗi

Rất nhiều hầm thư viện trong C làm việc với chuỗi theo con trỏ . Ví dụ hàm strchr() trả về con trỏ trỏ đến lần xuất hiện đầu tiên của một kí tự nào đó trong chuỗi Ví dụ:  ptr = strchr(str,'x')
thì biến con trỏ ptr sẽ được gán địa chỉ của lần xuất hiện kí tự “x” đầu tiên trong chuỗi str.
Sau đây là chương trình cho phép ta gõ vào một câu và một kí tự cần định vị trong câu.
Chương trình sẽ cho ta :
- địa chỉ bắt đầu của chuỗi
- địa chỉ của kí tự cần định vị
- độ lệch so với điểm đầu chuỗi

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

int main()   
{
    char ch, line[81], * ptr;

    printf("Cho mot cau : ");
    gets(line);
    printf("Cho ki tu can tim : ");
    ch = getche();
    ptr = strchr(line, ch);
    printf("\nChuoi bat dau tai dia chi %u.\n", line);
    printf("Ki tu xuat hien lan dau tai %u.\n", ptr);
    printf("Do la vi tri %d", (ptr - line + 1));
        
    return 0;
}   




Chuỗi cũng có thể được khởi tạo bằng con trỏ. Ta xét ví dụ sau
Chương trình mẫu Dev C:
// Pointer_12
#include <stdio.h>
#include <conio.h>
#include <string.h>

int main()   
{
    char * chao = "Xin chao !";

    char ten[30];
    
    printf("Cho ten cua ban : ");
    gets(ten);
    printf(chao);
    puts(ten);
    getch();
        
    return 0;
}   




Trong chương trình trên ta đã khởi tạo chuỗi bằng phát biểu char *chao = " Xin chao !" thay cho static char chao[ ]=" Xin chao !"
Cả hai cách đều cho cùng một kết quả. Trong phương án dùng con trỏ, chao là biến con trỏ nên có thể thay đổi được. Ví dụ phát biểu: puts(++chao)sẽ cho kết quả: in chao !

Nếu ta có một mảng chuỗi ta cũng có thể dùng mảng con trỏ trỏ tới mảng chuỗi này. Ta khởi tạo chúng giống như khởi tạo biến con trỏ đơn.

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

#define max 5

int main()   
{
    int dex;
    int enter = 0;
    char name[40];
    static char * list[max] = {
        "Hung",
        "Ngan",
        "Van",
        "Hoa",
        "Ntech"
    };
    
    printf("Cho ten cua ban : ");
    gets(name);
    for (dex = 0; dex < max; dex++)
        if (strcmp(list[dex], name) == 0)
            enter = 1;
    if (enter == 1)
        printf("Ban da dang ki hoc lop C");
    else printf("Ban chua dang ki vao lop");
    getch();

    return 0;
}   




Phát biểu char *list[max] nói rằng list là một mảng con trỏ gồm max phần tử chỉ tới các kí tự . Chúng ta xét tiếp một ví dụ như sau :

Chương trình mẫu Dev C: Nhập vào một dãy tên và sắp xếp lại đúng thứ tự a,b,c
// Pointer_14
#include <stdio.h>
#include <conio.h>
#include <string.h>

#define maxnum 3
#define maxlen 10

int main()   
{
    static char name[maxnum][maxlen];
    char *ptr[maxnum];
    char *temp;
    int count = 0;
    int in, out;
    
    while (count < maxnum)
    {
         printf("Ban cho ten : ");
         gets(name[count]);
         if (strlen(name[count]) == 0)
             break;
         ptr[count] = name[count];
         count++;
    }

    for (out = 0; out < count - 1; out++)
    {
      for (in = out + 1; in < count; in++)
      {
         if (strcmp(ptr[out], ptr[ in ]) > 0)
         {
             temp = ptr[in];
             ptr[in] = ptr[out];
             ptr[out] = temp;
         }
      }
    }
    printf("Danh sach da sap xep :\n");
    for (out = 0; out < count; out++)
    {
        printf("Ten thu %d : %s\n", out + 1, ptr[out]);
    }
    getch();

    return 0;
}   




Chương trình này dùng cả mảng chuỗi và mảng con trỏ chuỗi . Con trỏ nằm trong mảng được khai báo như sau:
char *ptr[maxnum]
chuỗi nằm trong mảng hai chiều
static char name[maxnumn][maxlen]
Do ta không biết một chuỗi dài bao nhiêu nên phải dùng mảng chuỗi name có tối đa maxnum phần tử , mỗi phần tử có maxlen kí tự . Khi nhập chuỗi phát biểu

pt[count++] = name[count]
sẽ gán địa chỉ của mỗi chuỗi được cất giữ trong mảng name[][] vào phần tử con trỏ ptr.. Sau đó mảng con trỏ này được sắp xếp dựa trên mảng name[{][] nhương mảng name[][] không thay đổi gì cả.

Ngôn ngữ C có thể xử lí các thành phần của mảng như một mảng . Cụ thể C có thể xem một dòng của mảng hai chiều như là một mảng một chiều. điều này rất tiện lợi như ta đã thấy trong chương trình trên . Câu lệnh ptr[count++] = name[count] hoàn toàn hợp lí vì vế phải chính là địa chỉ của mảng name|count] và mảng này là một thành phần của mảng name[][] là một mảng hai chiều. Ta xem lại khai báo:
static char name[maxnumn][maxlen]
rõ ràng ta có thể xem đây là một mảng một chiều có maxnum chuỗi và tham khảo tới phần tử của mảng một chiều bằng 1 chỉ số.
Ví dụ:
- name[count] với count<=maxnum như thế
- name[{0] : địa chỉ của chuỗi 1
- name[{[1] : địa chỉ của chuỗi 2


- Con Trỏ Trỏ Đến Con Trỏ

Chúng ta có một chương trình in ra một bảng số được viết như sau :
Chương trình mẫu Dev C:
// Pointer_15
#include <stdio.h>
#include <conio.h>
#include <string.h>

#define row 4
#define col 5

int main()   
{
    static int table[row][col] = {
     {
         13, 15, 17, 19, 21
     },
     {
         20, 22, 24, 26, 28
     },
     {
         31, 33, 35, 37, 39
     },
     {
         40, 42, 44, 46, 48
     }
    };
    int c = 10;
    int i, j;


    for (i = 0; i < row; i++)
    {
         for (j = 0; j < col; j++)
         {
             table[i][j] += c;
        }
    }
    for (i = 0; i < row; i++)
    {
         for (j = 0; j < col; j++)
         {
             printf("%5d", table[i][j]);
         }
         printf("\n");
    }
    getch();

    return 0;
}   




Trong chương trình trên ta dùng kí hiệu mảng. Bây giờ ta muốn viết chương trình dùng kí hiệu con trỏ thay cho kí hiệu mảng. Vậy thì làm thế nào để mô tả table[i][j] bằng con trỏ. Ta thấy rằng:

- table là địa chỉ của phần tử đầu tiên của toàn bộ mảng, giả định là 1000
- do đây là mảng nguyên nên mỗi phần tử chiếm 2 byte và mỗi dòng chiếm 10 byte vì có 5 phần tử . Như vậy địa chỉ của hai dòng liền nhau cách nhau 10 byte
- do có thể xem mỗi dòng là một mảng một chiều nên các mảng một chiêu liền nhau cách nhau 10 byte
- trình biên dịch biết số cột trong mảng qua khai báo nên nó sẽ hiểu table+1 là đem table (giá trị 1000) cộng với 10 byte thành 1010. Tương tự table+2 cho ta 1020.

1000 13 15 17 19 21 table[O]
1010 20 22 21 26 28 table[1]
1020 31 33 35 37 39 table[2]
1030 40 42 44 46 48 table|3]

Để tham khảo đến từng phần tử của dòng trước hết ta lưu ý địa chỉ của mảng cũng là địa chỉ của phần tử đầu tiên của mảng . Ví dụ với mảng một chiều a[size] thì a và a[O] là như nhau.
Trở lại mảng hai chiều địa chỉ của mảng một chiều tạo bởi dòng thứ 3 của mảng table[][] là table[2] hay table+2.
Trong kí hiệu con trỏ địa chỉ của phần tử đầu tiên của mảng một chiều này là &table[2][O] hay *(table+2).
Cả hai cách viết table+2 và *(table+2) đều tham khảo nội dung của cùng một ô nhớ (1020) . Nếu cộng 1 vào table +3 để có table+3 thì ta nhận được địa chỉ của dòng thứ 4 trong mảng table[][].
Nếu cộng 1 vào *(able+2) để có *(table+2)+1 thì có địa chỉ của phần tử thứ 2 trong dòng thứ 3 của mảng table[][].

Tóm lại :
table[i] = *(table+i)
&table[i] = table+i

table[i][j] = *(*table+i)+j)
&table[i][j] = (*table+i)+j)

Như vậy chương trình trên được viết lại như sau:

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

#define row 4
#define col 5

int main()   
{
    static int table[row][col] = {
      {
          13,15,17,19,21
      },
      {
          20,22,24,26,28
      },
      {
          31,33,35,37,39
      },
      {
          40,42,44,46,48
      }
    };
    int c = 10;
    int i, j;


    for (i = 0; i < row; i++)
    {
          for (j = 0; j < col; j++)
          {    
              *(*(table + i) + j) += c;
          }
    }
    for (i = 0; i < row; i++)
    {
          for (j = 0; j < col; j++)
          {
              printf("%5d", *( * (table + i) + j));
        }
          printf("\n");
    }
    getch();

    return 0;
}   



0 bình luận:

Đăng nhận xét