Tài liệu Session 13 - Concept pdf

19 282 0
Tài liệu Session 13 - Concept pdf

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Bài 13 Con trỏ Mục tiêu: Kết thúc học này, bạn có thể:  Hiểu trỏ gì, trỏ sử dụng đâu  Biết cách sử dụng biến trỏ toán tử trỏ  Gán giá trị cho trỏ  Hiểu phép toán số học trỏ  Hiểu phép toán so sánh trỏ  Biết cách truyền tham số trỏ cho hàm  Hiểu cách sử dụng trỏ kết hợp với mảng chiều  Hiểu cách sử dụng trỏ kết hợp với mảng đa chiều  Hiểu cách cấp phát nhớ thực Giới thiệu Con trỏ cung cấp cách thức truy xuất biến mà không tham chiếu trực tiếp đến biến Nó cung cấp cách thức sử dụng địa Bài đề cập đến khái niệm trỏ cách sử dụng chúng C 13.1 Con trỏ gì? Một trỏ biến, chứa địa vùng nhớ biến khác, không lưu trữ giá trị biến Nếu biến chứa địa biến khác, biến gọi trỏ đến biến thứ hai Một trỏ cung cấp phương thức gián tiếp để truy xuất giá trị phần tử liệu Xét hai biến var1 var2, var1 có giá trị 500 lưu địa 1000 nhớ Nếu var2 khai báo trỏ tới biến var1,ơ biểu diễn sau: Vị trí Bộ nhớ 1000 1001 1002 1108 Giá trị lưu trữ 500 Tên biến var1 1000 var2 Ở đây, var2 chứa giá trị 1000, địa biến var1 Các trỏ trỏ đến biến kiểu liệu sở int, char, hay double liệu có cấu trúc mảng Con trỏ 181 13.1.2 Tại trỏ dùng? Con trỏ sử dụng số trường hợp sau:  Để trả nhiều giá trị từ hàm  Thuận tiện việc truyền mảng chuỗi từ hàm đến hàm khác  Sử dụng trỏ để làm việc với phần tử mảng thay truy xuất trực tiếp vào phần tử  Để cấp phát nhớ động truy xuất vào vùng nhớ cấp phát (dynamic memory allocation) 13.2 Các biến trỏ Nếu biến sử dụng trỏ, phải khai báo trước Câu lệnh khai báo trỏ bao gồm kiểu liệu bản, dấu *, tên biến Cú pháp tổng quát để khai báo biến trỏ sau: type *name; Ở type kiểu liệu hợp lệ bất kỳ, name tên biến trỏ Câu lệnh khai báo nói với trình biên dịch name sử dụng để lưu địa biến có kiểu liệu type Trong câu lệnh khai báo, * xác định biến trỏ khai báo Trong ví dụ var1 var2 ỏ trên, var2 trỏ giữ địa biến var1 có kiểu int, khai báo sau: int *var2; Bây giờ, var2 sử dụng chương trình để trực tiếp truy xuất giá trị var1 Nhớ rằng, var2 khơng phải có kiểu liệu int trỏ trỏ đến biến có kiểu liệu int Kiểu liệu sở trỏ xác định kiểu biến mà trỏ trỏ đến Về mặt kỹ thuật, trỏ có kiểu trỏ đến vị trí nhớ Tuy nhiên, tất phép toán số học trỏ có liên quan đến kiểu sở nó, khai báo kiểu liệu trỏ cách rõ ràng điều quan trọng 13.3 Các tốn tử trỏ Có hai tốn tử đặc biệt dùng với trỏ: * & Tốn tử & tốn tử ngơi trả địa tốn hạng Ví dụ, var2 = &var1; lấy địa vùng nhớ biến var1 gán cho var2 Địa vị trí ô nhớ bên máy tính biến var1 khơng làm với giá trị var1 Tốn tử & hiểu trả “địa của” Vì vậy, phép gán có nghĩa “var2 nhận địa var1” Trở lại, giá trị var1 500 dùng vùng nhớ 1000 để lưu giá trị Sau phép gán trên, var2 có giá trị 1000 Tốn tử thứ hai, tốn tử *, dùng với trỏ phần bùbổ xung tốn tử &, tốn tử * Nó tốn tử ngơi trả giá trị chứa vùng nhớ trỏ giá trị biến trỏ Xem ví dụ trước, var1 có giá trị 500 lưu vùng nhớ 1000, sau câu lệnh 182 Lập trình C var2 = &var1; var12 chứa giá trị 1000, sau lệnh gán temp = *var2; temp chứa 500 1000 Tốn tử * hiểều “tại địa chỉ” Cả hai toán tử * & có độ ưu tiên cao tất tốn tử toán học ngoại trừ toán tử lấy giá trị âm Chúng có độ ưu tiên với tốn tử lấy giá trị âm (-) Chương trình in giá trị biến kiểu số nguyên, địa lưu biến trỏ, chương trình in địa biến trỏ #include void main() { int var = 500, *ptr_var; /* var is declared as an integer and ptr_var as a pointer pointing to an integer */ ptr_var = &var; /*stores address of var in ptr_var*/ /* Prints value of variable (var) and address where var is stored */ printf(“The value %d is stored at address %u:”, var, &var); /* Prints value stored in ptr variable (ptr_var) and address where ptr_var is stored */ printf(“\nThe value %u is stored at address: %u”, ptr_var, &ptr_var); /* Prints value of variable (var) and address where var is stored, using pointer to variable */ printf(“\nThe value %d is stored at address:%u”, *ptr_var, ptr_var); } Kết ví dụ hiển thị sau:Một ví dụ kết thực thi chương trình sau: The value 500 is stored at address: 65500 The value 65500 is stored at address: 65502 The value 500 is stored at address: 65500 Trong ví dụ trên, ptr_var chứa địa 65500, địa vùng nhớ lưu trữ giá trị var Nội dung nhớ 65500 lấy cách sử dụng toán tử *, *ptr_var Lúc *ptr_var trình bàytương ứng với giá trị 500, giá trị var Bởi ptr_var biến, nên địa in tốn tử & Trong ví dụ trên, ptr_var lưu địa 65502 Mã quy cách %u định cách in giá trị tham số theo kiểu số nguyên không dấu (unsigned int) Con trỏ 183 Nhớ lại là, biến kiểu số nguyên chiếm bytes nhớ Vì vậy, giá trị var lưu trữ địa 65500 trình biên dịch cấp phát ô nhớ 65502 cho ptr_var Tương tự, số thập phân kiểu float yêu cầu bytes kiểu double yêu cầu bytes Các biến trỏ lưu trữ giá trị nguyên Với hầu hết chương trình sử dụng trỏ, kiểu trỏ xem giá trị 16-bit – chiếm bytes nhớ Chú ý hai câu lệnh sau cho kết printf(“The value is %d”, var); printf(“The value is %d”, *(&var)); Gán giá trị cho trỏ Các giá trị gán cho biến trỏ thơng qua tốn tử & Câu lệnh gán là: ptr_var = &var; Lúc địa var lưu biến ptr_var Cũng gán giá trị cho trỏ thơng qua biến trỏ khác trỏ đến phần tử liệu có kiểu ptr_var = &var; ptr_var2 = ptr_var; Giá trị NULL gán đến trỏ số sau: ptr_var = 0; Các biến gán giá trị thông qua trỏ chúng *ptr_var = 10; gán 10 cho biến var ptr_var trỏ đến var Nói chung, biểu thức có chứa trỏ theo qui luật biểu thức khác C Điều quan trọng cần ý phải gán giá trị cho biến trỏ trước sử dụng chúng; khơng chúng trỏ đến giá trị khơng xác định Phép tốn số học trỏ Chỉ phép cộng trừ toán tử thực trỏ Ví dụ sau minh họa điều này: int var, *ptr_var; ptr_var = &var; var = 500; Trong ví dụ trên, giả sử var lưu địa 1000 Sau đó, giá trị 1000 lưu vào ptr_var Vì kiểu số nguyên chiếm bytes, nên sau biểu thức: ptr_var++ ; ptr_var chứa 1002 mà KHÔNG phải 1001 Điều có nghĩa ptr_var trỏ đến số nguyên lưu địa 1002 Mỗi ptr_var tăng lên, trỏ đến số nguyên số nguyên bytes, ptr_var tăng trị Điều tương tự với phép toán giảm trị 184 Lập trình C Đây vài ví dụ ++ptr_var or ptr_var++ ptr_var or ptr_var-ptr_var + i ptr_var - i ++*ptr_var or (*ptr_var)++ *ptr_var++ Trỏ đến số nguyên đứng sau var Trỏ đến số nguyên đứng trước var Trỏ đến số nguyên thứ i sau var Trỏ đến số nguyên thứ i trước var Sẽ tăng trị var Sẽ tác động đến giá trị số nguyên sau var Mỗi trỏ tăng giá trị, trỏ đến ô nhớ phần tử Mỗi giảm giá trị, trỏ đến vị trí phần tử đứng trước Với trỏ trỏ tới ký tự, xuất bình thường, ký tự chiếm byte Tuy nhiên, tất trỏ khác tăng giảm trị tuỳ thuộc vào độ dài kiểu liệu mà chúng trỏ tới Như thấy ví dụ trên, ngồi tốn tử tăng trị giảm trị, số nguyên cộng vào trừ với trỏ Ngoài phép cộng trừ trỏ với số nguyên, khơng có phép tốn khác thực trỏ Nói rõ hơn, trỏ nhân chia Cũng kiểều float double cộng trừ với trỏ So sánh trỏ Hai trỏ so sánh biểu thức quan hệ Tuy nhiên, điều hai biến trỏ đến biến có kiểu liệu ptr_a ptr_b hai biến trỏ trỏ đến phần tử liệu a b Trong trường hợp này, phép so sánh sau thực hiện: ptr_a < ptr_b ptr_a > ptr_b ptr_a = ptr_b ptr_a == ptr_b ptr_a != ptr_b ptr_a == NULL Trả giá trị true a lưu trữ vị trí trước b Trả giá trị true a lưu trữ vị trí sau b Trả giá trị true a lưu trữ vị trí trước b ptr_a ptr_b trỏ đến vị trí Trả giá trị true a lưu trữ vị trí sau b ptr_a ptr_b trỏ đến vị trí Trả giá trị true hai trỏ ptr_a ptr_b trỏ đến phần tử liệu Trả giá trị true hai trỏ ptr_a ptr_b trỏ đến phần tử liệu khác có kiểu liệu Trả giá trị true ptr_a gán giá trị NULL (0) Tương tự, ptr_begin ptr_end trỏ đến phần tử mảng thì, ptr_end - ptr_begin trả số bytes cách biệt giữ hai vị trí mà chúng trỏ đến 13.4 Con trỏ mảng chiều Tên mảng thật trỏ trỏ đến phần tử mảng Vì vậy, ary mảng chiều, địa phần tử mảng biểu diễn &ary[0] đơn giản ary Tương tự, địa phần tử mảng thứ hai viết &ary[1] ary+1, Tổng quát, địa phần tử mảng thứ (i + 1) biểu diễn &ary[i] hay (ary+i) Như vậy, địa phần tử mảng biểu diễn theo hai cách:  Sử dụng ký hiệu & trước phần tử mảng  Sử dụng biểu thức số cộng vào tên mảng Con trỏ 185 Ghi nhớ biểu thức (ary + i), ary tượng trưng cho địa chỉ, i biểu diễn số nguyên Hơn nữa, ary tên mảng mà phần tử có kiểều số nguyên, ký tự, số thập phân,… (dĩ nhiên, tất phần tử mảng phải có kiểu liệu) Vì vậy, biểu thức khơng phép cộng; thật xác định địa chỉ, số xác định ô nhớ Biểu thức (ary + i) trình bày cho địa khơng phải biểu thức tốn học Như nói trước, số lượng ô nhớ kết hợp với mảng tùy thuộc vào kiểu liệu mảng kiến trúc máy tính Tuy nhiên, người lập trình xác định địa phần tử mảng đầu tiên, tên mảng (trong trường hơp ary) số phần tử tiếp sau phần tử đầu tiên, là, giá trị số Giá trị i xem độ dời dùng theo cách Các biểu thức &ary[i] (ary+i) biểu diễn địa phần tử thứ i ary, cách logic ary[i] *(ary + i) biểu diễn nội dung địa đó, nghĩa là, giá trị phần tử thứ i mảng ary Cả hai cách thay cho sử dụng ứng dụng người lập trình mong muốn Chương trình sau biểu diễn mối quan hệ phần tử mảng địa chúng #include void main() { static int ary[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int i; for (i = 0; i < 10; i ++) { printf(“\n i = %d , ary[i] = %d , *(ary+i)= %d “, i, ary[i], *(ary + i)); printf(“&ary[i] = %X , ary + i = %X”, &ary[i], ary + i); /* %X gives unsigned hexadecimal */ } } Chương trình định nghĩa mảng chiều ary, có 10 phần tử kiểu số nguyên, phần tử mảng gán giá trị tương ứng 1, 2, 10 Vòng lặp for dùng để hiển thị giá trị địa tương ứng phần tử mảng Chú ý rằng, giá trị phần tử xác định theo hai cách khác nhau, ary[i] *(ary + i), nhằm minh họa tương đương chúng Tương tự, địa phần tử mảng hiển thị theo hai cách Kết thực thi chương trình sau: i=0 i=1 i=2 i=3 i=4 i=5 i=6 i=7 i=8 i=9 ary[i]=1 ary[i]=2 ary[i]=3 ary[i]=4 ary[i]=5 ary[i]=6 ary[i]=7 ary[i]=8 ary[i]=9 ary[i]=10 *(ary+i)=1 *(ary+i)=2 *(ary+i)=3 *(ary+i)=4 *(ary+i)=5 *(ary+i)=6 *(ary+i)=7 *(ary+i)=8 *(ary+i)=9 *(ary+i)=10 &ary[i]=194 &ary[i]=196 &ary[i]=198 &ary[i]=19A &ary[i]=19C &ary[i]=19E &ary[i]=1A0 &ary[i]=1A2 &ary[i]=1A4 &ary[i]=1A6 ary+i ary+i ary+i ary+i ary+i ary+i ary+i ary+i ary+i ary+i = = = = = = = = = = 194 196 198 19A 19C 19E 1A0 1A2 1A4 1A6 Kết trình bày rõ ràng khác ary[i] - biểu diễn giá trị phần tử thứ i mảng, &ary[i] - biểu diễn địa 186 Lập trình C Khi gán giá trị cho phần tử mảng ary[i], vế trái lệnh gán viết ary[i] *(ary + i) Vì vậy, giá trị gán trực tiếp đến phần tử mảng gán đến vùng nhớ mà địa phần tử mảng Đôi cần thiết phải gán địa đến định danh Trong trường hợp vậy, trỏ phải xuất vế trái câu lệnh gán Không thể gán địa tùy ý cho tên mảng phần tử mảng Vì vậy, biểu thức ary, (ary + i) &ary[i] xuất vế trái câu lệnh gán Hơn nữa, địa mảng thay đổi cách tùy ý, biểu thức ary++ khơng phép Lý vì: ary địa mảng ary Khi mảng khai báo, liên kết định mảng bắt đầu đâu, ví dụ, bắt đầu địa 1002 Một địa đưa ra, mảng Việc cố gắng tăng địa lên điều vơ nghĩa, giống nói x = 5++; Bởi khơng thể tăng trị, trình biên dịch đưa thông báo lỗi Trong trường hợp mảng ary, ary xem trỏ Nhớ rằng, (ary + 1) không di chuyển mảng ary đến vị trí (ary + 1), trỏ đến vị trí đó, ary++ cố găng dời ary sang vị trí Địa phần tử gán cho phần tử mảng khác, giá trị phần tử mảng gán cho phần tử khác thông qua trỏ &ary[2] ary[2] = &ary[3]; = ary[3]; /* không cho phép*/ /* cho phép*/ Nhớ lại hàm scanf(), tên tham biến kiểu liệu phải đặt sau dấu (&), tên tham biến mảng ngoại lệ Điều dễ hiểu Vì scanf() địi hỏi địa nhớ biến liệu danh sách tham số, toán tử & trả địa nhớ biến, trước tên biến phải có dấu & Tuy nhiên dấu & không yêu cầu tên mảng, tên mảng tự biểu diễn địa nó.Tuy nhiên, phần tử mảng đọc, dấu & cần phải sử dụng scanf(“%d”, *ary) /* scanf(“%d”, &ary[2]) /* phần tử */ phần tử */ 13.4.1 Con trỏ mảng nhiều chiều Một mảng nhiều chiều biểu diễn dạng trỏ mảng chiều (tên mảng) độ dời (chỉ số) Thực điều mảng nhiều chiều tập hợp mảng chiều.Ví dụ, mảng hai chiều định nghĩa trỏ đến nhóm mảng chiều Cú pháp báo mảng hai chiều viết sau: data_type (*ptr_var)[expr 2]; thay data_type array[expr 1][expr 2]; Khái niệm tổng quát hóa cho mảng nhiều chiều, là, data_type (*ptr_var)[exp 2] [exp N]; thay data_type array[exp 1][exp 2] [exp N]; Con trỏ 187 Trong khai báo trên, data_type kiểu liệu mảng, ptr_var tên biến trỏ, array tên mảng, exp 1, exp 2, exp 3, exp N giá trị nguyên dương xác định số lượng tối đa phần tử mảng kết hợp với số Chú ý dấu ngoặc () bao quanh tên mảng dấu * phía trước tên mảng cách khai báo theo dạng trỏ Cặp dấu ngoặc () thiếu, ngược lại cú pháp khai báo khai báo mảng trỏ trỏ nhóm mảng Ví dụ, ary mảng hai chiều có 10 dịng 20 cột, khai báo sau: int (*ary)[20]; thay int ary[10][20]; Trong khai báo thứ nhất, ary định nghĩa trỏ trỏ tới nhóm mảng chiều liên tiếp nhau, mảng có 20 phần tử kiểu số nguyên Vì vậy, ary trỏ đến phần tử mảng, dịng (dịng 0) mảng hai chiều Tương tự, (ary + 1) trỏ đến dòng thứ hai mảng hai chiều, Một mảng thập phân ba chiều fl_ary khai báo như: float (*fl_ary)[20][30]; thay float fl_ary[10][20][30]; Trong khai báo đầu, fl_ary định nghĩa nhóm mảng thập phân hai chiều có kích thước 20 x 30 liên tiếp Vì vậy, fl_ary trỏ đến mảng 20 x 30 đầu tiên, (fl_ary + 1) trỏ đến mảng 20 x 30 thứ hai, Trong mảng hai chiều ary, phần tử dịng cột truy xuất sử dụng câu lệnh: ary[3][8]; *(*(ary + 3) + 8); Cách thứ cách thường dùng Trong cách thứ hai, (ary + 3) trỏ trỏ đến dịng thứ Vì vậy, đối tượng trỏ này, *(ary + 3), tham chiếu đến tồn dịng Vì dịng mảng chiều, *(ary + 3) trỏ trỏ đến phần tử dòng 3, sau cộng vào trỏ Vì vậy, *(*(ary + 3) + 8) trỏ trỏ đến phần tử (phần tử thứ 9) dòng thứ Vì đối tượng trỏ này, *(*(ary + 3) + 8), tham chiếu đến tham chiếu đến phần tử cột thứ dòng thứ 4, ary [3][8] Có nhiều cách thức để định nghĩa mảng, có nhiều cách để xử lý phần tử mảng Lựa chọn cách thức tùy thuộc vào người dùng Tuy nhiên, ứng dụng có mảng dạng số, định nghĩa mảng theo cách thông thường dễ dàng Con trỏ chuỗi Chuỗi đơn giản mảng chiều có kiểu ký tự Mảng trỏ có mối liên hệ mật thiết, vậy, cách tự nhiên chuỗi có mối liên hệ mật thiết với trỏ Xem trường hợp hàm 188 Lập trình C strchr() Hàm nhận tham số chuỗi ký tự để tìm kiếm ký tự mảng, nghĩa là, ptr_str = strchr(strl, ‘a’); biến trỏ ptr_str gán địa ký tự ‘a’ xuất chuỗi str Đây khơng phải vị trí chuỗi, từ đến cuối chuỗi, mà địa chỉ, từ địa bắt đầu chuỗi đến địa kết thúc chuỗi Chương trình sau sử dụng hàm strchr(), chương trình cho phép người dùng nhập vào chuỗi ký tự để tìm kiếm Chương trình in địa bắt đầu chuỗi, địa ký tự, vị trí tương đối ký tự chuỗi (0 vị trí ký tự đầu tiên, vị trí ký tự thứ hai, ) Vị trí tương đối hiệu số hai địa chỉ, địa bắt đầu chuỗi địa nơi mà ký tự cần tìm xuất #include #include void main () { char a, str[81], *ptr; printf(“\nEnter a sentence:”); gets(str); printf(“\nEnter character to search for:”); a = getche(); ptr = strchr(str, a); /* return pointer to char*/ printf(“\nString starts at address: %u”, str); printf(“\nFirst occurrence of the character is at address: %u”, ptr); printf(“\nPosition of first occurrence (starting from 0)is: %d”, ptr-str); } Kết ví dụ hiển thị sau:Một ví dụ kết thực chương trình sau: Enter a sentence: We all live in a yellow submarine Enter character to search for: Y String starts at address: 65420 First occurrence of the character is at address: 65437 Position of first occurrence (starting from 0) is: 17 Trong câu lệnh khai báo, biến trỏ ptr thiết đặt để chứa địa trả từ hàm strchr(), địa ký tự (ptr có kiểu char) Hàm strchr() không cần thiết phải khai báo thư viện string.h khai báo 13.5 Cấp phát nhớ Cho đến thời điểm biết tên mảng thật trỏ trỏ tới phần tử mảng Hơn nữa, cách định nghĩa mảng thơng thường định nghĩa mảng biến trỏ Tuy nhiên, mảng khai báo cách bình thường, kết khối nhớ cố định dành sẵn thời điểm bắt đầu thực thi chương trình, điều khơng xảy mảng khai báo biến trỏ Sử dụng biến trỏ để biểu Con trỏ 189 diễn mảng đòi hỏi việc gán vài ô nhớ khởi tạo trước phần tử mảng xử lý Sự cấp phát nhớ thông thường thực cách sử dụng hàm thư viện malloc() Xem ví dụ sau Một mảng số nguyên chiều ary có 20 phần tử khai báo sau: int *ary; thay int ary[20]; Tuy nhiên, ary không tự động gán khối nhớ khai báo biến trỏ, khối ô nhớ đủ để chứa 10 số nguyên dành sẵn ary khai báo mảng Nếu ary khai báo trỏ, số lượng nhớ gán sau: ary = malloc(20 *sizeof(int)); Sẽ dành khối nhớ có kích thước (tính theo bytes) tương đương với kích thước số nguyên Ở đây, khối nhớ cho 20 số nguyên cấp phát 20 số gán với 20 bytes (một byte cho số nguyên) nhân với sizeof(int), sizeof(int) trả kết 2, máy tính dùng bytes để lưu trữ số nguyên Nếu máy tính sử dụng byte để lưu số nguyên, hàm sizeof() khơng địi hỏi Tuy nhiên, sử dụng tạo khả uyển chuyển cho mã lệnh Hàm malloc() trả trỏ chứa địa vị trí bắt đầu vùng nhớ cấp phát Nếu khơng gian nhớ u cầu khơng có, malloc() trả giá trị NULL Sự cấp phát nhớ theo cách này, nghĩa là, yêu cầu chương trình gọi Cấp phát nhớ động Trước tiếp tục xa hơn, thảo luận khái niêm Cấp phát nhớ động Một chương trình C lưu trữ thơng tin nhớ máy tính theo hai cách Phương pháp thứ bao gồm biến toàn cục cục – bao gồm mảng Trong trường hợp biến toàn cục biến tĩnh, lưu trữ cố định suốt thời gian thực thi chương trình Các biến địi hỏi người lập trình phải biết trước tổng số dung lượng nhớ cần thiết cho trường hợp Phương pháp thứ hai, thông tin lưu trữ thơng qua Hệ thống cấp phát động C Trong phương pháp này, lưu trữ thơng tin cấp phát từ vùng nhớ cịn tự cần thiết Hàm malloc() hàm thường dùng nhất, cho phép thực việc cấp phát nhớ từ vùng nhớ tự Tham số cho malloc() số nguyên xác định số bytes cần thiết Một ví dụ khác, xét mảng ký tự hai chiều ch_ary có 10 dòng 20 cột Sự khai báo cấp phát nhớ trường hợp phải sau: char (*ch_ary)[20]; ch_ary = (char*)malloc(10*20*sizeof(char)); Như nói trên, malloc() trả trỏ trỏ đến kiểu rỗng (void) Tuy nhiên, ch_ary trỏ kiểu char, chuyển đổi kiểu cần thiết Trong câu lệnh trên, (char*) đổi kiểu trả malloc() thành trỏ trỏ đến kiểu char Tuy nhiên, khai báo mảng phải chứa phép gán giá trị khởi tạo mảng phải khai báo theo cách bình thường, khơng thể dùng biến trỏ: int ary[10] = {1,2,3,4,5,6,7,8,9,10}; int ary[] = {1,2,3,4,5,6,7,8,9,10}; 190 Lập trình C Ví dụ sau tạo mảng chiều xếp mảng theo thứ tự tăng dần Chương trình sử dụng trỏ hàm malloc() để gán nhớ #include #include void main() { int *p, n, i, j, temp; printf("\n Enter number of elements in the array: "); scanf("%d", &n); p = (int*) malloc(n * sizeof(int)); for(i = 0; i < n; ++i) { printf("\nEnter element no %d:", i + 1); scanf("%d", p + i); } for(i = 0; i < n - 1; ++i) for(j = i + 1; j < n; ++j) if(*(p + i) > *(p + j)) { temp = *(p + i); *(p + i) = *(p + j); *(p + j) = temp; } for(i = 0; i < n; ++i) printf("%d\n", *(p + i)); } Chú ý lệnh malloc(): p = (int*)malloc(n*sizeof(int)); Ở đây, p khai báo trỏ trỏ đến mảng gán nhớ sử dụng malloc() Dữ liệu đọc vào sử dụng lệnh scanf() scanf("%d",p+i); Trong scanf(), biến trỏ sử dụng để lưu liệu vào mảng Các phần tử mảng lưu trữ hiển thị printf() printf("%d\n", *(p + i)); Chú ý dấu * trường hợp này, giá trị lưu vị trí phải hiển thị Khơng có dấu *, printf() hiển thị địa  free() Con trỏ 191 Hàm sử dụng để giải phóng nhớ khơng cần thiết Dạng tổng quát hàm free(): void free( void *ptr ); Hàm free() giải phóng khơng gian trỏ ptr, khơng gian giải phóng sử dụng tương lai ptr sử dụng trước cách gọi đến malloc(), calloc(), realloc(), calloc() realloc() (sẽ thảo luận sau) Ví dụ bên hỏi bạn có số nguyên bạn lưu vào mảng Sau cấp phát nhớ động cách sử dụng malloc lưu số lượng số nguyên, in chúng ra, sau xóa nhớ cấp phát cách sử dụng free #include #include /* required for the malloc and free functions */ int main() { int number; int *ptr; int i; printf("How many ints would you like store? "); scanf("%d", &number); ptr = (int *) malloc (number * sizeof(int)); /*allocate memory*/ if(ptr != NULL) { for(i = ; i < number ; i++) { *(ptr+i) = i; } for(i=number ; i>0 ; i ) { printf("%d\n", *(ptr+(i-1))); /*print out in reverse order*/ } free(ptr); /* free allocated memory */ return 0; } else { } printf("\nMemory allocation failed - not enough memory.\n"); return 1; } Kết sau giá trị nhập vào 3: How many ints would you like store?  calloc() 192 Lập trình C calloc tương tự malloc, khác biệt giá trị lưu không gian nhớ cấp phát Với malloc, cấp phát nhớ có giá trị calloc đòi hỏi hai đối số Đối số thứ số biến mà bạn muốn cấp phát nhớ cho Đối số thứ hai kích thước biến void *calloc( size_t num, size_t size ); Giống malloc, calloc trả trỏ rỗng (void) cấp phát nhớ thành cơng, ngược lại trả trỏ NULL Ví dụ bên cho bạn gọi hàm calloc tham chiếu đến ô nhớ cấp phát sử dụng số mảng Giá trị khởi tạo vùng nhớ cấp phát in vòng lặp for #include #include int main() { float *calloc1, *calloc2; int i; calloc1 = (float *) calloc(3, sizeof(float)); calloc2 = (float *) calloc(3, sizeof(float)); if(calloc1 != NULL && calloc2 != NULL) { for(i = 0; i < 3; i++) { printf("\ncalloc1[%d] holds %05.5f ", i, calloc1[i]); printf("\ncalloc2[%d] holds %05.5f", i, *(calloc2 + i)); } free(calloc1); free(calloc2); return 0; } } else { printf("Not enough memory\n"); return 1; } Kết quả: calloc1[0] calloc2[0] calloc1[1] calloc2[1] calloc1[2] calloc2[2] holds holds holds holds holds holds 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 Trong tất máy, mảng calloc1 calloc2 phải chứa giá trị calloc đặc biệt hữu dụng bạn sử dụng mảng đa chiều Đây ví dụ khác minh họa cách dùng hàm calloc() Con trỏ 193 /* This program gets the number of elements, allocates spaces for the elements, gets a value for each element, sum the values of the elements, and print the number of the elements and the sum */ #include #include main() { int *a, i, n, sum = 0; printf(“\n%s%s”, “An array will be created dynamically \n\n”, “Input an array size n followed by integers: ”); scanf( “%d”, &n); /* get the number of elements */ a = (int *) calloc (n, sizeof(int)); /* allocate space */ /* get a value for each element */ for( i = 0; i < n; i++ ) { printf(“Enter %d values: “, n); scanf(“%d”, a + i); } /* sum the values */ for(i = 0; i < n; i++ ) sum += a[i]; free(a); /* free the space */ /* print the number and the sum */ } printf(“\n%s%7d\n%s%7d\n\n”, “Number of elements: ”, n, “Sum of the elements: ”, sum);  realloc() Giả sử cấp phát số bytes cho mảng sau nhận bạn muốn thêm giá trị Bạn chép thứ vào mảng lớn hơn, cách không hiệu Hoặc bạn cấp phát thêm bytes sử dụng cách gọi hàm realloc, mà liệu bạn không bị realloc() nhận hai đối số Đối số thứ trỏ tham chiếu đến nhớ Đối số thứ hai tổng số bytes bạn muốn cấp phát thêm void *realloc( void *ptr, size_t size ); Truyền đối số thứ hai tương đương với việc gọi hàm free Một lần, realloc trả trỏ rỗng (void) thành công, ngược lại trỏ NULL trả Ví dụ sử dụng calloc để cấp phát đủ nhớ cho mảng int có năm phần tử Sau realloc gọi để mở rộng mảng để chứa bảy phần tử 194 Lập trình C #include #include int main() { int *ptr; int i; ptr = (int *)calloc(5, sizeof(int *)); if(ptr!=NULL) { *ptr = 1; *(ptr + 1) = 2; ptr[2] = 4; ptr[3] = 8; ptr[4] = 16; /* ptr[5] = 32; wouldn't assign anything */ ptr = (int *)realloc(ptr, * sizeof(int)); if(ptr!=NULL) { printf("Now allocating more memory \n"); ptr[5] = 32; /* now it's legal! */ ptr[6] = 64; for(i = 0;i < 7; i++) { printf("ptr[%d] holds %d\n", i, ptr[i]); } realloc(ptr, 0); /* same as free(ptr); - just fancier! */ return 0; } else { printf("Not enough memory - realloc failed.\n"); return 1; } } else { printf("Not enough memory - calloc failed.\n"); return 1; } } Kết quả: Now allocating more memory ptr[0] holds ptr[1] holds ptr[2] holds ptr[3] holds ptr[4] holds 16 Con trỏ 195 ptr[5] holds 32 ptr[6] holds 64 Chú ý hai cách khác sử dụng khởi tạo mảng: ptr[2] = tương đương với *(ptr + 2) = (chỉ dễ đọc hơn!) Trước sử dụng realloc, việc gán giá trị đến phần tử ptr[5] không gây lỗi cho trình biên dịch Chương trình thực thi, ptr[5] không chứa giá trị mà bạn gán 196 Lập trình C Tóm tắt học  Một trỏ cung cấp phương thức truy xuất biến mà không cần tham chiếu trực tiếp đến biến  Một trỏ biến, chứa địa vùng nhớ biến khác  Sự khai báo trỏ bao gồm kiểu liệu sở, dấu *, tên biến  Có hai tốn tử đặc biệt dùng với trỏ: * &  Toán tử & trả địa nhớ toán hạng  Toán tử thứ hai, *, phần bổ xung toán tử & Nó trả giá trị chứa vị trí nhớ trỏ trỏ  Chỉ có phép cộng phép trừ thực thi với trỏ  Hai trỏ so sánh biểu thức quan hệ hai biến trỏ đến biến có kiểu liệu  Các trỏ truyền tới hàm đối số  Một tên mảng thật trỏ trỏ đến phần tử mảng  Một trỏ địa chỉ; biến trỏ nơi để lưu địa  Bộ nhớ cấp phát cần dùng cách dùng hàm malloc(),calloc(),realloc() Sự cấp phát nhớ theo cách gọi cấp phát nhớ động Con trỏ 197 Kiểm tra tiến độ học tập Một _ cung cấp phương thức truy xuất biến mà không tham chiếu trực tiếp đến biến A Mảng C Cấu trúc B Con trỏ D Tất sai Các trỏ trỏ đến mảng (Đúng/Sai) trỏ xác định kiểu biến mà trỏ trỏ đến A Kiểu C Nội dung B Kích thước D Tất sai Có hai tốn tử đặc biệt dùng với trỏ _ A ^ % C * & B ; ? D Tất sai Chỉ có phép toán thực trỏ A Cộng, Trừ C Chia, Cộng B.Nhân, Chia D Tất sai Hai trỏ so sánh hai biến trỏ đến kiểu liệu khác (Đúng/Sai) Sự cấp phát nhớ theo cách này, nghĩa là, chương trình có u cầu gọi A Cấp phát nhớ động C Cấp phát nhớ nội dung 198 B Cấp phát nhớ tĩnh D Tất sai Lập trình C Bài tập tự làm Viết chương trình để nhận vào chuỗi in chuỗi đọc xi – ngược giống Viết chương trình sử dụng trỏ trỏ đến chuỗi để nhận tên thú chim trả tên theo dạng số nhiều Nguồn Maths.vn Con trỏ 199 ... tử liệu Trả giá trị true hai trỏ ptr_a ptr_b trỏ đến phần tử liệu khác có kiểu liệu Trả giá trị true ptr_a gán giá trị NULL (0) Tương tự, ptr_begin ptr_end trỏ đến phần tử mảng thì, ptr_end -. .. trình để trực tiếp truy xuất giá trị var1 Nhớ rằng, var2 khơng phải có kiểu liệu int trỏ trỏ đến biến có kiểu liệu int Kiểu liệu sở trỏ xác định kiểu biến mà trỏ trỏ đến Về mặt kỹ thuật, trỏ có kiểu... allocation) 13. 2 Các biến trỏ Nếu biến sử dụng trỏ, phải khai báo trước Câu lệnh khai báo trỏ bao gồm kiểu liệu bản, dấu *, tên biến Cú pháp tổng quát để khai báo biến trỏ sau: type *name; Ở type kiểu liệu

Ngày đăng: 16/01/2014, 20:20

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan