24/05/2018, 14:43

Cấu trúc

là tập hợp của một hoặc nhiều biến, chúng có thể khác kiểu nhau, được nhóm lại dưới một cái tên duy nhất để tiện sử lý. còn gọi là bản ghi trong một số ngôn ngữ khác, chẳng hạn như PASCAL. giúp cho việc tổ chức các dữ liệu phức tạp, đặc biệt trong ...

là tập hợp của một hoặc nhiều biến, chúng có thể khác kiểu nhau, được nhóm lại dưới một cái tên duy nhất để tiện sử lý. còn gọi là bản ghi trong một số ngôn ngữ khác, chẳng hạn như PASCAL.

giúp cho việc tổ chức các dữ liệu phức tạp, đặc biệt trong những chương trình lớn vì trong nhiều tình huống chúng cho phép nhóm các biến có liên quan lại để xử lý như một đơn vị thay vì các thực thể tách biệt.

Một ví dụ được đề cập nhiều đến là cấu trúc phiếu ghi lương, trong đó mỗi nhân viên được mô tả bởi một tập các thuộc tính chẳng hạn như : tên, địa chỉ, lương, phụ cấp vv.. một số trong các thuộc tính này lại có thể là cấu trúc bởi trong nó có thể chứa nhiều thành phần : Tên ( Họ, đệm, tên ), Địa chỉ ( Phố, số nhà ) vv.

Trong chương này chúng ta sẽ minh hoạ cách sử dụng của các cấu trúc trong chương trình.

Khi xây dựng cấu trúc, ta cần mô tả kiểu của nó. Điều này cũng tương tự như việc phải thiết kế ra một kiểu nhà trước khi ta đi xây dựng những căn nhà thực sự ở các địa điểm khác nhau. Công việc định nghĩa một kiểu cấu trúc bao gồm việc nêu ra tên của kiểu cấu trúc và các thành phần của nó theo mẫu sau :

struct tên_kiểu _cấu_trúc
    {
    Khai báo các thành phần của cấu trúc (1)
    };
    

Trong đó :

  • struct là từ khoá
  • tên_kiểu _cấu_trúc là một tên bất kỳ do người lập trình tự đặt theo qui tắc đặt tên nêu ra trong chương 1.

Thành phần của cấu trúc có thể là : biến, mảng, cấu trúc khác đã được định nghĩa trước đó vv..

Ví dụ

Ví dụ 1:

Đoạn chương trình :

struct ngay {
    int ngaythu;
    char thang[12];
    int nam;
     };
    
    

mô tả một kiểu cấu trúc có tên là ngay gồm có ba thành phần : Biến nguyên ngaythu, mảng thang, và biến nguyên nam.

Ví dụ 2:

Đoạn chương trình :

struct nhancong 
    {
      
    char ten[15];
    char diachi[20]
    double bacluong;
    struc ngay ngaysinh;
    struc ngay ngaybatdaucongtac;
     };
    

tạo ra kiểu cấu trúc có tên là nhancong gồm có năm thành phần. Ba thành phần đầu không có gì cần nói thêm. Chỉ có hai thành phần còn lại là các cấu trúc ngaysinhngaybatdaucongtac được xây dựng theo cấu trúc ngay được định nghĩa trong ví dụ 1.

Định nghĩa cấu trúc bằng typedef

Có thể dùng toán tử typedef để định nghĩa các kiểu cấu trúc ngay nhancong ở trên như sau :

typedef struct
    {
    int ngaythu;
    char thang[12];
    int nam;
    
    } ngay;
    typedef struct
    {
      
    char ten[15];
    char diachi[20]
    double bacluong;
    struc ngay ngaysinh;
    struc ngay ngaybatdaucongtac;
     } nhancong;
    

Xây dựng những cấu trúc thực sự theo các kiểu đã khai báo trước đó. Vấn đề này hoàn toàn giống như việc khai báo các biến và các mảng. Giả sử ta đã có các kiểu cấu trúc ngaynhancong như trong mục trên. Khi đó ta khai báo :

Ví dụ 1 :

struct ngay ngaydi, ngayden
;

sẽ cho ta hai cấu trúc với tên là ngaydingayden. Cả hai cấu trúc đều được xây dựng theo cấu trúc kiểu ngay.

Ví dụ 2 :

struct nhancong nhom1,nhom2;

sẽ cho ta hai cấu trúc với tên là nhom1nhom2. Cả hai cấu trúc đều được xây dựng theo cấu trúc kiểu nhancong.

Như vậy, một cách tổng quát, việc khai báo cấu trúc được thực hiện theo mẫu sau :

Cách 1

Struct tên_kiểu_cấu_trúc_đã_khai_báo danh_sách_tên_các_cấu_trúc; (2)

Các biến cấu trúc được khai báo theo mẫu trên sẽ được cấp phát bộ nhớ một cách đầy đủ cho tất cả các thành phần của nó.

Việc khai báo có thể thực hiện đồng thời với việc định nghĩa kiểu cấu trúc. Muốn vậy, chỉ cần đặt danh sách tên biến cấu trúc cần khai báo sau dấu } của (* ) như trên .

Nói cách khác, để vừa khai báo kiểu vừa khai báo biến ta dùng cách sau :

Cách 2

struct tên_kiểu_cấu_trúc
    {
    Các thành phần của cấu trúc (3)
    } danh_sách_tên_các_cấu_trúc;
    

Ví dụ :

Ví dụ 1 :

struct ngay
    {
    int ngaythu;
    char thang[12];
     int nam;
    } ngaydi,ngayden;
    

Ví dụ 2 :

struct nhancong 
    {
      
    char ten[15];
    char diachi[20];
    double bacluong;
    struc ngay ngaysinh;
    struc ngay ngaybatdaucongtac;
    
     } nhom1,nhom2;
    

Khi vừa định nghĩa kiểu cấu trúc vừa khai báo cấu trúc như trong ví dụ trên, ta không thể không cần đến tên kiểu cấu trúc. Nói cách khác cấu trúc có thể được khai báo theo cách sau :

struct 
    {
    Các thành phần của cấu trúc (4)
    } danh_sách_tên_các_cấu_trúc;
    

Ví dụ :

struct 
    {
    int ngaythu;
    char thang[12];
     int nam;
    } ngaydi,ngayden;
    

Sự khác nhau của các cách khai báo cấu trúc trong (3) và (4) là ở chỗ : Với (3) ta vừa khai báo được một kiểu cấu trúc vừa khai báo được các cấu trúc, và có thể dùng kiểu cấu trúc này để khai báo cho các cấu trúc khác như trong (2), còn (4) chỉ khai báo được các cấu trúc.

Nếu dùng từ khoá typedef để định nghĩa kiểu cấu trúc như trong mục 8.1 thì khi khai báo các cấu trúc mới ta không cần dùng từ khoá struct, chỉ cần dùng tên kiểu.

Ví dụ như kiểu cấu trúc ngay được khai báo bằng typedef trong 8.1 thì khi khai báo các cấu trúc mới là ngaydi ngayden có cùng kiểu ngay ta dùng dòng lệnh sau :

ngay ngaydi,ngayden;

Truy nhập đến các thành phần cấu trúc

Ta đã khá quen với việc sử dụng các biến, các phần tử của mảng và tên mảng trong các câu lệnh. Trên đây ta cũng đã đề cập đến các thành phần của cấu trúc là biến và mảng. Việc xử lý một cấu trúc bao giờ cũng phải được thực hiện thông qua các thành phần của nó.

Để truy cập đến một thành phần cơ bản ( là biến hoặc mảng ) của một cấu trúc ta sử dụng một trong các cách viết sau :

tên_cấu_trúc.tên_thành_phần

tên_cấu_trúc.tên_cấu_trúc.tên_thành_phần

tên_cấu_trúc. tên_cấu_trúc.tên_cấu_trúc.tên_thành_phần

.....

Cách viết thứ nhất như trên được sử dụng khi biến hoặc mảng là thành phần trực tiếp của một cấu trúc. Ví dụ như biến ngaythu, biến nam và mảng thang là các thành phần trực tiếp của các cấu trúc ngaydi, ngayden. Các biến bacluong, các mảng ten, diachi là các thành phần trực tiếp của các cấu trúc nhancong.

Các cách viết còn lại như trên được sử dụng khi biến hoặc mảng là thành phần trực tiếp của một cấu trúc mà bản thân cấu trúc này lại là thành phần của các cấu trúc lớn hơn.

Ví dụ :

Ta xét phép toán trên các thành phần của cấu trúc nhom1, nhom2 :

Câu lệnh :

printf("%s",nhom1.ten); 

