SỬ DỤNG HÀM TRONG C
CHƯƠNG 7 KIỂU DỮ LIỆU CẤU TRÚC
7.3 CON TRỎ CẤU TRÚC
C hỗ trợ con trỏ cấu trúc. Trong đó, một con trỏ cấu trúc được định nghĩa là
một biến lưu địa chỉ của biến cấu trúc khác.
7.3.1 Con trỏ tới biến cấu trúc
Giống như các kiểu con trỏ khác, con trỏ cấu trúc được khai báo bằng cách đặt dấu * trước tên của biến cấu trúc như sau:
<Tên kiểu cấu trúc> *<Tên biến trỏ>;
Hoặc:
struct <Tên kiểu cấu trúc> *<Tên biến trỏ>;
Trường hợp kiểu cấu trúc được định nghĩa bằng từ khóa typedef thì sử dụng
dạng khai báo
thứ nhất:
typedef struct{
int numerator, denominator; } Fraction;
141
Fraction *ptr_fact;
Ngược lại, sử dụng dạng khai báo thứ hai:
struct Fraction{
int numerator, denominator;
}; struct Fraction *ptr_fact;
Giả sửfactđược khai báo là một biến cấu trúc Fraction: struct Fraction fact;
Khi đó, nếu muốn con trỏ ptr trỏ đến biến cấu trúc fact, ta sẽ gán giá trị cho con trỏ này bằng địa chỉ của biến fact(sử dụng phép toán lấy địa chỉ &), cụ thể như sau:
ptr_fact =&fact
Để truy cập đến các thành phần của một con trỏ cấu trúc, có thể sử dụng một trong các cách sau đây.
Cách 1 : Sử dụng toán tử trỏ -> (là tổ hợp của dấu trừ (-) và dấu lớn hơn ( >)).
Chẳng hạn,
để truy cập đến các thành phần tử số (numerator) và mẫu số (denominator) của con trỏptr_factở trên, ta sử dụng các câu lệnh:
ptr_fact->numerator;
ptr_fact->denominator;
Cách 2: Sử dụng toán tử *trước tên biến trỏ và toán tử thành viên (.) như bình thường, ví
dụ:
(*ptr_fact).numerator;
(*ptr_fact).denominator;
Chú ý: Do toán tử chấm . có độ ưu tiên cao hơn toán tử *nên*ptr_fact bắt buộc phải đặt trong ngoặc. Nếu không có dấu ngoặc, trình biên dịch sẽ sinh ra một lỗi, vì toán tử chấm không thể áp dụng trên biến con trỏ ptr_fact.
Cũng giống như với các kiểu dữ liệu khác, có thể sử dụng con trỏ cấu trúc để truy xuất tới các biến hoặc các phần tử mảng một cách gián tiếp.
Ví dụ 7.5 sau đây sẽ khai báo một biến có kiểu cấu trúc sinh viên, sử dụng một con trỏ để nhập dữ liệu cho biến cấu trúc, sau đó kiểm tra lại bằng cách hiển thị thông tin của biến cấu trúc đó ra màn hình.
Ví dụ 7.5:
#includ e<stdio .h>
struct Student {
char id[5];
char name[30];
birth_yearint
;
float avg_mark;
};
int main(){
struct Student st;
struct Student *ptr_st = &st;
printf("Enter the information:\n");
printf("\nStudent ID: "); scanf("%s",
&ptr_st->id); fflush(stdin);
printf("\nName: "); gets(ptr_st->name);
142
printf("\nYear of birth: ");
scanf("%d",&ptr_st-
>birth_year);
printf("\nAverage Mark: ");
scanf("%f", &ptr_st-
>avg_mark);
fflush(stdin);
printf("\n--- ---\n"); printf("Display entered information:\n");
printf("%s\n", st.id);
printf("%s\n",st.name);
printf("%d\n",st.bir th_year);
printf("%0.2f\n",st.avg_m ark); }
Kết quả thực hiện của chương trình trên như sau:
Enter the information:
Student ID: 102 Name: Le Thi Hanh Phuc
Year of birth: 1992
Average Mark:
6.5
--- ---
Display entered information:
102 Le Thi Hanh Phuc 1992 6.50
7.3.2 Con trỏ và mảng cấu trúc
Tương tự như các kiểu dữ liệu khác, con trỏ và mảng cấu trúc có mối liên hệ chặt chẽ với nhau. Tên của một mảng cấu trúc là một con trỏ trỏ đến phần tử đầu tiên của mảng đó. Nếu str_ary là một mảng cấu trúc thì:
Địa chỉ phần tử đầu tiên trong mảng là &str_ary[0] hoặc đơn giản là str_ary. Địa chỉ phần tử thứ i bất kỳ được biểu diễn là &str_ary[i]
hoặc (str_ary+i). Điều này sẽ được thể hiện rõ hơn trong ví dụ 7.6 sau đây.
Ví dụ 7.6:
#includ e<stdio
.h>
typedef struct{
int numerator,
denominator; }
Fraction;
int main(){
Fraction fact_ary[5]={{1,2},{1,3},{1,4}, {1,5},{1,6}};
int i;
for (i=0; i<5; i++){
printf("&fact_ary[%d]=%d, " , i, &fact_ary[i]);
printf("(fact_ary+%d) = %d\n", i, fact_ary+i);
} }
Kết quả thực hiện của chương trình như sau:
&fact_ary[0] = 2293568, (fact_ary+0) = 2293568 143
&fact_ary[1] = 2293576, (fact_ary+1) = 2293576
&fact_ary[2] = 2293584, (fact_ary+2) = 2293584
&fact_ary[3] = 2293592, (fact_ary+3) = 2293592
&fact_ary[4] = 2293600, (fact_ary+4) = 2293600 Từ kết quả thực hiện của chương trình cho thấy &fact_ary[i] và (fact_ary+i) đều
trả về địa chỉ phần tử thứ i của mảng.
Từ đó, có thể sử dụng con trỏ để cấp phát bộ nhớ động cho mảng cấu trúc như trong ví dụ
7.7 sau đây:
Ví dụ 7.7:
#includ e<stdio .h>
typedef struct{
int numerator,
denominator; }
Fraction;
int main(){
Fraction
*fact_ary
; int i, n;
printf("Enter n = "); scanf("%d", &n);
fact_ary = (Fraction*)
malloc(n*sizeof(Fraction)); if (fact_ary==NULL){
printf(“Not enough memory!”); return } 1;
printf("Input the fraction
%dth: ",i+1);
printf("\nnumerator = ");
scanf("%d",
&fact_ary[i].numerator);
printf("\ndenominator=");
scanf("%d",&fact_ary[i].den ominator);
}
printf("\n--- ---\n"); printf("Display entered fractions:\n");
for (i=0; i<n; i++){
printf("%d/
%d\n",fact_ary[i].numerator, fact_ary[i].denominator);
} free(fact_ary);
}
Kết quả thực hiện của chương trình như sau:
Enter n = 3
Input the fraction 1:
numerator = 1
denominator= 2
Input the fraction 2:
numerator = 1
denominator= 3
Input the fraction 3:
numerator = 1
denominator= 4
144
--- ---
Display entered fractions:
1/2 1 / 3
1 / 4
Chú ý rằng , để cấp phát bộ nhớ động cho một mảng nói chung và mảng cấu trúc nói riêng cần thực hiện các thao tác cơ bản sau đây:
Khai báo mảng dưới dạng con trỏ:
Fraction
*fact_ary;
Cấp phát bộ nhớ động sử dụng hàm malloc() hoặc calloc():
fact_ary = (Fraction*) malloc(n*sizeof(Fraction));
Hoặc
fact_ary = (Fraction*)
calloc(n,sizeof(Fraction)); Hủy khối bộ nhớ đã cấp phát cho mảng khi không cần dùng đến nó nữa:
free(fact_ary);