Giáo án Tin học Lớp 11 - Bài 13: Con trỏ
Mục tiêu:
Kết thúc bài học này, bạn có thể:
Hiểu con trỏ là gì, và con trỏ được sử dụng ở đâu
Biết cách sử dụng biến con trỏ và các toán tử con trỏ
Gán giá trị cho con trỏ
Hiểu các phép toán số học con trỏ
Hiểu các phép toán so sánh con trỏ
Biết cách truyền tham số con trỏ cho hàm
Hiểu cách sử dụng con trỏ kết hợp với mảng một chiều
Hiểu cách sử dụng con trỏ kết hợp với mảng đa chiều
Hiểu cách cấp phát bộ nhớ được thực hiện như thế nào
Giới thiệu
Con trỏ cung cấp một 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 chỉ. Bài này sẽ đề cập đến các khái niệm về con trỏ và cách sử dụng chúng trong C.
àm malloc() là một trong các hàm thường được dùng nhất, nó cho phép thực hiện việc cấp phát bộ nhớ từ vùng nhớ còn tự do. Tham số cho malloc() là một 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 và 20 cột. Sự khai báo và cấp phát bộ nhớ trong trường hợp này phải như sau: char (*ch_ary)[20]; ch_ary = (char*)malloc(10*20*sizeof(char)); Như đã nói ở trên, malloc() trả về một con trỏ trỏ đến kiểu rỗng (void). Tuy nhiên, vì ch_ary là một con trỏ kiểu char, sự chuyển đổi kiểu là cần thiết. Trong câu lệnh trên, (char*) đổi kiểu trả về của malloc() thành một con trỏ trỏ đến kiểu char. Tuy nhiên, nếu sự khai báo của mảng phải chứa phép gán các giá trị khởi tạo thì mảng phải được khai báo theo cách bình thường, không thể dùng một biến con trỏ: int ary[10] = {1,2,3,4,5,6,7,8,9,10}; hoặc int ary[] = {1,2,3,4,5,6,7,8,9,10}; Ví dụ sau đây tạo một mảng một chiều và sắp xếp mảng theo thứ tự tăng dần. Chương trình sử dụng con trỏ và hàm malloc() để gán bộ 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 được khai báo như một con trỏ trỏ đến một mảng và được gán bộ nhớ sử dụng malloc(). Dữ liệu được đọc vào sử dụng lệnh scanf(). scanf("%d",p+i); Trong scanf(), biến con trỏ được sử dụng để lưu dữ liệu vào trong mảng. Các phần tử mảng đã lưu trữ được hiển thị bằng printf(). printf("%d\n", *(p + i)); Chú ý dấu * trong trường hợp này, vì giá trị lưu trong vị trí đó phải được hiển thị. Không có dấu *, printf() sẽ hiển thị địa chỉ. free() Hàm này có thể được sử dụng để giải phóng bộ nhớ khi nó không còn cần thiết. Dạng tổng quát của hàm free(): void free( void *ptr ); Hàm free() giải phóng không gian được trỏ bởi ptr, không gian được giải phóng này có thể sử dụng trong tương lai. ptr đã sử dụng trước đó bằng cách gọi đến malloc(), calloc(), hoặc realloc(), calloc() và realloc() (sẽ được thảo luận sau). Ví dụ bên dưới sẽ hỏi bạn có bao nhiêu số nguyên sẽ được bạn lưu vào trong một mảng. Sau đó sẽ cấp phát bộ nhớ động bằng cách sử dụng malloc và lưu số lượng số nguyên, in chúng ra, và sau đó xóa bộ nhớ cấp phát bằng 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 = 0 ; 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 quả như sau nếu giá trị được nhập vào 3: How many ints would you like store? 3 2 1 0 calloc() calloc tương tự như malloc, nhưng khác biệt chính là mặc nhiên các giá trị được lưu trong không gian bộ nhớ đã cấp phát là 0. Với malloc, cấp phát bộ nhớ có thể có giá trị bất kỳ. calloc đòi hỏi hai đối số. Đối số thứ nhất là số các biến mà bạn muốn cấp phát bộ nhớ cho. Đối số thứ hai là kích thước của mỗi biến. void *calloc( size_t num, size_t size ); Giống như malloc, calloc sẽ trả về một con trỏ rỗng (void) nếu sự cấp phát bộ nhớ là thành công, ngược lại nó sẽ trả về một con trỏ NULL. Ví dụ bên dưới chỉ ra cho bạn gọi hàm calloc như thế nào và tham chiếu đến ô nhớ đã cấp phát sử dụng một chỉ số mảng. Giá trị khởi tạo của vùng nhớ đã cấp phát được in ra trong 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] holds 0.00000 calloc2[0] holds 0.00000 calloc1[1] holds 0.00000 calloc2[1] holds 0.00000 calloc1[2] holds 0.00000 calloc2[2] holds 0.00000 Trong tất cả các máy, các mảng calloc1 và calloc2 phải chứa các giá trị 0. calloc đặc biệt hữu dụng khi bạn đang sử dụng mảng đa chiều. Đây là một ví dụ khác minh họa cách dùng của hàm calloc(). /* 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ử chúng ta đã cấp phát một số bytes cho một mảng nhưng sau đó nhận ra là bạn muốn thêm các giá trị. Bạn có thể sao chép mọi thứ vào một mảng lớn hơn, cách này không hiệu quả. Hoặc bạn có thể cấp phát thêm các bytes sử dụng bằng cách gọi hàm realloc, mà dữ liệu của bạn không bị mất đi. realloc() nhận hai đối số. Đối số thứ nhất là một con trỏ tham chiếu đến bộ nhớ. Đối số thứ hai là tổng số bytes bạn muốn cấp phát thêm. void *realloc( void *ptr, size_t size ); Truyền 0 như là đối số thứ hai thì tương đương với việc gọi hàm free. Một lần, realloc trả về một con trỏ rỗng (void) nếu thành công, ngược lại một con trỏ NULL được trả về. Ví dụ này sử dụng calloc để cấp phát đủ bộ nhớ cho một mảng int có năm phần tử. Sau đó realloc được gọi để mở rộng mảng để có thể chứa bảy phần tử. #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, 7 * 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 1 ptr[1] holds 2 ptr[2] holds 4 ptr[3] holds 8 ptr[4] holds 16 ptr[5] holds 32 ptr[6] holds 64 Chú ý hai cách khác nhau được sử dụng khi khởi tạo mảng: ptr[2] = 4 là tương đương với *(ptr + 2) = 4 (chỉ dễ đọc hơn!). Trước khi sử dụng realloc, việc gán một giá trị đến phần tử ptr[5] không gây ra lỗi cho trình biên dịch. Chương trình vẫn thực thi, nhưng ptr[5] không chứa giá trị mà bạn đã gán. Tóm tắt bài học Một con trỏ cung cấp một phương thức truy xuất một biến mà không cần tham chiếu trực tiếp đến biến. Một con trỏ là một biến, chứa địa chỉ vùng nhớ của một biến khác. Sự khai báo con trỏ bao gồm một kiểu dữ liệu cơ sở, một dấu *, và một tên biến. Có hai toán tử đặc biệt được dùng với con trỏ: * và &. Toán tử & trả về địa chỉ bộ nhớ của toán hạng. Toán tử thứ hai, *, là phần bổ xung của toán tử &. Nó trả về giá trị được chứa trong vị trí bộ nhớ được trỏ bởi con trỏ. Chỉ có phép cộng và phép trừ là có thể được thực thi với con trỏ. Hai con trỏ có thể được so sánh trong một biểu thức quan hệ chỉ khi cả hai biến này cùng trỏ đến các biến có cùng kiểu dữ liệu. Các con trỏ được truyền tới hàm như các đối số. Một tên mảng thật ra là một con trỏ trỏ đến phần tử đầu tiên của mảng. Một hằng con trỏ là một địa chỉ; một biến con trỏ là một nơi để lưu địa chỉ. Bộ nhớ có thể được cấp phát khi cần dùng bằng cách dùng các hàm malloc(),calloc(),realloc(). Sự cấp phát bộ nhớ theo cách này được gọi là sự cấp phát bộ nhớ động. Kiểm tra tiến độ học tập Một _________ cung cấp một phương thức truy xuất một biến mà không tham chiếu trực tiếp đến biến. A. Mảng B. Con trỏ C. Cấu trúc D. Tất cả đều sai Các con trỏ không thể trỏ đến các mảng. (Đúng/Sai) __________ của con trỏ xác định kiểu của các biến mà con trỏ có thể trỏ đến. A. Kiểu B. Kích thước C. Nội dung D. Tất cả đều sai Có hai toán tử đặc biệt được dùng với con trỏ là ____ và _____. A. ^ và % B. ; và ? C. * và & D. Tất cả đều sai Chỉ có ________ và __________ là những phép toán có thể được thực hiện trên các con trỏ. A. Cộng, Trừ B.Nhân, Chia C. Chia, Cộng D. Tất cả đều sai Hai con trỏ có thể được so sánh chỉ khi cả hai biến này đang trỏ đến các kiểu dữ liệu khác nhau. (Đúng/Sai) Sự cấp phát bộ nhớ theo cách này, nghĩa là, khi trong chương trình có yêu cầu được gọi là __________ . A. Cấp phát bộ nhớ động B. Cấp phát bộ nhớ tĩnh C. Cấp phát bộ nhớ nội dung D. Tất cả đều sai Bài tập tự làm Viết một chương trình để nhận vào một chuỗi và in ra nó nếu đó là chuỗi đọc xuôi – ngược đều giống nhau. Viết một chương trình sử dụng con trỏ trỏ đến các chuỗi để nhận tên của một con thú và một con chim và trả về các tên theo dạng số nhiều.
File đính kèm:
- Session 13 - Concept.doc