sẽ đưa lên màn hình tên của nhom1.

Câu lệnh :

tongluong=nhom1.bacluong+nhom2.bacluong;

sẽ gán tổng lương của nhom1nhom2 rồi gán cho biến tongluong.

Câu lệnh :

printf("%d",nhom1.ngaysinh.ten); 
    

sẽ đưa lên màn hình ngày sinh của nhom1.

Câu lệnh :

printf("%d",nhom1. ngaybatdaucongtac.nam); 

sẽ đưa lên màn hình ngày bắt đầu công tác của nhom1.

  • Có thể sử dụng phép toán lấy địa chỉ đối với các thành phần cấu trúc để nhập số liệu trực tiếp vào các thành phần cấu trúc.Ví dụ như ta viết

scanf("%d",&nhom1. ngaybatdaucongtac.nam);

Nhưng đối với các thành phần không nguyên, việc làm trên có thể dẫn đến treo máy. Vì thế nên nhập số liệu vào một biến trung gian sau đó mới gán cho thành phần của cấu trúc.

Cách làm như sau :

int year;
    scanf("%d",&year);
    nhom1. ngaybatdaucongtac.nam=year;
    
  • Để tránh dài dòng khi làm việc với các thành phần cấu trúc ta có thể dùng lệnh #define. Ví dụ trong câu lênh scanf ở ví dụ trên, ta có thể viết như sau :
#define p nhom1. ngaybatdaucongtac
    .....
    scanf("%d",&p.nam);
    

Ví dụ :

Giả sử ta lập trình quản lý thông tin cán bộ. Giả sử mỗi dữ liệu của một cán bộ gồm :

Ngày tháng năm sinh.

Ngày tháng năm vào cơ quan.

Bậc lương.

Yêu cầu viết một chương trình để :

Xây dựng cấu trúc cơ sở dữ liệu cho cán bộ.

Vào số lệu của một cán bộ.

Đưa số liệu đó ra máy in.

Chương trình được viết như sau :

