Bài 7: CON TRỎ VÀ MẢNG
7.2. Con trỏ và mảng một chiều
Trong C có mối quan hệ chặt chẽ giữa con trỏ và mảng: các phần tử của mảng có thể được xác định nhờ chỉ số hoặc thông qua con trỏ.
7.2.1. Phép toán lấy địa chỉ:
Giả sử ta có khai báo: double b[20]; Khi đó phép toán: &b[9] sẽ cho địa chỉ của phần tử b[9].
7.2.2. Tên mảng là một hằng địa chỉ:
Khi chúng ta khai báo: float a[10]; máy sẽ bố trí bố trí cho mảng a mười khoảng nhớ liên tiếp, mỗi khoảng nhớ là 4 byte. Như vậy, nếu biết địa chỉ của một phần tử nào đó của mảng a, thì ta có thể dễ dàng suy ra địa chỉ của các phần tử khác của mảng.
Trong C ta có:
a tương đương với &a[0]
a+i tương đương với &a[i]
*(a+i) tương đương với a[i]
7.2.3. Con trỏ trỏ tới các phần tử của mảng một chiều:
Khi con trỏ pa trỏ tới phần tử a[k] của mảng a thì:
pa+i trỏ tới phần tử thứ i sau a[k], có nghĩa là nó trỏ tới a[k+i].
pa-i trỏ tới phần tử thứ i trước a[k], có nghĩa là nó trỏ tới a[k-i].
*(pa+i) tương đương với pa[i].
Như vậy, sau hai câu lệnh:
float a[20],*pa;
pa=a;// pa=&a[0];
thì bốn cách viết sau có tác dụng như nhau và cùng truy cập đến phần tử thứ i của mảng a:
a[i] *(a+i) pa[i] *(pa+i)
Ví dụ:Vào số liệu cho các phần tử của một mảng và tính tổng các phần tử của chúng:
Cách 1:
#include "stdio.h"
main()
{ float a[4],tong;
int i;
for (i=0;i<4;++i)
{ printf("\n a[%d]=",i);
scanf("%f",(a+i+i));
} tong=0;
for (i=0;i<4;++i) tong+=a[i]a[i];
printf("\n Tong cac phan tu mang la:%8.2f ",tong);
} Cách 2:
#include "stdio.h"
main()
{ float a[4],tong, *troa;
int i;
troa=a;
for (i=0;i<4;++i)
{ printf("\n a[%d]=",i); scanf("%f",&ttroa[+i]);
} tong=0;
for (i=0;i<4;++i) tong+=troa[i];
printf("\n Tong cac phan tu mang la:%8.2f ",tong);
} Cách 3:
#include "stdio.h"
main() {
float a[4],tong,*troa;
int i;
troa=a;
for (i=0;i<4;++i) {
printf("\n a[%d]=",i);
scanf("%f",troa+i);
}
tong=0;
for (i=0;i<4;++i)
tong+=*(troa+i);
printf("\n Tong cac phan tu mang la:%8.2f ",tong);
}
Chú ý: Mảng một chiều và con trỏ tương ứng phải cùng kiểu dữ liệu.
7.2.4. Mảng, con trỏ và xâu ký tự:
Như ta đã biết trước đây, xâu ký tự là một dãy ký tự đặt trong hai dấu nháy kép, ví dụ như xâu ký tự: "Viet nam".
Khi gặp một xâu ký tự, máy sẽ cấp phát một khoảng nhớ cho một mảng kiểu char đủ lớn để chứa các ký tự của xâu và chứa thêm ký tự '\0' dùng làm ký tự kết thúc của một xâu ký tự. Mỗi ký tự của xâu được chứa trong một phần tử của mảng.
Cũng giống như tên mảng, xâu ký tự là một hằng địa chỉ biểu thị địa chỉ phần tử đầu tiên của mảng chứa nó.
Vì vậy nếu ta khai báo biến xau như một con trỏ kiểu char:
char *xau=”Viet Nam”;
thì phép gán: xau="Ha noi" ; là hoàn toàn có nghĩa. Sau khi thực hiện câu lệnh này trong con trỏ xau sẽ có địa chỉ đầu của mảng (kiểu char) đang chứa xâu ký tự bên phải. Khi đó các câu lệnh: puts("Ha noi");
puts(xau);
sẽ có cùng một tác dụng là cho hiện lên màn hình dòng chữ Ha noi.
Mảng kiểu char thường dùng để chứa một dãy ký tự. Ví dụ, để nạp từ bàn phím tên của một người ta dùng một mảng kiểu char với độ dài 25, ta sử dụng các câu lệnh sau:
char ten[25];
printf("\n Ho ten:");
gets(ten);
Bây giờ chúng ta sẽ xem xét giữa mảng kiểu char và con trỏ kiểu char có những gì giống và khác nhau. Để thấy được sự khác nhau của chúng, ta đưa ra sự so sánh sau:
char *xau, ten[15];
ten="Ha noi" ; (1) gets(xau); (2) xau= "Ha noi" ; gets(ten) ;
Các câu lệnh (1) và (2) là không hợp lệ. Câu lệnh (1) sai vì ten là một hằng địa chỉ và ta không thể gán một hằng địa chỉ này cho một hằng địa chỉ khác. Câu lệnh (2) không thực hiện được bởi mục đích của câu lệnh là đọc từ bàn phím một dãy ký tự và lưu vào một vùng nhớ mà con trỏ xau trỏ tới vì địa chỉ vùng nhớ của con trỏ xau còn chưa xác định. Nếu trỏ xau đã trỏ tới một vùng nhớ nào đó thì câu lệnh (2) hoàn toàn thực hiện được. Chẳng hạn như sau khi thực hiện câu lệnh:
xau=ten; thì cách viết: gets(ten) ; và gets(xau); đều cho phép nhập xâu kí tự vào con trỏ xau.