Tổng hợp lập trình ngôn ngữ C chi tiết (Tiếng việt)

192 9 0
Tổng hợp lập trình ngôn ngữ C chi tiết (Tiếng việt)

Đ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

Khác với các ngôn ngữ lập trình cấp cao (highlevel), C cung cấp các tác vụ gần với các chỉ thị của máy, các khái niệm truy xuất cấp độ máy, … Vì vậy các chương trình C thường có tốc độ thực thi nhanh, ít dòng lệnh, ít hao tốn vùng nhớ. C được xem là ngôn ngữ lập trình cấp thấp (lowlevel language). C có ít đặc tả hơn các ngôn ngữ khác, thay vào đó C cung cấp một thư viện lớn các hàm chuẩn: xuất nhập, xử lý chuỗi, cấp phát vùng nhớ, … và các tác vụ tiện ích khác . Trình biên dịch C cũng nhỏ và dễ viết. Sau khi chuẩn hóa, C cung cấp tính khả chuyển (portability), cho phép chương trình biên dịch được trên nhiều hệ nền.

Mở đầu Lịch sử C  Phát triển Ken Thompson Dennis Ritchie AT&T Bell Labs vào năm 1972    Năm 1983, ANSI bắt đầu q trình chuẩn hóa  Năm 1999, chuẩn bổ sung, kết C99 Năm 1989, ISO tiếp tục q trình chuẩn hóa Năm 1990, q trình chuẩn hóa kết thúc, kết C90 (Standard C) Lịch sử C Ken Thompson Dennis Ritchie & Brian Kernighan C sản phẩm hệ điều hành UNIX Năm 1970, UNIX viết hợp ngữ, khó dị lỗi phát triển Ken Thompson thiết kế ngôn ngữ cấp cao nhằm phát triển UNIX tương lai, ngôn ngữ B, dựa BCPL – ngôn ngữ dùng viết hệ điều hành trình biên dịch (Martin Richards, 1967) Năm 1972, Dennis Ritchie tham gia phát triển UNIX ngôn ngữ B DEC PDP-11 Do B khơng cịn phù hợp, Ritchie phát triển phiên mở rộng B, gọi NB (New B) Sau đó, Ritchie đặt tên cho ngơn ngữ C Năm 1973, UNIX viết lại hoàn toàn C Năm 1978, Brian Kernighan Dennis Ritchie xuất sách “The C Programming Language”, sách xem chuẩn thực tế ngôn ngữ C, gọi K&R C Chuẩn hóa Cộng đồng lập trình viên dùng C nhanh chóng phát triển, chuẩn thực tế K&R C cịn nhiều điểm mơ hồ, cần chuẩn hóa C ANSI ANSI (American National Standards Institute) bắt đầu q trình chuẩn hóa C từ năm 1983, kéo dài đến năm 1989 Kết chuẩn ANSI X3.159-1989 ISO Năm 1989, chuẩn ISO (International Standards Organization) chấp thuận chuẩn quốc tế ISO/IEC 9899:1990, mô tả C90, gọi ANSI C C99 C trải qua vài thay đổi mở rộng vào năm 1999, mô tả Amendment1 Phát hành chuẩn ISO/EIC 9899:1999; mô tả C99 Tuy nhiên C99 chưa áp dụng rộng rãi © Dương Thiên Tứ www.trainingwithexperts.com Đặc điểm C     C ngôn ngữ cấp thấp C ngơn ngữ lập trình hệ thống C gọn nhẹ khả chuyển Chương trình C khó hiểu khó tùy biến Đặc điểm C Ngơn ngữ cấp thấp Khác với ngơn ngữ lập trình cấp cao (high-level), C cung cấp tác vụ gần với thị máy, khái niệm truy xuất cấp độ máy, … Vì chương trình C thường có tốc độ thực thi nhanh, dịng lệnh, hao tốn vùng nhớ C xem ngơn ngữ lập trình cấp thấp (low-level language) Ngơn ngữ lập trình hệ thống C ngơn ngữ lựa chọn hàng đầu để phát triển hệ điều hành: UNIX, Linux, Windows, … C tích hợp cơng cụ UNIX Linux Gọn nhẹ khả chuyển C có đặc tả ngơn ngữ khác, thay vào C cung cấp thư viện lớn hàm chuẩn: xuất nhập, xử lý chuỗi, cấp phát vùng nhớ, … tác vụ tiện ích khác Trình biên dịch C nhỏ dễ viết Sau chuẩn hóa, C cung cấp tính khả chuyển (portability), cho phép chương trình biên dịch nhiều hệ “Write Only” C mang tiếng ngôn ngữ “Write Only”, nghĩa dễ dàng viết code khó đọc code Tính linh động gọn nhẹ C có tác động khơng mong muốn, lập trình viên C viết chương trình hiệu quả, khó đọc nên khó tùy biến © Dương Thiên Tứ www.trainingwithexperts.com Một chương trình C Báo cho trình biên dịch biết có tham gia thư viện hàm nhập xuất chuẩn (standard input and output functions) #include /* thích */ Hàm main int main(void) { printf("Hello\n"); printf("Welcome to C!"); “Khởi đầu” “Kết thúc” return 0; } Hello Welcome to C! Cờ báo cho Hệ điều hành biết chương trình thành cơng Một chương trình C #include Chỉ thị biên dịch (directives) #include hướng dẫn cho tiền xử lý C (C Preprocessor) tìm khai báo hàm tập tin “stdio.h” “stdio” viết tắt “standard input and output” (nhập xuất chuẩn) “.h” nghĩa tập tin tiêu đề (header) Chú thích Các thích đặt cặp dấu /* */ theo thứ tự trải dài nhiều dòng C99 cho phép dùng // C++, Java, … main Hàm main quan trọng Nó định nghĩa điểm chương trình bắt đầu thực Nếu bạn khơng viết hàm main chương trình khơng biên dịch Cặp ngoặc nhọn {} C dùng dấu ngoặc nhọn “{” với nghĩa “khởi đầu” dấu “}” với nghĩa “kết thúc” Cặp ngoặc giúp phân chương trình thành khối, nên dễ nhập lệnh dễ đọc Cặp {} chương trình bao khối hàm main printf Gọi hàm (function call) printf cách chuẩn để kết xuất liệu từ chương trình Hàm định nghĩa thư viện chuẩn stdio.h Hai ký tự liên tiếp “\” (ký tự escape) “n” (newline) cách C xử lý tạo dịng Thay in “\n”, trỏ chuyển đến đầu dòng return return trả trị cho Hệ điều hành Hệ điều hành xử lý thơng tin từ chương trình trả nào? MS-DOS lưu trữ biến ERRORLEVEL, dùng tập tin lô (batch) Các shell Bourne Korn UNIX lưu trữ biến tạm $?, dùng shell script nghĩa thành công Một trị 1, 2, 3, định mã lỗi khác Tất Hệ điều hành hỗ trợ trị lên đến 255 © Dương Thiên Tứ www.trainingwithexperts.com Dạng thức C    Các phát biểu kết thúc với dấu ;    Các chuỗi đặt cặp ngoặc kép “” Các cân chỉnh dịng trình duyệt bỏ qua C phân biệt chữ hoa-chữ thường Tên từ khóa hàm thư viện chuẩn chữ thường Tạo dịng xử lý với \n Các chương trình báo thành cơng hay lỗi, đừng qn điều Dạng thức C Dấu ; Dấu ; quan trọng C Nó báo cho trình biên dịch biết phát biểu (statements) kết thúc phát biểu khác bắt đầu Nếu quên đặt sau phát biểu, bạn nhận lỗi biên dịch Dạng thức tự C ngôn ngữ dạng thức tự (free format) Nghĩa bạn trình bày, cân chỉnh (indentation) hoàn toàn theo ý muốn, nên theo nguyên tắc để chương trình dễ đọc Có thể viết phát biểu nhiều dịng Có thể phân phần khối dòng trắng cho dễ đọc Các dấu cách (space) dấu tab chương trình trình biên dịch bỏ qua Phân biệt chữ hoa chữ thường C ngôn ngữ phân biệt chữ hoa với chữ thường (case sensitive) Chỉ có từ khóa int biên dịch; từ “Int”, “INT” biến thể khác không từ khóa Tất từ khóa C chữ thường Tất khoảng hàng trăm hàm thư viện chuẩn C chữ thường Từ khóa C: auto break case char const continue default double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while C99: inline restrict _Bool _Complex _Imaginary Hành vi ngẫu nhiên Chúng ta nói hàm main trả số nguyên cho Hệ điều hành, bạn quên điều (chỉ có return thiếu return) bạn nhận cảnh báo lúc biên dịch Nếu bỏ qua, chương trình có hành vi ngẫu nhiên tùy theo số nguyên ngẫu nhiên trả cho Hệ điều hành © Dương Thiên Tứ www.trainingwithexperts.com Một ví dụ khác #include Tạo hai biến nguyên a b int main(void) { int a, b; Đọc hai số nguyên vào “a” “b” printf("Nhap hai so: "); scanf("%i %i", &a, &b); printf("%i - %i = %i\n", a, b, a - b); Viết “a”, “b” “a-b” theo dạng thức định return 0; } Nhap hai so: 21 17 21 - 17 = Một ví dụ khác int Từ khóa int, dùng để khai báo (declarations) biến kiểu nguyên (interger), Ở có hai biến khai báo: “a” “b” scanf Hàm scanf ngược với printf Trong printf kết xuất hình, scanf lại đọc vào từ bàn phím Chuỗi định dạng “%i” báo cho scanf đọc số ngun từ bàn phím Do “%i %i” nghĩa hai số nguyên đọc, trị đặt vào biến “a” trị thứ hai đặt vào biến “b” Dấu cách hai “%i” “%i %i” quan trọng: báo cho scanf biết phải đợi dấu cách trước nhận trị thứ hai Nếu thay “%i,%i”, scanf đợi dấu , trước nhận trị thứ hai printf Ví dụ cho thấy printf scanf hàm xuất nhập có định dạng Với chuỗi định dạng “%i”, hàm scanf nhập số nguyên từ bàn phím hàm printf xuất số nguyên hình Chú ý chuỗi định dạng printf có ba “%i”, để nhận ba trị từ hai biến (a, b) biểu thức (a-b) Định dạng “%i” “%d” giống dùng printf Tuy nhiên, với scanf, “%i” nhận số hệ octal hexadecimal, “%d” nhận số thập phân Các biểu thức Chú ý C hiểu phải tính tốn biểu thức “a-b” in kết trị nguyên Bạn tạo biến “c” khác, gán cho trị “a-b” in trị “c”, khơng cần thiết © Dương Thiên Tứ www.trainingwithexperts.com Các biến (variables)  Các biến phải khai báo trước dùng sau dấu “{”    Các ký tự hợp lệ chữ cái, số, “_”  Vài trình biên dịch hiểu ký tự đầu biến toàn cục tên hàm  Phân biệt chữ hoa chữ thường Ký tự số C hiểu 31 ký tự đầu biến cục bộ, dùng nhiều bị bỏ qua Các biến Khai báo biến Trong C, tất biến phải khai báo trước dùng Nghĩa bạn phải đặt cho biến tên (gọi định danh – identifier), gán cho biến kiểu, khởi tạo biến với trị) trước dùng Trong C, khai báo biến phải nằm trước tất phát biểu khối C99 không bắt buộc điều Các tên hợp lệ Khơng dùng từ khóa dành riêng cho C làm tên biến Chỉ có chữ cái, số dấu “_” dùng hợp lệ tên biến Ký tự đầu biến chữ hay dấu “_” (theo chuẩn, nên tránh dùng ký tự “_” làm ký tự đầu) Như tên biến “temp_in_celsius”, “index32” “sine_value” hợp lệ, tên biến “32index”, “tempin-celsius” “sine$value” khơng hợp lệ Tên biến dài, trình biên dịch xếp với 31 ký tự đầu Các ký tự cịn lại trình biên dịch bỏ qua, nên tên biến dài phải khác 31 ký tự đầu C99 cho phép đến 63 ký tự Vài trình biên dịch hiểu ký tự đầu với biến toàn cục tên hàm Các ký tự hoa Các ký tự hoa dùng tên biến muốn Người ta thường dùng thay ký tự “_” dùng phổ biến trước đây, ví dụ thay cho tên biến “temp_in_celcius” tên biến “tempInCelcius” Kiểu định danh dùng phổ biến gần dấu “_” bị bỏ quên © Dương Thiên Tứ www.trainingwithexperts.com printf scanf  printf xuất trị nguyên (integer) hình dùng %i  scanf nhập trị nguyên (integer) từ bàn phím dùng %i  “&” RẤT quan trọng với scanf (cần để thay đổi tham số truyền, bàn sau) - thiếu, chương trình chạy sai  “&” khơng cần với printf trị tham số dùng printf scanf printf Hàm printf ghi kết xuất hình Khi gặp định dạng thức %i, số nguyên in Muốn in dấu %, dùng %% chuỗi định dạng, ví dụ: printf("Net profit: %d%%\n", profit); scanf Hàm scanf nhập từ bàn phím Khi gặp định dạng thức %i, chờ người dùng nhập số nguyên & Toán tử (operator) “&” quan trọng với scanf Nó cho phép scanf thay đổi biến truyền tới Một lỗi phổ biến quên “&” dùng scanf Ví dụ: scanf("%i", &j); ký tự “&” cho phép biến “j” thay đổi Nếu quên ký tự này, j nhận ký tự ngẫu nhiên mà có trước (trừ bạn khởi tạo “j”) Trong đó, printf khơng cần thay đổi trị biến in ra, khơng cần dấu “&” Nếu “j” chứa trị 15, sau thực phát biểu: printf("%i", j); bạn nhận trị 15 từ biến “j” printf khơng có khả thay đổi © Dương Thiên Tứ www.trainingwithexperts.com Kiểu nguyên C   C hỗ trợ nhiều kiểu số nguyên khác Cực đại Cực tiểu định nghĩa “limits.h” Kiểu Dạng thức bytes Cực tiểu Cực đại char %c CHAR_MIN CHAR_MAX signed char %c SCHAR_MIN SCHAR_MAX unsigned char %c UCHAR_MAX short [int] %hi SHRT_MIN SHRT_MAX unsigned short %hu USHRT_MAX int %i INT_MIN INT_MAX unsigned int %u UINT_MAX long [int] %li LONG_MIN LONG_MAX unsigned long %lu ULONG_MAX Kiểu nguyên C limits.h Tập tin tiêu đề (header) chứa định nghĩa vài số, cho biết kích thước cực đại cực tiểu vài kiểu nguyên Các kiểu nguyên khác C hỗ trợ kiểu ngun có kích thước khác Các từ short long phản ánh kích thước vùng nhớ dành cho Một số ngun short cần vùng nhớ số nguyên long Các số cho bạn biết giới hạn kiểu nguyên, dễ dàng cho việc lựa chọn kiểu nguyên thích hợp Chú ý kích thước kiểu int byte tùy theo hệ (platform) cụ thể Vì chương trình có tính khả chuyển thường khơng dùng kiểu int mà dùng kiểu short long unsigned Từ khóa unsigned cho thấy tất bit dùng để lưu trữ số, với số có dấu, bit cao dùng để lưu trữ dấu Nghĩa kiểu unsigned lưu trữ trị lớn kiểu có dấu lần Khi unsigned dùng, số âm không lưu trữ, có số số dương %hi “h” cho thấy kiểu short nửa (half) kiểu int (tùy trình biên dịch) C99 C99 cung cấp thêm hai kiểu nguyên chuẩn: long long int unsigned long long int, 64-bit, dùng cho trị nguyên cực lớn Với kiểu long long int, thêm LL (hoặc ll) phía sau trị khai báo Với kiểu unsigned long long int, thêm U (hoặc u) phía trước sau LL (hoặc ll) Chuỗi định dạng printf scanf thêm ll trước d, o, u, x: long long x; scanf("%lld", &x); © Dương Thiên Tứ www.trainingwithexperts.com Ví dụ kiểu nguyên #include #include int main(void) { unsigned long big = ULONG_MAX; printf("minimum printf("maximum printf("maximum printf("maximum printf("maximum return 0; } int = %i, ", INT_MIN); int = %i\n", INT_MAX); unsigned = %u\n", UINT_MAX); long int = %li\n", LONG_MAX); unsigned long = %lu\n", big); minimum maximum maximum maximum int = -32768, maximum int = 32767 unsigned = 65535 long int = 2147483647 unsigned long = 4294967295 Ví dụ kiểu nguyên INT_MIN, INT_MAX Kết chương trình cho thấy kiểu int có kích thước byte (hệ 16 bit) Vì trị cực đại 32767 Nó cho thấy trị cực đại kiểu unsigned int gấp đôi 65535 Tương tự, trị cực đại unsigned long int gấp đôi trị cực đại signed long int © Dương Thiên Tứ www.trainingwithexperts.com Ví dụ kiểu char #include #include int main(void) { char lower_a = 'a'; char lower_m = 'm'; printf("minimum char = %i, ", CHAR_MIN); printf("maximum char = %i\n", CHAR_MAX); printf("Sau '%c' la '%c'\n", lower_a, lower_a + 1); printf("Chu hoa la '%c'\n", lower_m - 'a' + 'A'); return 0; } minimum char = -128, maximum char = 127 Sau 'a' la 'b' Chu hoa la 'M' Ví dụ kiểu char char C dành kiểu liệu char cho ký tự Các ký tự định trị cách đặt ký tự cặp dấu nháy đơn ‘ ’ Ví dụ: char lower_a = 'a'; đặt mã ASCII ký tự “a” thường, 97, vào biến “lower_a” Khi trị 97 in với chuỗi định dạng %c, chuyển trở lại thành ký tự “a” thường Nếu chương trình chạy máy EBCDIC, trị lưu có giá trị khác, chuyển lại thành “a” xuất CHAR_MIN, CHAR_MAX Hai trị cực đại cực tiểu kiểu char Vì kiểu char ln chiếm byte, bạn nghĩ trị đến 255 Tuy nhiên, C không định nghĩa char unsigned signed, nên trị cực tiểu -128 trị cực đại 127 Số học với char Chương trình cho thấy trình biên dịch dễ dàng thực phép tính số học với kiểu char Ví dụ: lower_a + kết 97+1=98 Đây trị ký tự “b” thường (nằm sau “a”) Phép tính: lower_m - 'a' + 'A' in ký tự “M”, cho kết khác máy EBCDIC %c %i Mặc dù kiểu char in với chuỗi định dạng %i, kiểu làm Ví dụ khơng thể in kiểu int short với %li Chú ý mẫu tự “l” khơng phải số © Dương Thiên Tứ www.trainingwithexperts.com Dùng tập tin tiêu đề  Tập tin tiêu đề bảo đảm tính quán module project.h #include "project.h" int main(void) { step = 0.15F; print(0.0, 5.5F); return 0; } extern double step; void print(double, double); #include #include "project.h" double step; void print(double start, double stop) { printf("Celsius\tFahrenheit\n"); for (;start < stop; start += step) printf("%.1lf\t%.1lf\n", start, start * 1.8 + 32); } Dùng tập tin tiêu đề Giải pháp trì tính qn dự án C đơn giản: dùng tập tin tiêu đề (header) Từ khai báo tập tin tiêu đề (.h), tiền xử lý kiểm tra chéo nội dung module khác Đặt khai báo sau tập tin tiêu đề, không đặt module: - Các khai báo extern - Các khai báo nguyên mẫu hàm (prototype), chúng khai báo static Trong ví dụ trên, tập tin tiêu đề có khai báo extern prototype: extern double step; void print(double, double); Module thứ (bên trái) nhìn thấy khai báo này, trình biên dịch kiểm tra biến danh sách tham số gọi hàm nên biên dịch thành công, dù module thứ hai (bên phải) chưa biên dịch 177 © Dương Thiên Tứ www.trainingwithexperts.com Kết hợp với tiền xử lý  Dùng tiền xử lý khai báo biến tập tin tiêu đề #if defined(MAIN) #define EXTERN #else #define EXTERN extern #endif #include "globals.h" first.c #include "globals.h" second.c EXTERN double step; EXTERN long current; EXTERN short res; #define MAIN #include "globals.h" main.c globals.h Kết hợp với tiền xử lý Module “main.c” có #define MAIN, nên EXTERN khơng định nghĩa, module “main.c” nhìn thấy biến “globals.h” sau: double step; long current; short res; Các module “first.c” “second.c” không định nghĩa MAIN, nên EXTERN định nghĩa extern hai module nhìn thấy biến “globals.h”: extern double step; extern long current; extern short res; Không khởi tạo cho biến khai báo extern 178 © Dương Thiên Tứ www.trainingwithexperts.com Tổng kết  Một union lưu trị nhiều kiểu khác thời điểm khác  enum cung cấp cách thiết lập trị tự động cho số     Bộ tiền xử lý cho phép tạo số macro Dữ liệu hàm chia sẻ module static khóa việc chia sẻ liệu hàm Dùng tập tin tiêu đề để đảm bảo tính quán module dự án Tổng kết 179 © Dương Thiên Tứ www.trainingwithexperts.com Heap C Heap      Heap gì? Các mảng cấp phát động Các hàm calloc \ malloc \ realloc free Cấp phát động mảng mảng Các cấu trúc liệu cấp phát động C Heap Chương trình bày cách lưu trữ liệu heap, khôi phục heap, mở rộng thu giảm không gian lưu trữ heap giải phóng 180 © Dương Thiên Tứ www.trainingwithexperts.com Heap gì?   Một chương trình thực thi chia vào phần:  Data segment: biến tồn cục chuỗi lưu trữ Kích thước cố định  Code segment: hàm main, printf, scanf, …lưu trữ Chỉ đọc (read-only) Kích thước cố định  Heap: biết với tên “vùng nhớ cấp phát động” (dynamic memory), heap cho phép sử dụng thay đổi kích thước chương trình hoạt động Stack: cung cấp nơi lưu trữ biến cục bộ, thay đổi kích thước chương trình hoạt động Heap gì? Các phần chương trình thực thi Một chương trình thực thi nhớ thể theo sơ đồ: biến auto Stack (kích thước thay đổi) “vùng nhớ cấp phát động” Heap (kích thước thay đổi) biến tồn cục, chuỗi Data segment (kích thước cố định, phần đọc) main, printf, scanf, … Code segment (kích thước cố định, đọc) Stack Code segment Data segment có kích thước cố định thời gian sống chương trình Trong lúc Stack: tăng kích thước hàm gọi, tham số đẩy vào, biến cục tạo giảm kích thước hàm trả về, biến cục bị hủy, tham số đẩy Heap Stack “đối lập” với Heap “đối lập” với stack, việc dùng stack tăng (gọi hàm lồng nhiều cấp, tạo mảng cục lớn, …) lượng khơng gian heap dùng thu lại Tương tự, heap tăng, khơng gian stack dùng thu lại Lượng nhớ cấp cho heap tùy thuộc vào hệ điều hành 181 © Dương Thiên Tứ www.trainingwithexperts.com Mảng cấp phát động  Các mảng C có vấn đề – kích thước chúng phải cố định viết chương trình  Khơng có cách tăng (hoặc giảm) kích thước mảng chương trình biên dịch  Các mảng cấp phát động khác, kích thước chúng xác định lúc chạy thay đổi lần có yêu cầu  Chỉ cần pointer Mảng cấp phát động Mảng C cấu trúc liệu thơ sơ Khơng có chế ngơn ngữ C để thay đổi kích thước mảng khai báo Tuy nhiên việc lưu trữ mảng cấp phát động heap Việc lưu trữ phải thực vùng nhớ vật lý liên tục (chỉ cần cho mảng), thường trình quản lý heap bảo đảm điều Tất chương trình cần pointer chứa địa nơi bắt đầu khối nhớ Một ví dụ Một mảng 100 long int khai báo sau: long a[100]; có kích thước cố định (400 byte) Cố gắng làm cho rộng hơn, như: long a[200]; gây lỗi trình biên dịch nhìn thấy biến “a” khai báo hai lần Nếu ta làm sau: long *p; p = malloc(100 * sizeof(long)); ta nhận lượng vùng nhớ (400 byte), lưu trữ heap thay cho stack data segment Một phần tử mảng “a” truy xuất với a[58], phần tử mảng “p” truy xuất với p[58] Mảng làm rộng (cấp phát lại từ p) với: long *q; q = realloc(p, 200 * sizeof(long)); tăng không gian lưu trữ lên 200 long int, tức 800 byte 182 © Dương Thiên Tứ www.trainingwithexperts.com Dùng mảng cấp phát động  Các bước sau tạo mảng cấp phát động: Khai báo pointer có kiểu tương ứng với kiểu liệu phần tử mảng Khởi tạo pointer calloc hay malloc với tổng không gian lưu trữ cần cho tất phần tử mảng Kiểm tra tránh trường hợp pointer NULL Tăng giảm số phần tử cách gọi hàm realloc Giải phóng vùng nhớ cấp phát cách gọi hàm free Dùng mảng cấp phát động Một pointer cho mảng cấp phát động Một pointer cần cho mảng cấp phát động lưu trữ heap Kiểu pointer kiểu liệu phần tử mảng Như cho mảng double, mảng short int, cần có hai pointer: double *double_array; short *short_array; Tính tốn khơng gian lưu trữ Bước dùng để tính tốn có vùng nhớ cần cho mảng Nếu có 100 double, 480 short cần: double_array = malloc(100 * sizeof(double)); short_array = calloc(480, sizeof(short)); Có chút khác malloc calloc, 100 double “double_array” đến chứa trị ngẫu nhiên, lúc 480 short int “short_array” đến Thiếu không gian lưu trữ Có gọi malloc calloc khơng bảo đảm đủ vùng nhớ để lưu trữ phần tử Lần gọi thất bại ta cấp phát trước vùng nhớ lớn khơng cịn vùng nhớ Các thường trình báo giới hạn cách trả pointer NULL Như sau gọi thường trình cấp phát vùng nhớ, cần kiểm tra xem pointer trả về, “double_array” “short_array”, có NULL khơng Thay đổi kích thước mảng Kích thước vùng nhớ cấp pointer đến thay đổi cách gọi hàm realloc Giả sử 10 double không cần cần thêm 20 short int: da = realloc(double_array, 90 * sizeof(double)); sa = realloc(short_array, 500 * sizeof(short)); “da” pointer đến kiểu double, “sa” pointer đến kiểu short int Chú ý khơng nên viết: 183 © Dương Thiên Tứ www.trainingwithexperts.com sArray = realloc(short_array, 500 * sizeof(short)); Trong trường hợp cấp phát thất bại, tức khơng tìm thấy vùng nhớ liên tục để cấp phát, realloc trả NULL, NULL gán cho “short_array” mảng bị Khi realloc thành công Khi cấp phát thành công, pointer khác NULL gán cho “sa” Có hai “kịch bản”: realloc mở rộng khối nhớ trường hợp “sa” có địa với “short_array” 480 short int giữ nguyên, 20 short int tiếp sau realloc mở rộng khối nhớ tìm thấy khối nhớ “sa” chứa địa khác “short_array” 480 short int cũ chép tới khối nhớ mới, 20 short int tiếp sau Chúng ta không cần quan tâm “kịch bản” nào, cần quan tâm đến pointer “sa” đến phần tử đầu mảng short_int Nếu “sa” NULL, realloc thất bại “short_array” đến mảng khơng bị thay đổi Bảo trì vài pointer Một hệ “kịch bản” thứ hai tất pointer khác đếm mảng trước khơng dùng Ví dụ, có pointer: short * short100; khởi tạo với: short100 = short_array + 100; pointer khơng dùng mảng chuyển sang chỗ cấp phát Chúng ta KHÔNG dùng pointer “short_array” pointer “short100” Cả hai cần “tính tốn lại” sau: short_array = sa; short100 = sa + 100; Giải phóng vùng nhớ cấp phát Khi mảng double short int sử dụng xong, vùng nhớ cấp phát cho chúng giải phóng: free(double_array); free(short_array); Nói cho việc khơng cần làm heap phần tiến trình Khi tiến trình kết thúc tất vùng nhớ liên kết với giải phóng hệ điều hành Tuy nhiên, tốt giải phóng vùng nhớ trường hợp chương trình gọi gọi lại thường trình “dùng lần”, vùng nhớ cấp phát không giải phóng heap thu hẹp phân mảnh làm chương trình bị lỗi, thường gọi lỗi “memory leaks” 184 © Dương Thiên Tứ www.trainingwithexperts.com Ví dụ calloc / malloc #include #include int main(void) { unsigned i, s; double *p; printf("Bao nhieu so double? "); scanf("%u", &s); if ((p = calloc(s, sizeof(double))) == NULL) { fprintf(stderr, "Khong the cap phat %u bytes " "cho %u so double\n", s * sizeof(double), s); return 1; } Truy xuất "s" số for(i = 0; i < s; i++) p[i] = i; double thơng free(p); qua pointer "p" Giải phóng vùng return 0; nhớ cấp phát } If ((p = malloc(s * sizeof(double))) == NULL) { Ví dụ calloc / malloc Chương trình cho thấy làm việc với mảng double mà kích thước người dùng định thời gian chạy Chú ý số unsigned (int) dùng để tránh trường hợp người dùng nhập số âm Chú ý cách gán số cho pointer “p” để truy xuất trực tiếp mảng: p[i] = i; làm cho chương trình dễ đọc thay dùng pointer: *(p+i) 185 © Dương Thiên Tứ www.trainingwithexperts.com Ví dụ realloc double *p; double *p2; if((p = calloc(s, sizeof(double))) == NULL) { fprintf(stderr, "Khong the cap phat %u bytes " "cho %u so double\n", s * sizeof(double), s); return 1; } printf("Muon bao nhieu double? "); scanf("%u", &s); p2 = realloc(p, s * sizeof(double)); Tính kích thước mảng if(p2 == NULL) { cấp phát fprintf(stderr, "Khong the tang / giam mang"); free(p); Pointer “p” return 1; hợp lệ } Pointer “p” khơng cịn p = p2; đây, phải free(p); gán cho trị Ví dụ realloc Chương trình trình bày cách dùng realloc Như thảo luận trên, phép gán: p2 = realloc(p, s * sizeof(double)); bảo đảm hơn: p = realloc(p, s * sizeof(double)); cấp phát lại bị thất bại, pointer NULL trả gán vào “p” làm mảng Nếu không cấp phát lại chương trình cịn làm việc tiếp với mảng có 186 © Dương Thiên Tứ www.trainingwithexperts.com realloc làm tất  Hàm malloc free thừa hàm realloc làm tất  calloc khởi gán cho phần tử cấp phát trị p = malloc(s * sizeof(double)); p = realloc(NULL, s * sizeof(double)); free(p); realloc(p, 0); realloc làm tất Với tham số thích hợp, realloc thay malloc free Nó khơng thay hồn tồn calloc calloc khởi tạo cho phần tử cấp phát trị realloc thay malloc Một pointer NULL truyền tham số thứ khiến cho realloc thực thi malloc Ở đây, realloc không mở rộng vùng nhớ có (vì khơng có vùng này, NULL), mà cấp phát vùng nhớ malloc realloc thay free Một trị truyền tham số thứ hai khiến realloc giải phóng vùng nhớ có, điều tương đương với cấp phát vùng nhớ có kích thước Như realloc thay free 187 © Dương Thiên Tứ www.trainingwithexperts.com Cấp phát mảng mảng  Chú ý việc chuyển kiểu dùng cấp phát mảng mảng (mảng nhiều chiều) float *p; p = calloc(s, sizeof(float)); float **rain; rain = calloc(s, 365 * sizeof(float)); float (*rainfall)[365]; rainfall = calloc(s, 365 * sizeof(float)); rainfall[s-1][18] = 4.3F; Cấp phát mảng mảng Chúng ta biết cách cấp phát cho mảng Đơn giản thực việc cấp phát địa khối nhớ đến pointer Với mảng mảng việc cấp phát khơng đơn Trong ví dụ trên, mảng cấp phát với: float *p; p = calloc(s, sizeof(float)); thành phần mảng truy xuất cách gán số cho pointer, ví dụ truy xuất phần tử thứ tư p[3] (giả sử số phần tử “s” lớn 4) Cuối chương bàn mảng, ví dụ “rainfall”, dùng đến mảng mảng Mảng “rainfall” lưu lượng mưa 12 vùng 365 ngày năm, khai báo sau: float rainfall[12][365]; Bây giả sử mảng “rainfall” cấp phát động với số vùng nhập vào lúc chạy chương trình Rõ ràng với vùng cần có 365 số float, với 10 vùng chẳng hạn, cần 3650 số float Cách mà thường nghĩ hợp lý là: float **rain; rain = calloc(s, 365 * sizeof(float)); (ở giả sử “s” 10) Tuy nhiên pointer “rain” không chứa đủ thông tin để truy xuất phần tử, ví dụ “rain[2][100]” chẳng hạn Trình biên dịch khơng thể xác định vị trí truy xuất từ pointer “rain” Dùng pointer đến mảng Giải pháp khai báo pointer sau: float (*rainfall)[365]; (“rainfall” pointer đến mảng khối 365 float) Trình biên dịch hiểu 188 © Dương Thiên Tứ www.trainingwithexperts.com với truy xuất “rainfall[2][100]”, nhân với kích thước 365 số float (bởi “rainfall” ;là pointer đến mảng khối 365 float, nhân tỷ lệ với kích thước đối tượng “rainfall” đến, tức 365 số float) Nó hiểu 100 nhân tỷ lệ với kích thước float Như trình biên dịch tính tốn vị trí 189 © Dương Thiên Tứ www.trainingwithexperts.com Cấu trúc liệu cấp phát động struct Node { int data; struct Node *next; }; struct Node* newNode(int data) { struct Node *p; if ((p = malloc(sizeof(struct Node)) == NULL) { fprintf(stderr, "Ran out of dynamic memory!\n"); exit(9); } p->data = data; p->next = NULL; return p; } Cấu trúc liệu cấp phát động Các cấu trúc liệu cấp phát động, sở để xây dựng cấu trúc liệu nâng cao danh sách liên kết, nhị phân, đồ thị có hướng, … Ta khai báo pointer đến cấu trúc liệu cần cấp phát, node danh sách liên kết Cấp phát cho pointer hàm malloc, với kích thước vùng nhớ kích thước node Dữ liệu data node nhận từ tham số truyền cho hàm newNode, pointer next thiết lập NULL khơng đến node khác Tạo danh sách liên kết duyệt danh sách liên kết: struct Node *firstNode, *secondNode, *thirdNode, *current; firstNode = newNode(-100); secondNode = newNode(0); firstNode->next = secondNode; thirdNode = newNode(10); secondNode->next = thirdNode; current = firstNode; while (current != NULL) { printf("%i\n", current->data); current = current->next; } 190 © Dương Thiên Tứ www.trainingwithexperts.com Tổng kết        Heap Stack phát triển theo hai hướng ngược Khả cấp phát động nhiều hay Heap tùy theo hệ điều hành Các hàm malloc, calloc, realloc free giúp thao tác cấp phát giải phóng Heap Chỉ có realloc thật cần Cấp phát động mảng Cấp phát động mảng mảng Cấp phát động structure Tổng kết 191 ... www.trainingwithexperts.com Đ? ?c điểm C     C ngôn ngữ c? ??p thấp C ngơn ngữ lập trình hệ thống C gọn nhẹ khả chuyển Chương trình C khó hiểu khó tùy biến Đ? ?c điểm C Ngơn ngữ c? ??p thấp Kh? ?c với ngơn ngữ lập trình c? ??p... c? ? đ? ?c tả ngơn ngữ kh? ?c, thay vào C cung c? ??p thư viện lớn hàm chuẩn: xuất nhập, xử lý chuỗi, c? ??p phát vùng nhớ, … t? ?c vụ tiện ích kh? ?c Trình biên dịch C nhỏ dễ viết Sau chuẩn hóa, C cung c? ??p... Kiểu nguyên C limits.h Tập tin tiêu đề (header) chứa định nghĩa vài số, cho biết kích thư? ?c c? ?c đại c? ? ?c tiểu vài kiểu nguyên C? ?c kiểu nguyên kh? ?c C hỗ trợ kiểu ngun c? ? kích thư? ?c kh? ?c C? ?c từ short

Ngày đăng: 21/12/2022, 09:38

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

Tài liệu liên quan