#include "stdio.h"
    typedef struct
    {
    int ngay;
    char thang[10];
    int nam;
    } date;
    typedef struct
    {
    date ngaysinh;
    date ngayvaocq;
    float luong;
    } canbo;
    main()
    {
    canbo p;
    printf("
 Sinh ngay : ");
    scanf("%d",&p.ngaysinh.ngay);
    printf("
 Thang : ");
    scanf("%d",&p.ngaysinh.thang);
    printf("
 Nam : ");
    scanf("%d",&p.ngaysinh.nam);
    printf("
 Vao co quan ngay : ");
    scanf("%d",&p.ngayvaocq.ngay);
    printf("
 Thang : ");
    scanf("%d",&p.ngayvaocq.thang);
    printf("
 Nam : ");
    scanf("%d",&p.ngayvaocq.nam);
    printf("
 Luong : ");
    scanf("%d",&p.luong);
    fprintf(stdprn,"
 Ngay sinh:%d%s%d",p.ngaysinh.ngay,p.ngaysinh.thang,
    p.ngaysinh.nam);
    fprintf(stdprn,"
 Ngay vao co quan:%d%s%d",p.ngayvaocq.ngay,
    p.ngayvaocq.thang,p.ngayvaocq.nam);
    fprintf(stdprn,"
 Luong : %8.2f",p.luong);
    }
    

Như đã đề cập ở các chương trước, khi sử dụng một kiểu giá trị ( ví dụ như kiểu int ) ta có thể khai báo các biến và các mảng kiểu đó. Ví dụ như khai báo :

int a,b,c[10]; 

cho ta hai biến nguyên là a,b và một mảng nguyên c có 10 phần tử.

Hoàn toàn tương tự như vậy : ta có thể sử dụng một kiểu cấu trúc đã mô tả để khai báo các cấu trúc và mảng cấu trúc.

Cách khai báo mảng cấu trúc :

struct tên_kiểu_cấu_trúc_đã_định_nghĩa tên_mảng_cấu_trúc[số phần tử của mảng];

Ví dụ :

Ví dụ 1 :

Giả sử kiểu cấu trúc canbo đã được định nghĩa như mục trên. Khi đó dòng khai báo :

struct canbo cb1,cb2,nhom1[10],nhom2[7];

sẽ cho :

Hai biến cấu trúc cb1 và cb2.

Hai mảng cấu trúc nhom1 co 10 phần tử và nhom2 có 7 phần tử và mỗi phần tử của hai nhóm này có kiểu canbo.

Ví dụ 2 :

Đoạn chương trình sau sẽ tính tổng lương cho các phần tử nhóm 1:

double tongluong=0;
    for (i=0;i<10;++i)
    tongluong+=nhom1[i].luong;
    

Không cho phép sử dụng phép toán lấy địa chỉ đối với các thành phần của mảng cấu trúc khác kiểu nguyên. Chẳng hạn không cho phép sử dụng câu lệnh sau :

scanf("%f",&nhom1[5].luong);
Trong trường hợp này ta dùng biến trung gian.

Có thể khởi đầu cho một cấu trúc ngoài, cấu trúc tĩnh, mảng cấu trúc ngoài và mảng cấu trúc tĩnh

Có thể thực hiện phép gán trên các biến và phần tử mảng cấu trúc cùng kiểu như sau :

  • Gán hai biến cấu trúc cho nhau
  • Gán biến cấu trúc cho phần tử mảng cấu trúc
  • Gán phần tử mảng cấu trúc cho biến cấu trúc
  • Gán hai phần tử mảng cấu trúc cho nhau

Mỗi một phép gán trên tương đương với một dãy phép gán các thành phần tương ứng.

Ví dụ :

Đoạn chương trình sau minh hoạ cách dùng phép gán cấu trúc để để sắp xếp n thí sinh theo thứ tự giảm của tổng điểm :

struct thisinh
    {
    char ht[25];
    float td;
    } tg,ts[100];
    for (i=1;i<=n-1;++i)
    for (j=1;j<=n;++j)
    if (ts[i].td<ts[j].td)
    {
    tg=ts[i];
    ts[i]=ts[j];
    ts[j]=tg;
    }
    

Con trỏ và địa chỉ

Ta xét ví dụ sau :

struct ngay
    {
    int ngaythu;
    char thang[10];
    int nam;
    };
    struct nhancong
    {
    char ten[20];
    char diachi[25];
    double bacluong;
    struct ngay ngaysinh;
    };
    

Nếu khai báo :

struct nhancong *p,*p1,*p2,nc1,nc2,ds[100];

ta có :

  • p, p1, p2 là con trỏ cấu trúc
  • nc1, nc2 là các biến cấu trúc
  • ds là mảng cấu trúc

Con trỏ cấu trúc dùng để lưu trữ địa chỉ của biến cấu trúc và mảng cấu trúc.

Ví dụ :

p1=&nc1;/* Gửi địa chỉ nc1 vào p1 */
    p2=&ds[4];/* Gửi địa chỉ ds[4] vào p2 */
    p=ds; /* Gửi địa chỉ ds[0] vào p */
    

Truy nhập qua con trỏ

Có thể truy nhập đến các thành phần thông qua con trỏ theo một trong hai cách sau :

Cách một :

Tên_con_trỏ->Tên_thành_phần

Cách hai :

(*Tên_con_trỏ).Tên_thành_phần

Ví dụ :

nc1.ngaysinh.nam
    p1-> ngaysinh.nam
    ds[4].ngaysinh.thang
    (*p2). ngaysinh.thang
    

Phép gán qua con trỏ

Giả sử ta gán :

p1=&nc1;
    p2=&ds[4];
    

Khi đó có thể dùng :

*p1 thay cho nc1

*p2 thay cho ds[4]

Tức là viết:

ds[5]=nc1;
    ds[4]=nc2;
    

Tương đương với :

ds[5]=*p1;
    *p2=nc2;
    

Phép cộng địa chỉ

Sau các phép gán :

p=ds;
    p2=&ds[4];
    

thì p trỏ thới ds[[0]] và p2 trỏ tới ds[4]. Ta có thể dùng các phép cộng, trừ địa chỉ để làm cho p và p2 trỏ tới các thành phần bất kỳ nào khác.

Ví dụ :

Sau các lệnh :

p=p+10;
    p2=p2-4;
    

thì p trỏ tới ds[10] còn p2 trỏ tới ds[0]

Con trỏ và mảng

Giả sử con trỏ p trỏ tới đầu mảng ds, khi đó :

  • Ta có thể truy nhập tới các thành phần cấu trúc bằng các cách sau :
+ ds[i].thành_phần ds[i].ngaysinh.nam
    + p[i].thành_phầnp[i].ngaysinh.nam
    + (p+i)->thành_phần (p+i)->ngaysinh.nam
    
  • Khi ta sử dụng cả cấu trúc thì các cách viết sau là tương đương :

ds[i]    p[i]  *(p+i)

Khi ta lập một chương trình quản lý mà bản thân số biến (cấu trúc) chưa được biết trước, nếu ta sử dụng mảng ( cấp phát bộ nhớ tĩnh ) thì ta phải sử dụng số các phần tử là tối đa. Như vậy sẽ có rất nhiều vùng nhớ được cấp phát mà không bao giờ dùng đến. Lúc đó ta có cách để cấp phát bộ nhớ động. Số vùng nhớ cấp ra đủ số biến cần dùng.

có ít nhất một thành phần là con trỏ kiểu cấu trúc đang định nghĩa gọi là cấu trúc tự trỏ.

Ví dụ :

Các cách để định nghĩa cấu trúc tự trỏ person:

Cách 1 :
    typedef struct pp
    {
    char ht[20];
    char qq[25];
    int tuoi;
    struct pp *tiep;
    } person;
    
    Cách 2 :
    typedef struct pp person
    struct pp
    {
    char ht[20];
    char qq[25];
    int tuoi;
    person *tiep;
    };
    
    Cách 3 :
    struct pp
    {
    char ht[20];
    char qq[25];
    int tuoi;
    struct pp *tiep;
    };
    typedef pp person;
    

tự trỏ được dùng để xây dựng danh sách liên kết ( móc nối ), đó là một nhóm các cấu trúc có tính chất sau : ( Móc nối theo chiều thuận ).

  • Biết địa chỉ cấu trúc đầu đang được lưu trữ trong một con trỏ nào đó.
  • Trong mỗi cấu trúc ( trừ cấu trúc cuối ) chứa địa chỉ của cấu trúc tiếp sau của danh sách.
  • cuối chứa hằng NULL.

Ví dụ :

Danh sách

Với danh sách này, ta có thể lần lượt từ cấu trúc đầu đến cấu trúc cuối theo chiều từ trên xuống dưới.

Nhóm cấu trúc móc nối theo chiều ngược có tính chất sau :

  • Biết địa chỉ cấu trúc cuối.
  • Trong mỗi cấu trúc ( trừ cấu trúc đầu ) đều chứ địa chỉ của cấu trúc trước.
  • đầu chứa hằng NULL.

Với danh sách này, ta có thể lần lượt từ cấu trúc cuối lên cấu trúc đầu theo chiều từ dưới lên trên.

Ngoài ra, ta có thể xây dựng các danh sách mà mỗi phần tử chứa hai địa chỉ của cấu trúc trước và cấu trúc sau. Với loại danh sách này, ta có thể truy nhập theo cả hai chiều trên.

Khi làm việc với danh sách móc nối, ta thường phải tiến hành các công việc sau sau :

( Giả sử ta có con trỏ p, trỏ pdau chỉ cấu trúc đầu của danh sách, con trỏ tiep là thành phần con trỏ của cấu trúc )

Tạo danh sách mới

  • Cấp phát bộ nhớ cho một cấu trúc
  • Nhập một biến cấu trúc vào vùng nhớ vừa cấp
  • Gán địa chỉ của cấu trúc sau cho thành phần con trỏ của cấu trúc trước

Duyệt qua tất cả các phần tử của danh sách

  • Đưa trỏ p về trỏ cùng cấu trúc với pdau bằng lệnh :

p=pdau
  • Để chuyển tiếp đến người tiếp theo ta dùng lệnh :

p=p->tiep
  • Dấu hiệu để biết đang xét cấu trúc cuối cùng của danh sách là :

p->tiep==NULL

Loại một cấu trúc ra khỏi danh sách

  • Lưu trữ địa chỉ của cấu trúc cần loại vào một con trỏ (Để giải phóng bộ nhớ của cấu trúc này)
  • Sửa để cấu trúc trước đó có địa chỉ của cấu trúc cần loại
  • Giải phóng bộ nhớ cấu trúc cần loại

Bổ xung hoặc chèn một cấu trúc vào danh sách

  • Cấp phát bộ nhớ và nhập bổ xung
  • Sửa thành phần con trỏ trong các cấu trúc có liên quan để đảm bảo mỗi cấu trúc chứa địa chỉ của cấu trúc tiếp theo

Hàm cấp phát bộ nhớ

void *malloc(kichthuoc_t kichthuoc);

Hàm lấy trong thư viện alloc.h hoặc stdlib.h.

kichthuoc tính bằng số by te. Hàm sẽ đưa con trỏ về vị trí ô nhớ vừa được cấp hoặc về NULL nếu không đủ bộ nhớ cần thiết. Nếu kichthuoc == 0 thì nó trả về NULL.

Ví dụ :

#include "stdio.h"
    #include "string.h"
    #include "alloc.h"
    #include "process.h"
    int main()
    {
    char *str;
    /* Cấp phát bộ nhớ cho xâu ký tự */
    if ((str = malloc(10)) == NULL)
    {
    printf("Not enough memory to allocate buffer
");
    exit(1); /* Kết thúc chương trình nếu thiếu bộ nhớ */
    }
    /* copy "Hello" vào xâu */
    strcpy(str, "Hello");
    /* Hiển thị xâu */
    printf("String is %s
", str);
    /* Giải phóng bộ nhớ */
    free(str);
    return 0;
    }
    
    

Ví dụ :

Tạo một danh sách liên kết. Các biến cấu trúc gồm các trường : Họ tên, Quê quán, tuổi, và một trường con trỏ là Tiếp.

Móc nối theo chiều thuận (Vào trước ra trước FIFO first in first out ):

#include "stdio.h"
    #include "alloc.h"
    #include "conio.h"
    #include "string.h"
    typedef struct pp
    {
    char ht[25];
    char qq[20];
    int tuoi;
    struct pp *tiep;
    } nhansu;
    main()
    {
    char tt;
    nhansu *pdau,*pcuoi,*p;
    char tam[10];
    clrscr();
    pdau=NULL;
    do
    {
    p=(nhansu*)malloc(sizeof(nhansu));
    printf("
 Ho ten : ");
     gets(p->ht);
     printf(" Que quan : ");
     gets(p->qq);
     printf(" Tuoi: ");
     gets(tam);
     p->tuoi=atoi(tam);
    if (pdau==NULL)
    { 
    pdau=p;
    pcuoi=p;
     p->tiep=NULL;
     }
     else 
     {
     pcuoi->tiep=p;
    pcuoi=p;
    p->tiep=NULL;
     } 
     printf("
Bam phim bat ky de tiep tuc, ESC de dung");
     tt=getch();
    } while(tt!=27) ;
    /* Đưa danh sách liên kết ra màn hình, trỏ pdau tro */
    printf("
 Danh sach nhu sau :
");
    p=pdau;
    while (p!=NULL)
    {
    printf("
 Ho ten: %25s Que : %20s Tuoi : %d",(*p).ht,(*p).qq,(*p).tuoi);
    p=p->tiep;
    }
    getch();
    }
    Móc nối theo chiều ngược (Vào sau ra trước LIFO last in first out ):
    #include "stdio.h"
    #include "alloc.h"
    #include "conio.h"
    #include "string.h"
    typedef struct pp
    {
    char ht[25];
    char qq[20];
    int tuoi;
    struct pp *tiep;
    } nhansu;
    main()
    {
    char tt;
    nhansu *pdau,*pcuoi,*p;
     char tam[10];
    clrscr();
    pdau=NULL;
    do
    {
    p=(nhansu*)malloc(sizeof(nhansu));
    printf("
 Ho ten : ");
     gets(p->ht);
    printf(" Que quan : ");
     gets(p->qq);
    printf(" Tuoi: ");
     gets(tam);
     p->tuoi=atoi(tam);
    if (pdau==NULL)
    { 
    pdau=p;
    pcuoi=p;
     p->tiep=NULL;
     }
     else 
     {
     p->tiep=pcuoi;
    pcuoi=p;
     } 
     printf("
Bam phim bat ky de tiep tuc, ESC de dung");
     tt=getch();
    } while(tt!=27) ;
    /* Đưa danh sách liên kết ra màn hình, trỏ pdau tro */
    printf("
 Danh sach nhu sau :
");
    p=pcuoi;
    while (p!=NULL)
    {
    printf("
 Ho ten: %25s Que : %20s Tuoi : %d",(*p).ht,(*p).qq,(*p).tuoi);
    p=p->tiep;
    }
    getch();
    }
    
0