Mảng là một tập hợp các dữ liệu cùng kiểu với độ dài cố định.. Mảng thường được khai báo với kiểu dữ liệu của mảng, tên của mảng và số phần tử liên quan được đặt trong mảng. Ví dụ khai báo mảng sau:
char arr [5];
khai báo một mảng char với tên ‘arr’ với không gian cho 5 phần tử char trong bộ nhớ như trong Hình 3.23
Hình 3.23: Minh họa mảng trong "C"
Phương thức đầu tiên là khởi tạo toàn bộ mảng tại thời điểm khai báo mảng. Phương pháp thứ hai là khởi tạo có chọn lọc trong đó bất kỳ thành viên nào cũng có thể được khởi tạo hoặc thay đổi với một giá trị.
Hình 3.24: Địa chỉ phần tử mảng và mối quan hệ nội dung //Initialisation of array at the time of declaration
unsigned char arr[5] = {5, 10, 20, 3, 2};
unsigned char arr[ ] = {5, 10, 20, 3, 2};
//Selective initialisation unsigned char arr[5];
197 arr[0] = 5;
arr[1] = 10;
arr[2] = 20;
arr[3] = 3;
arr[4] = 2;
Con trỏ là một tính năng linh hoạt đồng thời là tính năng nguy hiểm nhất, có khả năng tạo ra các sự cố tiềm ẩn dẫn đến sự cố phần mềm, nếu không được sử dụng đúng cách. Con trỏ là một kỹ thuật dựa trên bộ nhớ để truy cập và sửa đổi biến. Con trỏ rất hữu ích trong những trường hợp
- Truy cập và sửa đổi các biến.
- Tăng tốc độ thực hiện.
- Truy cập nội dung trong một khối bộ nhớ.
- Truyền biến cho các hàm bằng cách loại bỏ việc sử dụng bản sao biến cục bộ.
- Cấp phát bộ nhớ động.
Để hiểu khái niệm con trỏ, chúng ta hãy xem tổ chức bộ nhớ dữ liệu của bộ xử lý. Đối với bộ xử lý / bộ điều khiển có RAM bên trong có thể lập trình 128 byte (ví dụ AT89C51), bộ nhớ được tổ chức dưới dạng
Hình 3.25: Tổ chức bộ nhớ dữ liệu cho 8051
198
Hình 3.26: Mối quan hệ giữa tên biến, địa chỉ và dữ liệu được giữ bởi biến
Nội dung của vị trí bộ nhớ 0x45 (đại diện cho đầu vào biến) có thể được truy cập và sửa đổi bằng cách sử dụng một con trỏ có cùng loại với biến (char cho đầu vào biến trong ví dụ). Việc truy cập này được thực hiện bằng phương pháp sau.
char input; //Declaring input as character variable
char *p; //Declaring a character pointer p (* denotes p is a pointer) p = &input //Assigns the address of input as content to p
Tương tự được biểu diễn bằng sơ đồ như:
Hình 3.27: Kỹ thuật truy cập bộ nhớ dựa trên con trỏ
Trình biên dịch gán một bộ nhớ cho biến con trỏ ký tự p. Đặt nó là 0x00 (Giá trị tùy ý được chọn để minh họa) và vị trí bộ nhớ giữ địa chỉ bộ nhớ của đầu vào biến (0x45) làm nội dung. Tính năng con trỏ trong “C” giống như kỹ thuật đánh địa chỉ gián tiếp được sử dụng trong 8051.
MOV R0, #45H
MOV A,@R0 ; R0 & R1 are the indirect addressing registers.
là một ví dụ cho việc sử dụng con trỏ bộ nhớ 8 bit trong 8051 MOV DPTR, #0045H
199
MOV A,@DPTR ; DPTR is the 16bit indirect addressing register
là một ví dụ cho hoạt động con trỏ bộ nhớ 16 bit. Hình thức chung của việc khai báo một con trỏ trong ‘C’ là
data type *pointer; //’data type’ is the standard data type like //int, char, fl oat etc… supported by ‘C’ language.
Biểu tượng * (dấu hoa thị) thông báo cho trình biên dịch rằng con trỏ biến là biến con trỏ. Giống như bất kỳ biến nào khác, con trỏ cũng có thể được khởi tạo trong chính khai báo của nó.
E.g. char x, y;
char *ptr = &x;
Nội dung được trỏ bởi một con trỏ được sửa đổi / truy xuất bằng cách sử dụng * làm tiền tố cho con trỏ.
E.g. char x=5, y=56;
char *ptr=&x; //ptr holds address of x *ptr = y; //x = y
Con trỏ: Toán tử số học và quan hệ trên ngôn ngữ C
Bổ sung số nguyên với con trỏ. Ví dụ: ptr + 2 (cần lưu ý rằng con trỏ được chuyển tiếp theo chiều dài lưu trữ được trình biên dịch hỗ trợ cho kiểu dữ liệu của con trỏ nhân với số nguyên. Ví dụ: đối với con trỏ nguyên có kích thước lưu trữ của int = 4, phép cộng ở trên sẽ tăng con trỏ lên 4 × 2 = 8)
- Phép trừ số nguyên từ con trỏ, ví dụ: ptr - 2 (quy tắc trên được áp dụng)
- Hoạt động tăng dần của con trỏ, ví dụ: ++ ptr và ptr ++ (tùy thuộc vào loại con trỏ, bối cảnh gia tăng ++ thay đổi). Đối với toán tử con trỏ ++, toán tử tăng con trỏ lên 1 và đối với con trỏ nguyên, con trỏ được tăng theo kích thước lưu trữ của số nguyên được trình biên dịch hỗ trợ (ví dụ: con trỏ ++ dẫn đến con trỏ + 4 nếu kích thước cho số nguyên được hỗ trợ bởi trình biên dịch là 4)
- Hoạt động giảm dần của con trỏ, ví dụ: - - ptr và ptr - - (quy tắc bối cảnh cho hoạt động giảm dần giống như hoạt động gia tăng)
- Phép trừ con trỏ, ví dụ: ptr1 - ptr2
- So sánh hai con trỏ sử dụng toán tử quan hệ, ví dụ: ptr1> ptr2, ptr1 <ptr2, ptr1 = = ptr2, ptr1! = ptr2 vv (chỉ so sánh các con trỏ cùng loại sẽ cho kết quả có ý nghĩa)
Một vài điểm quan trọng trên con trỏ
200
- Lệnh * ptr ++ chỉ tăng con trỏ chứ không phải nội dung được trỏ bởi con trỏ ‘ptr’ và *ptr- - giảm con trỏ không phải nội dung được trỏ bởi con trỏ ‘ptr’
- Lệnh (* ptr) ++ tăng nội dung được trỏ bởi con trỏ ptr chứ không phải con trỏ ptr. (*ptr) - - giảm nội dung được trỏ bởi con trỏ ‘ptr’ và không phải con trỏ ‘ptr’
- Không thể sử dụng một con trỏ đúc kiểu trong biểu thức gán và không thể tăng hoặc giảm, ví dụ: ((int * ptr)) ++; sẽ không hoạt động theo cách mong đợi
- Con trỏ Null là một con trỏ chứa một giá trị đặc biệt gọi là NULL không phải là địa chỉ của bất kỳ biến hoặc hàm nào hoặc địa chỉ bắt đầu của khối bộ nhớ được phân bổ trong phân bổ bộ nhớ động
- Con trỏ của mỗi loại có thể có một con trỏ null liên quan viz. có thể có con trỏ null kiểu ký tự, con trỏ null kiểu số nguyên, v.v.
- NULL là một macro tiền xử lý được định nghĩa theo nghĩa đen là zero hoặc ((void*)0).
#define NULL 0 hoặc #define NULL ((void *) 0)
- NULL là một số không đổi và cả hai có thể được sử dụng thay thế cho nhau như các hằng con trỏ Null
- Một con trỏ NULL có thể được kiểm tra bởi toán tử if (). Xem ví dụ sau:
if (ptr) //ptr is a pointer declared printf (“ptr is not a NULL pointer”);
else
printf (“ptr is a NULL pointer”);
Câu lệnh if (ptr) được chuyển đổi thành if (ptr! = 0) bởi trình biên dịch (chéo). Ngoài ra, bạn có thể trực tiếp sử dụng câu lệnh if (ptr! = 0) trong chương trình của mình để kiểm tra con trỏ NULL.
- Con trỏ Null là một khái niệm ngôn ngữ C và có giá trị bên trong không quan trọng. NULL luôn đảm bảo 0 cho bạn nhưng con trỏ Null không cần.