Ngôn ngữ lập trình C - Chương 8: Cấu trúc

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ấu trúc còn gọi là bản ghi

trong một số ngôn ngữ khác, chẳng hạn như PASCAL.

Cấu trúc 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.

pdf21 trang | Chuyên mục: C/C++ | Chia sẻ: dkS00TYs | Lượt xem: 1614 | Lượt tải: 0download
Tóm tắt nội dung Ngôn ngữ lập trình C - Chương 8: Cấu trúc, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
 8: Cấu trúc 
103 
 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; 
Chú ý : 
 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. 
8.5. Khởi đầu một cấu trúc : 
 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 
Chương 8: Cấu trúc 
104 
8.6. Phép gán cấu trúc : 
 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; 
 } 
8.7. Con trỏ cấu trúc và địa chỉ cấu trúc : 
8.7.1. Con trỏ và địa chỉ : 
 Ta xét ví dụ sau : 
struct ngay 
 { 
Chương 8: Cấu trúc 
105 
 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 */ 
8.7.2. 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 : 
Chương 8: Cấu trúc 
106 
 (*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 
8.7.3. 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; 
8.7.4. 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 : 
Chương 8: Cấu trúc 
107 
 p=p+10; 
 p2=p2-4; 
 thì p trỏ tới ds[10] còn p2 trỏ tới ds[0] 
8.7.5. 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ần p[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) 
8.8. Cấu trúc tự trỏ và danh sách liên kết : 
 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ấu trúc 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]; 
Chương 8: Cấu trúc 
108 
 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; 
 Cấu trúc 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. 
 Cấu trúc cuối chứa hằng NULL. 
Ví dụ : 
Chương 8: Cấu trúc 
109 
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. 
 Cấu 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 
......... 
NULL 
Pdau 
Chương 8: Cấu trúc 
110 
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\n"); 
 exit(1); /* Kết thúc chương trình nếu thiếu bộ nhớ */ 
 } 
Chương 8: Cấu trúc 
111 
 /* copy "Hello" vào xâu */ 
 strcpy(str, "Hello"); 
 /* Hiển thị xâu */ 
 printf("String is %s\n", 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; 
Chương 8: Cấu trúc 
112 
 do 
 { 
 p=(nhansu*)malloc(sizeof(nhansu)); 
 printf("\n 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("\nBam 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("\n Danh sach nhu sau :\n"); 
 p=pdau; 
 while (p!=NULL) 
 { 
Chương 8: Cấu trúc 
113 
 printf("\n 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("\n Ho ten : "); 
 gets(p->ht); 
 printf(" Que quan : "); 
Chương 8: Cấu trúc 
114 
 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("\nBam 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("\n Danh sach nhu sau :\n"); 
 p=pcuoi; 
 while (p!=NULL) 
 { 
 printf("\n Ho ten: %25s Que : %20s Tuoi : 
 %d",(*p).ht,(*p).qq,(*p).tuoi); 
 p=p->tiep; 
 } 
 getch(); 
 } 

File đính kèm:

  • pdfCHUONG_8.pdf