Bài giảng Ngôn ngữ lập trình C - Chương 3: Mảng và con trỏ cung cấp cho người học các kiến thức: Mảng, con trỏ, liên hệ giữa mảng và con trỏ, cấp phát bộ nhớ động, xâu ký tự. Đây là một tài liệu hữu ích dành cho các bạn sinh viên ngành Công nghệ thông tin dùng làm tài liệu học tập và nghiên cứu.
Trang 1Ngôn ngữ lập trình C
Trang 3 Mỗi phần tử của mảng lưu trữ 1 giá trị.
Mỗi một phần tử của mảng được coi như 1 biến.
Có bao nhiêu kiểu biến thì có bấy nhiêu kiểu mảng.
Trang 43.1 Mảng
Số chiều và kích thước của mảng
Ví dụ các khai báo: int a[10],b[4][2];
Trang 5 Khi chỉ số vượt ra ngoài kích thước mảng, trình biên dịch vẫn biên
dịch thành công, tuy nhiên khi thực hiện chương trình sẽ có lỗi.
Kích thước của mảng phải là một hằng số.
Trang 63.1 Mảng
Khởi tạo giá trị ban đầu cho biến mảng
Để khởi tạo giá trị ban đầu cho biến mảng, ta có thể sử dụng biểu thức hằng hoặc các câu lệnh gán Ví dụ:
Trang 83.1 Mảng
Sơ đồ khối sau thể
hiện thuật toán tìm
kiếm nhị phân Hãy
kiếm xem key có
xuất hiện trong dãy
không, nếu xuất
Mid = (Low + High)/2;
Key = A[Mid] Pos = MId Flag = 1;
Key < A[Mid] High = Mid - 1;
Low = Mid +1;
Flag = 0
Print “Khong tim thay”
Print Key xuat hien tai vi tri Pos
Trang 93.2 Con trỏ
Khái niệm: Là một đối tượng dữ liệu mà giá trị của
nó là địa chỉ của một đối tượng khác
Không lưu trữ giá trị của biến đó
Như vậy, nếu một biến chứa địa chỉ của một biến
khác, thì biến này được gọi là con trỏ, trỏ đến biếnthứ hai
Kích thước của biến con trỏ không phụ thuộc vào kiểu dữ liệu, luôn có kích thước cố định là 2 bytes hoặc 4 bytes
Trang 133.2 Con trỏ
Các phép toán trên con trỏ
Phép gán: chỉ thực hiện với các con trỏ cùng kiểu Muốn gán các con trỏ khác kiểu phải dùng phép ép kiểu.
Trang 143.2 Con trỏ
Các phép toán trên con trỏ
Phép truy cập bộ nhớ: Con trỏ float trỏ tới địa chỉ dài 4 byte, con trỏ int trỏ tới địa chỉ dài 2 byte, con trỏ char trỏ tới địa chỉ dài 1 byte.
float *pf; int *pi; char *pc;
Phép so sánh: so sánh các con trỏ cùng kiểu
p1<p2: địa chỉ p1 trỏ tới thấp hơn địa chỉ p2 trỏ tới.
p1==p2: địa chỉ p1 trỏ tới cũng là địa chỉ p2 trỏ tới.
p1>p2: địa chỉ p1 trỏ tới cao hơn địa chỉ p2 trỏ tới
Trang 153.2 Con trỏ
Con trỏ NULL
Con trỏ không chứa địa chỉ của bất kỳ vùng nhớ nào.
Nên khởi tạo giá trị NULL hoặc địa chỉ của vùng nhớ nào đó cho biến trỏ lúc khai báo.
Con trỏ void
Cú pháp: void *tên_con_trỏ;
Đây là con trỏ đặc biệt, con trỏ không kiểu, nó có thể nhận địa
chỉ kiểu bất kỳ
Ví dụ: void *pa; float a[20][30]; pa=a;
Thường được dùng làm tham số hình thức, nhận bất kỳ địa chỉ
kiểu nào từ tham số thực
Chú ý: Các phép toán tăng giảm địa chỉ, so sánh và truy cập
bộ nhớ không dùng được trên con trỏ void.
Trang 163.2 Con trỏ
Mảng con trỏ
Kiểu phần tử của biến mảng có thể là kiểu con trỏ Các biến mà địa chỉ chứa trong các phần tử mảngcon trỏ là một mảng, nhưng có vùng nhớ không liên tục
Thường dùng để lưu mảng của chuỗi
Ví dụ:
char *s[5] = {“Orange”, “Mango”, “Coconut”,
“Longan”, “Banana”};
Mỗi phần tử của s trỏ đến char * (1 chuỗi)
Mảng không chứa chuỗi, chỉ trỏ đến chuỗi
Trang 173.2 Con trỏ
Con trỏ đa cấp
Bản thân biến trỏ cũng có địa chỉ, do đó, có thể chứa địa chỉ của nó trong 1 biến trỏ khác, gọi là con trỏ trỏ đến
con trỏ, hay con trỏ 2 cấp
Số lượng dấu “*” xác định cấp của 1 biến trỏ
Con trỏ 2 cấp có liên quan mật thiết với mảng 2 chiều
Trang 183.3 Liên hệ giữa mảng và con trỏ
Con trỏ và mảng 1 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ỏ.
Phép toán lấy địa chỉ:
Giả sử ta có khai báo: double b[20];
phép toán: &b[9] sẽ cho địa chỉ của phần tử b[9]
Tên mảng là một hằng địa chỉ:
a &a[0]
a+i &a[i]
*(a+i) a[i]
Trang 193.3 Liên hệ giữa mảng và con trỏ
Con trỏ và mảng nhiều chiều
Giả sử, mảng hai chiều a[2][3] có 6 phần tử ứng với
sáu địa chỉ liên tiếp trong bộ nhớ được xếp theo thứ
tự sau:
Tên mảng a biểu thị địa chỉ đầu tiên của mảng.
Phép cộng địa chỉ: C coi mảng hai chiều là mảng
(một chiều) của mảng, như vậy khai báo float a[2][3];
thì a là mảng mà mỗi phần tử của nó là một dãy 3 sốthực (một hàng của mảng)
Trang 203.3 Liên hệ giữa mảng và con trỏ
Do đó:
a trỏ phần tử thứ nhất của mảng: phần tử a[0][0]
a+1 trỏ phần tử đầu hàng thứ hai của mảng: phần tử a[1][0],
a+i trỏ phần tử đầu hàng thứ i của mảng: phần tử a[i][0].
Khi dùng con trỏ để duyệt mảng, ta cần làm như sau:
float *pa,a[2][3]; pa=(float*)a;
pa &a[0][0]
pa+1 &a[0][1]
pa+2 &a[0][2]
pa+3 &a[1][0]
pa+4 &a[1][1]
pa+5 &a[1][2]
Trang 213.4 Cấp phát bộ nhớ động
Hàm malloc và calloc cho phép cấp phát các vùng
nhớ ngay trong lúc chạy chương trình Cú pháp:
void *malloc (size_t size);
void *calloc (size_t nItems, size_t size)
Hàm calloc cấp phát vùng nhớ và khởi tảo tất cả các
bit trong vùng nhớ mới cấp phát về 0
Hàm malloc chỉ cấp phát vùng nhớ
21
Trang 22 Hàm này giải phóng không gian được trỏ bởi ptr,
để dùng cho tương lai
ptr phải được dùng trước đó với lời gọi hàm
malloc(), calloc(), hoặc realloc()
22
Trang 233.4 Cấp phát bộ nhớ động
Cấp phát lại vùng nhớ: realloc
C dung hàm realloc để cấp phát lại vùng nhớ, thực hiện sao chép nội dung của vùng nhớ cũ sang vùng nhớ mới.
Cú pháp:
void *realloc(void *block, size_t size);
Trang 24// p = (int *)calloc (n, sizeof(int);
for(i=0; i<n; i++) {
printf(“\n Enter value: %d : ”, i+1);
scanf(“%d”,p+i);
} for(i=1; i<10; i++) printf(“%d\n“,i, *(p+i));
Trang 263.4 Cấp phát bộ nhớ động
Ví dụ:
Nhập vào một ma trận A cấp mxn (m,n nhập từ bànphím) Tìm và in ra phần tử lớn nhất của hàng k (k nhập từ bàn phím)
Nhập vào một ma trận A cấp mxn (m,n nhập từ bànphím) Tìm và in ra hàng có tổng các phần tử lớn
nhất
Nhập vào một ma trận A cấp mxn (m,n nhập từ bànphím) Kiểm tra xem ma trận A có phải là ma trận tam giác trên không?
Trang 273.5 Xâu ký tự
Khái niệm:
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 lượng ô nhớ
kiểu char đủ để chứa các ký tự của xâu và chứa thêm ký
tự ‘\0’
Xâu ký tự là một hằng địa chỉ
Trang 28char firstname[10] = "Bill";
char lastname[] = "Clinton";
char *namept = "Bill Clinton";
Trang 29firstname = "Bill Clinton";
Nhận và lưu vào mảng chuỗi ký tự được gõ từ bànphím bằng hàm gets()
gets(firstname );
Trang 30xau= "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ỉ, 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