Danh sách chương trình chính. Liệt kê - nói một cách đơn giản là gì: phân tích đầy đủ về khái niệm

Mà một người có thể đọc được. Theo nghĩa tổng quát - bất kỳ dữ liệu đầu vào nào cho người dịch. Mã nguồn được dịch sang mã thực thi hoàn toàn trước khi chạy chương trình bằng trình biên dịch hoặc có thể được thực thi ngay lập tức bằng trình thông dịch.

Mục đích

Mã nguồn được sử dụng để tạo mã đối tượng hoặc được thực thi bởi trình thông dịch. Các thay đổi chỉ được thực hiện đối với bản gốc, sau đó là chuyển đổi lại thành đối tượng.

Một mục đích quan trọng khác của mã nguồn là mô tả chương trình. Dựa trên văn bản của chương trình, bạn có thể xây dựng lại logic hành vi của nó. Bình luận được sử dụng để làm cho mã nguồn dễ hiểu hơn. Ngoài ra còn có các công cụ cho phép bạn tự động lấy tài liệu mã nguồn - cái gọi là. máy tạo tài liệu.

Ngoài ra mã nguồn còn có nhiều công dụng khác. Nó có thể được sử dụng như một công cụ giảng dạy; Những lập trình viên mới bắt đầu có thể thấy hữu ích khi kiểm tra mã nguồn hiện có để tìm hiểu các kỹ thuật và phương pháp lập trình. Nó cũng được sử dụng như một công cụ giao tiếp giữa các lập trình viên có kinh nghiệm do tính chất ngắn gọn và rõ ràng của nó. Chia sẻ Quản lý mã thường được các nhà phát triển coi là một yếu tố góp phần cải thiện trải nghiệm của lập trình viên.

Các lập trình viên thường di chuyển mã nguồn (nguyên trạng hoặc dưới dạng mô-đun) từ dự án này sang dự án khác, điều này được gọi là tái sử dụng mã.

Mã nguồn là một thành phần quan trọng cho quá trình chuyển phần mềm sang các nền tảng khác. Nếu không có mã nguồn của bất kỳ phần mềm nào thì việc chuyển đổi sẽ quá khó hoặc hoàn toàn không thể thực hiện được.

Tổ chức

Mã nguồn của một số phần của phần mềm (mô-đun, thành phần) có thể bao gồm một hoặc nhiều tệp. Mã chương trình không nhất thiết phải được viết chỉ bằng một ngôn ngữ lập trình. Ví dụ: thông thường các chương trình được viết bằng ngôn ngữ C có chứa phần chèn thêm mã hợp ngữ vì lý do tối ưu hóa. Cũng có thể xảy ra trường hợp một số thành phần hoặc bộ phận của chương trình được viết bằng nhiều ngôn ngữ khác nhau, tiếp theo là lắp ráp thành một mô-đun thực thi sử dụng một kỹ thuật được gọi là liên kết thư viện ( liên kết thư viện).

Tổ hợp phần mềm trong quá trình lắp ráp yêu cầu sử dụng hàng chục, thậm chí hàng trăm tệp với mã nguồn. Trong những trường hợp như vậy, để đơn giản hóa việc xây dựng, các tệp dự án thường được sử dụng có chứa mô tả về sự phụ thuộc giữa các tệp mã nguồn và mô tả quá trình xây dựng. Các tệp này cũng có thể chứa các tùy chọn cho trình biên dịch và môi trường thiết kế. Có thể được sử dụng cho các môi trường thiết kế khác nhau các tập tin khác nhau dự án và trong một số môi trường, các tệp này có thể ở định dạng văn bản, phù hợp với chỉnh sửa trực tiếp lập trình viên sử dụng phổ quát soạn thảo văn bản, được hỗ trợ trong các môi trường khác định dạng đặc biệt, việc tạo và sửa đổi tệp được thực hiện bằng các chương trình công cụ đặc biệt. Các tệp dự án thường được bao gồm trong thuật ngữ "mã nguồn". Thông thường, mã nguồn cũng có nghĩa là các tệp tài nguyên chứa nhiều dữ liệu khác nhau, ví dụ: Hình ảnh đồ hoạ, cần thiết để xây dựng chương trình.

Để làm việc với mã nguồn dễ dàng hơn và sự hợp tác Một nhóm lập trình viên sử dụng hệ thống kiểm soát phiên bản để xử lý mã.

Chất lượng

Không giống như con người, không có mã “viết tốt” hay “viết dở” cho máy tính. Nhưng cách viết mã có thể ảnh hưởng lớn đến quá trình bảo trì phần mềm. Chất lượng của mã nguồn có thể được đánh giá bằng các tham số sau:

  • khả năng đọc mã (bao gồm cả sự hiện diện của các nhận xét về mã);
  • dễ dàng hỗ trợ, kiểm tra, gỡ lỗi và sửa lỗi, sửa đổi và chuyển đổi;
  • sử dụng tiết kiệm tài nguyên: bộ nhớ, bộ xử lý, dung lượng ổ đĩa;
  • sự vắng mặt của các bình luận do trình biên dịch đưa ra;
  • không có "rác" - các biến không được sử dụng, các khối mã không thể truy cập được, các nhận xét lỗi thời không cần thiết, v.v.;
  • xử lý lỗi đầy đủ;
  • khả năng quốc tế hóa giao diện.

Mã nguồn không thể thực thi được

Giấy phép Copyleft cho phần mềm miễn phí yêu cầu phân phối mã nguồn. Các giấy phép này cũng thường được sử dụng cho các tác phẩm không phải là phần mềm - ví dụ: tài liệu, hình ảnh, tệp dữ liệu cho trò chơi máy tính.

Trong những trường hợp như vậy, mã nguồn được coi là dạng tác phẩm được ưu tiên biên tập. Trong các giấy phép không phải phần mềm, nó cũng có thể được gọi là phiên bản trong " định dạng trong suốt" Điều này có thể là, ví dụ:

  • đối với tệp nén bị mất dữ liệu - phiên bản không mất dữ liệu;
  • để hiển thị hình ảnh vector hoặc mô hình ba chiều - tương ứng là phiên bản vectơ và mô hình;
  • đối với hình ảnh văn bản - cùng một văn bản trong

Danh sách chương trình “demol.c” được hiển thị trong Hình. 6.7. Lập trình viên sẽ nghĩ ra tên cho chương trình. Phần mở rộng “.c” là bắt buộc. Văn bản được gõ vào trình soạn thảo “Programmer's Notepad 2” (viết tắt là PN2), được bao gồm trong gói WinAVR thay vì vỏ đồ họa. Nó cũng cho phép bạn tạo một dự án, sửa lỗi, biên dịch chương trình và thậm chí cả lập trình MK.

Cơm. 6.7. Danh sách chương trình "demol.c".

Cơm. 6.8. Vẻ bề ngoài chương trình PN2.

Trình soạn thảo PN2 là một dự án độc lập có trang web riêng. Giao diện của menu chính PN2 được thể hiện trong Hình 2. 6.8.

Thủ tục.

1.3chạy tệp “WlnAVR-20100110-install.exe” (29 MB) nằm trên đĩa CD được cung cấp. Tập tin này có thể được tải xuống miễn phí từ Internet nếu cần thiết. Cài đặt WlnAVR theo mặc định trong thư mục C:\WinAVR-20100110\.

2. Mở trình soạn thảo PN2: “Start - Programs - WinAVR-20100110 - Programmers Notepad”. Bật đánh số dòng: “Công cụ - Tùy chọn - Chung - Mặc định -<поставить «галочку» возле «Show Line Numbers»>- ĐƯỢC RỒI".

3. Tạo một tệp C mới trong trình soạn thảo PN2: “Tệp - Mới - C/C++”. Nhập văn bản chương trình từ bàn phím theo Hình 2. 6.7 và lưu vào ổ cứng: “File - Save As... -<ввести путь и имя файла, например, для однозначности C:\1001\demol.c>- ĐƯỢC RỒI".

Ở đây và bên dưới, ảnh chụp màn hình (ảnh chụp màn hình) sẽ chỉ được hiển thị ở những điểm hành động quan trọng nhất để tiết kiệm dung lượng. Các ảnh chụp màn hình khác trong chế độ từng bước Bạn có thể xem video hướng dẫn trên đĩa CD đi kèm.

Giải thích cho việc liệt kê.

Dòng 1 bắt đầu bằng các nhận xét, được phân cách ở bên trái bằng hai dấu gạch chéo về phía trước. Tất cả văn bản sau dấu “//” có thể là về bất kỳ thứ gì, bằng bất kỳ ngôn ngữ nào, với bất kỳ quyền tự do và chữ viết tắt nào. Đây là thông tin tùy ý mà lập trình viên viết cho chính mình, cho người thân của mình, để trong một hoặc hai tháng, anh ta có thể nhớ lại những gì đã thực sự được thảo luận. Thường được chỉ định tên ngắn chương trình và quyền tác giả.

Dòng 2 cũng chứa các bình luận nhưng mang tính chất kỹ thuật. Dưới đây là sơ đồ điện để kết nối đèn báo HL1 và nút SB1 với các dòng cổng MK cụ thể. Tên của các tín hiệu tương ứng với bố cục từ bảng điều khiển trên MHKpocxeMyATmega48A (Hình 6.9).

Cơm. 6.9. Bố trí tín hiệu MKATmega48A.

Luyện tập mô tả văn bản kế hoạch đơn giản trong tiêu đề chương trình được phổ biến rộng rãi trong cộng đồng lập trình viên quá lười vẽ và áp dụng sơ đồ đồ họa, tin rằng “dù sao thì mọi chuyện cũng rõ ràng”.

Dòng 3 chỉ định các tham số makefile. Để tham khảo, hoạt động binh thương Có thể biên dịch AVR-GCC nếu có hai tệp được yêu cầu. Đầu tiên trong số đó là tệp danh sách có phần mở rộng “.c”, thứ hai là tệp hướng dẫn hệ thống “makefile” không có phần mở rộng. “Makefile” được tạo bởi tiện ích “MFile” (Hình 6.10, tác giả JoergWunsch, Đức), có trong WinAVR.

Cơm. 6.10. Xuất hiện chương trình MFile.

Thủ tục.

1. Chạy tiện ích “MFile”: “Start - Programs - WinAVR-2010010 – MFile”.

2. Điền vào các trường mẫu như sau (Hình 6.11):

Trong mục “Makefile - Main file name… - Main file” nhập tên dự án đang được phát triển “demol” và nhấn OK;

Trong mục “Makefile - MCU type - ATmega” chọn MK “atmega48a”;

Trong mục “Makefile - Mức tối ưu hóa”, đặt mức tối ưu hóa thành “2”. Các khả năng khác: “0” - không tối ưu hóa, “s” - độ dài tối thiểu của mã, số “1” ... “3” là ba phương pháp khác nhau tối ưu hóa và số “3” không có nghĩa là sự lựa chọn tốt nhất, tất cả phụ thuộc vào chương trình C cụ thể.

Không cần thiết phải điều chỉnh các điểm còn lại của mẫu “makefile”, hãy để chúng giữ nguyên như mặc định.

Cơm. 6.11. Điền vào các trường trong chương trình MFile.

Đối với mỗi dự án mới và loại MK mới, bạn cần tạo “makefile” của riêng mình, tên dự án và loại MK sẽ thay đổi.

Dòng 4 chứa các chú thích xác định số thập lục phân byte cấu hình thấp (Thấp), cao (Cao) và mở rộng (Ext). Những giá trị này sẽ cần thiết sau này khi lập trình MK “cầu chì”.

S «ktg5 trống về mặt thông tin. Nó phân tách trực quan văn bản nhận xét khỏi phần còn lại của chương trình. Thay vì một, bạn có thể chèn hai dòng trống, điều này không quan trọng. Điểm chính là cải thiện khả năng hiển thị. Điều này không ảnh hưởng đến độ dài của mã firmware MK.

Thiết kế bên ngoài của danh sách do chính lập trình viên tạo ra, dựa trên ý tưởng của riêng anh ấy về vẻ đẹp của hình ảnh và sự dễ dàng trình bày thông tin. Thông thường, bằng phong cách thiết kế “tiêu đề”, liệt kê các dòng và nhận xét, bạn có thể xác định tác giả của chương trình hoặc tạo ra bức chân dung tâm lý của người đó. Những ghi chú thú vị về vấn đề này được Alain Golub đưa ra trong chuyên khảo.

Dòng 6 được phục vụ bởi bộ tiền xử lý trình biên dịch. Đây không phải là một tuyên bố hoặc một bình luận bằng ngôn ngữ C. Cái tên phức tạp nhưng ý nghĩa lại đơn giản. Bộ tiền xử lý (tiền XỬ LÝ) tìm kiếm các dòng trong chương trình bắt đầu bằng ký tự “#”. Tiếp theo, tùy thuộc vào từ khóa, nó thực hiện Hành động cụ thể, ví dụ: “xác định” - gán giá trị cho các hằng số, “nếu được xác định” - kiểm tra một điều kiện, “inC1ude” - kết nối một thư viện hàm, v.v.

Đối với người mới làm quen lần đầu, chỉ cần biết rằng thư viện hàm là một tập hợp các tệp chứa các văn bản của các quy trình tiêu chuẩn hoặc thường xuyên lặp lại. Trong trường hợp này, lệnh “#inC1ude” (dịch từ tiếng Anh là “bao gồm”) sẽ kích hoạt thư viện hệ thống“avr/io.h”, chịu trách nhiệm vận hành các cổng I/O. Thư viện này có các trình biên dịch khác nhau tên khác, nhưng bản chất vẫn như cũ, nếu không có nó bạn không thể quản lý bất kỳ dòng cổng MK nào. Do đó, việc kết nối thư viện I/O là bắt buộc đối với tất cả các chương trình C của vi điều khiển.

Dòng 7 cũng được xử lý bởi bộ tiền xử lý của trình biên dịch nhưng có từ khóa “define” (dịch từ tiếng Anh là “define”). Do đó, nó khai báo hằng số INI và gán cho nó giá trị không đổi 255. Các chú thích chỉ định cách dịch số 255 sang dạng số thập lục phân 0xFF và 0bl nhị phân 1111111. Sự khác biệt giữa chúng nằm ở các chữ cái “x” và “b” sau số bắt buộc “0”. Các số phù hợp trong hệ thống khác nhau thể hiện trong bảng. 6.3.

Bảng 6.3. Chuyển đổi số từ thập lục phân sang nhị phân và ngược lại

Nếu ở đâu đó trong “nội dung” của chương trình có hằng số INI, thì trình biên dịch sẽ thay thế mà không cần suy nghĩ giá trị số, được chỉ định ở dòng 7, tức là 255. Điều này rất thuận tiện cho người lập trình khi sửa các danh sách lớn, khi các hằng số nằm rải rác trong văn bản. Ngoài ra, bản thân tên của hằng số có thể mang tải ngữ nghĩa và đóng vai trò như một gợi ý bằng lời nói. Đặc biệt, INI là viết tắt của từ tiếng Anh “initialization”, có nghĩa là giá trị ban đầu nào đó.

Tầm quan trọng của việc đặt một hằng số trong “tiêu đề” của chương trình nằm ở việc dễ dàng tìm thấy nó và tốc độ thực hiện các thay đổi. Ví dụ: sau khi sửa số “255” một lần, bạn có thể chắc chắn rằng ở mọi nơi trong văn bản, số này sẽ được nhập tự động (và không có lỗi!) thông qua hằng số lNI.

Lập trình viên nghĩ ra tên cho hằng số dựa trên cảm nhận thông thường và sở thích của con người. Theo truyền thống lâu đời, tên hằng được viết ký tự viết hoa. Chữ cái đầu tiên trong tên phải là I2CBUS, T34. Việc sử dụng bảng chữ cái Cyrillic không được phép.

Việc khai báo hằng số INI có thể được viết theo hai cách tương đương hơn: “#define INI OxFF” hoặc “#define INI Obl 1111111”.

Dòng 8 chứa câu lệnh khai báo biến "a". Một biến có thể được biểu diễn theo nghĩa bóng dưới dạng một hộp (hộp, hộp, hộp bút chì), trong đó một số đối tượng nhất định (hạt, hạt, que diêm) được lưu trữ. Để làm cho các “hộp” khác nhau, chúng được đánh dấu bằng các dòng chữ khác nhau trên thân, trong trường hợp này là chữ “a”. Nếu mô tả biến không chỉ ra số ban đầu thì “hộp” được coi là trống và nó được khởi tạo bằng 0 (a = 0). Khi chương trình tiến triển, các đối tượng có thể được thêm vào “hộp” và các đối tượng có thể bị xóa khỏi nó, tức là. tăng và giảm giá trị của một biến.

Khối lượng của “hộp” phụ thuộc vào quảng cáo ban đầu của nó. Trong bảng. 6.4 hiển thị các giới hạn được chấp nhận trong trình biên dịch AVR-GCC. Như bạn có thể thấy, khai báo “unsigned char” cho phép bạn đặt 255 mục vào “hộp”. Cùng với giá trị 0(“hộp trống”) sẽ có tổng cộng 256 trạng thái hoặc 256 byte. Một biến có khai báo “unsigned long” không còn trông giống như một chiếc hộp nữa mà giống như cả một đoàn tàu, được thiết kế để chứa 4,2 tỷ vật phẩm.

Bảng 6.4. Thứ nguyên của các biến được AUK-vSS thông qua

Vì MK chưa phát triển các công cụ để làm việc với số âm nên ban đầu, để không bị nhầm lẫn, tốt hơn là chỉ sử dụng số dương trong các chương trình, tức là. chứa tuyên bố "không dấu".

Từ xa xưa, các “cha đẻ” của ngôn ngữ đã thiết lập một trật tự bất thành văn, theo đó tên của một biến không được quá 8 ký tự. Trong trình biên dịch AVR-GCC, quy tắc này bị bỏ qua và biến có thể chứa bao nhiêu ký tự tùy thích nhưng không quá cuồng tín. Điều duy nhất là chữ cái đầu tiên trong tên phải là một chữ cái trong bảng chữ cái Latinh, theo sau nó có thể là các chữ cái, số và ký hiệu. Bảng chữ cái Cyrillic không được phép.

Một biến, không giống như hằng số, chứa một số biến (chứ không phải hằng số). Để phân biệt các biến với các hằng, chúng được viết bằng chữ nhỏ. Thông thường, họ cố gắng đảm bảo rằng tên phù hợp với ý nghĩa, ví dụ: “đếm” cho bộ đếm, “dữ liệu” cho dữ liệu, “độ trễ” cho độ trễ. Mặc dù đôi khi các biến một chữ cái quen thuộc trong đại số ở trường trông đơn giản hơn, quen thuộc hơn và gọn hơn - a, b, c, d, i, j, k, x, y, z.

Lập trình viên xác định thứ nguyên nào sẽ chỉ ra cho một biến cụ thể. Vì biến “a” trong danh sách này là bộ lưu trữ để thu thập thông tin từ cổng 8 bit kỹ thuật số “C”, nên nó có nghĩa là nó phải chứa các byte “hai lũy thừa tám”, tức là. từ 0 đến 255.

Điều thú vị là trình biên dịch không đưa ra lỗi nếu bạn chơi an toàn và khai báo biến có giá trị dự trữ là “unsigned long a;”. Đúng, điều này dẫn đến việc tăng số lượng mã một cách không cần thiết từ 114 lên 126 byte và theo đó, tốc độ thực thi chương trình sẽ giảm một chút.

Một thái cực khác là đánh giá thấp thứ nguyên, ví dụ: khi thay vì khai báo “unsigned int”, “unsigned char” được sử dụng. Nếu bạn nhập một số lớn hơn 255 vào một biến như vậy thì chỉ phần dư của phép chia cho 256 sẽ được giữ lại và phần đầu sẽ bị mất không thể cứu vãn được. Nói một cách hình tượng, đồ vật tràn ra khỏi “chiếc hộp”. Trình biên dịch không phản ứng với những lỗi như vậy, tin rằng lập trình viên đang ở trạng thái thích hợp và hiểu mình đang làm gì. Việc xác định đúng và không có lỗi về số chiều của các biến thường đi kèm với kinh nghiệm.

Dòng 9 trống thông tin, tương tự như dòng 5. Việc chèn nó vào danh sách có phụ thuộc vào ý muốn của người lập trình hay không.

Dòng 10 chứa đầy các bình luận, nhưng chúng được trình bày theo một định dạng khác để đa dạng hơn. Cụ thể, văn bản được đánh dấu ở bên trái bằng các ký hiệu “/*” và ở bên phải bằng các ký hiệu “*/”. Phong cách này bắt nguồn từ các phiên bản cổ xưa nhất của ngôn ngữ C. Sau đó, ký hiệu “//” bắt đầu được sử dụng trong các chú thích, đặc trưng của ngôn ngữ C++. Trong WinAVR, cả hai tùy chọn đều có quyền tồn tại như nhau. Cách viết “mới” đơn giản hơn và trực quan hơn, trong khi cách viết “cũ” ở một số chỗ là cách duy nhất khả thi nếu bạn cần nhận xét ở phần đầu của một câu.

Dòng 11 chứa lời gọi điển hình đến chức năng "chính" theo quy định của Viện Tiêu chuẩn Quốc gia Hoa Kỳ (ANSI). Cho phép sử dụng các biểu thức viết tắt nhưng không nên sử dụng: “int main ()”, “main ()”, “main (void)”. Đôi khi họ còn viết “void main (void)”, nhấn mạnh sự vắng mặt hoàn toàn các thông số nhận và truyền. Đối với MK đơn giản không hỗ trợ hệ điều hành thời gian thực, sẽ không có hậu quả tiêu cực. Tuy nhiên, nếu bạn nghĩ về tương lai, tốt hơn hết bạn nên ghi nhớ ngay dạng văn bản đầy đủ, điều này sẽ giúp việc chuyển các chương trình C sang các nền tảng vi điều khiển hiện đại hơn trong tương lai trở nên dễ dàng hơn.

Dòng 12 hoàn toàn dành cho dấu ngoặc nhọn mở đầu. Không phải ngẫu nhiên mà cô được vinh dự này. Khi thực thi dòng 12, trình biên dịch ngôn ngữ C thực hiện khởi tạo ban đầu các thanh ghi MK, thiết lập ngăn xếp và phân bổ không gian địa chỉ. Không cần phải nghiên cứu cơ chế của quy trình chế tạo đồ trang sức này (không giống như các chương trình lắp ráp!).

Đối với một lập trình viên, điều quan trọng nhất là phải hiểu chính xác hai điều mà trình biên dịch có trong WinAVR tự động tạo ra:

Khi chương trình bắt đầu, tất cả các ngắt đều bị vô hiệu hóa;

Các đường dây của tất cả các cổng MK được cấu hình làm đầu vào không có điện trở “kéo lên”.

Dòng 13. Cuối cùng, dòng đầu tiên đã xuất hiện lệnh thực thi chương trình như một toán tử gán. Giải thích các ký hiệu:

“DDRB” là tên có điều kiện của thanh ghi DDR 8 bit của cổng “B”;

“=” - dấu hiệu ghi dữ liệu vào thanh ghi DDRB;

"Оь" - dấu hiệu cho thấy 8 chữ số tiếp theo sẽ nằm trong mã nhị phân;

“11111111” - bit được ghi vào thanh ghi DDRB Số nhị phân, sắp xếp theo thứ tự 7, 6, 5, 4, 3, 2, 1, 0 (bit quan trọng nhất ở bên trái, bit ít quan trọng nhất ở bên phải).

Kết quả của việc thực thi toán tử này là tất cả các dòng của cổng “B” được đặt ở chế độ đầu ra, vì tất cả các chữ số đều chứa số một.

Câu lệnh ở dòng 13 được thụt vào hai khoảng trống ở bên trái. Đây là quy ước định dạng văn bản được nhiều lập trình viên tuân thủ. Trình biên dịch sẽ giữ im lặng nếu bạn bắt đầu văn bản ở cột đầu tiên hoặc cột thứ bảy bên trái, tùy thích. Chỉ có một khuyến nghị: “Danh sách chương trình C phải dễ xem.” Theo đó, trong tương lai tất cả các văn bản sẽ được định dạng sao cho các dấu ngoặc nhọn nằm trong các cột lẻ theo chiều dọc (1, 3, 5, v.v.) và trong mỗi cột sẽ chỉ có một cột mở ở trên cùng và một cột đóng ở đáy.

Thứ tự này không phải là giáo điều mà là một cách để rút ngắn danh sách về chiều rộng và chiều dài mà không làm mất đi nội dung thông tin. Lập trình viên “ở nhà”, trong danh sách của mình, có quyền nhập bất kỳ số lượng khoảng trống nào, dòng trống vân vân.

Dòng /4 hoạt động cùng với dòng 13, vì trạng thái cụ thể của dòng cổng trong bộ điều khiển AVR được xác định bởi sự kết hợp của hai thanh ghi DDRx và PORTx, trong đó “x” là ký tự nối tiếp của cổng, ví dụ: B, C hoặc D. Xét rằng mỗi thanh ghi có 8 bit với các số từ 0 đến 7 (thông thường là “z”), thì bố cục chung dọc theo các đường cổng như sau:

DDRx.z = 1, PORTx.z = 1 - đầu ra từ Cấp độ cao;

DDRx.z = 1, PORTx.z = 0 - đầu ra ở mức THẤP;

DDRx.z = 0, PORTx.z = 1 - đầu vào có điện trở “kéo lên”;

DDRx.z = 0, PORTx.z = 0 - đầu vào không có điện trở “kéo lên”.

Tổng quát hóa dòng 13 và 14: DDRB.0…DDRB.7 = 1, PORTB.O = 0, PORTB.l = 1, PORTB.2…PORTB.7 = 0, do đó, các dòng 0, 2…7 của cổng “ B” sẽ được cấu hình làm đầu ra với mức THẤP và dòng 1 là đầu ra mức CAO. Do đèn báo HL1 được kết nối với đường PB1 (chân 15 của chip DD1 trong Hình 6.3) nên nó sẽ bị tắt. Hóa ra phần đầu điều khoản tham chiếu hoàn thành thành công.

Dòng 15, 16 tương tự như dòng 13, 14 nhưng dành cho cổng “C”. Để đa dạng, hằng số lNI được thay thế. Sau khi dòng 15 và 16 được thực thi, tất cả các dòng của cổng “C”, ngay cả những dòng không liên quan trực tiếp đến hoạt động, sẽ được cấu hình làm đầu vào với điện trở “kéo lên”. Đây là một kỹ thuật tiêu chuẩn để khởi tạo các cổng ban đầu nhằm kích hoạt các điện trở kéo lên bên trong, ngăn chặn đầu vào CMOS của đường MK “treo trong không khí” và loại bỏ các đường thâm nhập của tất cả các loại nhiễu và nhiễu.

Dòng 17 giống như dòng 13, 14 và 15, 16 nhưng dành cho cổng “D”. Anh ấy đang ở trong sơ đồ mạch điện hoàn toàn không liên quan, nhưng bạn nên đặt ra quy tắc khởi tạo tất cả các cổng mà không có ngoại lệ khi bắt đầu chương trình. Các đường dây nhàn rỗi của chúng phải được cấu hình làm đầu vào điện trở kéo lên hoặc đầu ra CAO/THẤP. Trong tương lai, việc tự động hóa như vậy sẽ giúp tránh được va chạm và hiểu lầm.

Một tính năng đặc biệt là thanh ghi PORTD được gán giá trị được gán trước đó ở dòng 13 cho thanh ghi DDRB, tức là. số nhị phân Obl 1111111. Việc này được thực hiện vì mục đích giáo dục vì lẽ ra nó có thể được thực hiện đơn giản hơn: “PORTD = OxFF;”.

Một chi tiết khác là sự vắng mặt của mục đăng ký DDRD. Đây không phải là lỗi đánh máy mà là sự cố tình giảm danh sách đi một dòng, vì khi bật nguồn, theo biểu dữ liệu, “số 0” sẽ tự động được nhập vào tất cả các thanh ghi DDRx và PORTx, tức là. Không cần thiết phải thiết lập lại thanh ghi DDRD.

Dòng 18 chứa câu lệnh vòng lặp "while". Đối với người mới làm quen lần đầu, chỉ cần nhớ rằng biểu thức “while (1)” có nghĩa là thực hiện tuần tự các câu lệnh ở dòng 19...21 trong một vòng lặp vô tận.

Dòng 19 chứa dấu ngoặc đơn mở và toán tử gán. Sự kết hợp này được cho phép bởi các quy tắc của ngôn ngữ C, làm cho danh sách có chiều cao nhỏ gọn hơn.

Sau khi dòng 19 được thực thi, biến “a” sẽ lưu byte trạng thái của 8 dòng của cổng “C”, được đọc từ thanh ghi PINC. Nếu không nhấn nút SB1 thì “a = OxFF”, còn nếu nhấn nút thì thì “a = OxFE”.

Dòng 20 dịch chuyển nội dung của biến “a” sang trái một vị trí. Có hai lựa chọn khả thi: nếu “a” trước đây là OxFF thì nó sẽ trở thành OxFE và nếu là OxFE thì nó sẽ trở thành OxFD. Tại sao điều này được thực hiện? hàng tiếp theo các chương trình.

Dòng 21 chứa câu lệnh gán, nhưng, so với dòng 19, biến "a" và thanh ghi cổng đã đổi chỗ cho nhau. Trong ngôn ngữ C, việc nhập kiểu như vậy dẫn đến việc thay thế thao tác đọc từ một cổng bằng thao tác ghi vào một cổng. Tổng cộng, mã 0xFE (nếu không nhấn nút SB1) hoặc mã 0xFD (nếu nhấn nút SB1) sẽ được xuất ra cổng “B”. Trong trường hợp đầu tiên, chỉ báo HL1 sẽ tắt, trong trường hợp thứ hai nó sẽ bật, đó là điều bắt buộc phải đạt được theo các thông số kỹ thuật.

Dòng 22, 23 chứa dấu ngoặc nhọn đóng. Nếu bạn rút ra được từ hai người họ một cách tinh thần đường thẳng đứng"từ dưới lên", khi đó chúng sẽ trỏ thẳng vào dấu ngoặc đơn mở ở dòng 19 và 12. Dấu ngoặc đơn ở dòng 22 biểu thị sự lặp lại của vòng lặp ở dòng 19...21. Dấu ngoặc đơn ở dòng 23 bắt đầu ở vị trí đầu tiên từ bên trái, do đó, đã đạt đến phần cuối của hàm “chính” và do đó là chương trình chính.

Dòng 24 chứa các nhận xét về số phiên bản WinAVR và độ dài của mã chương trình cơ sở, rất hữu ích khi người dùng khác biên dịch chương trình. Được biết, các phiên bản WinAVR không tương thích 100% với nhau và có những ví dụ rõ ràng về điều này. Do đó, độ dài của mã được biên dịch cho cùng một danh sách có thể khác nhau giữa các bản phát hành. Kết luận thực tế - trước tiên bạn phải biên dịch chương trình với gói WinAVR được chỉ ra ở dòng 24, sau đó chỉ với gói cũ hơn hoặc mới hơn. phiên bản mới, kiểm tra độ dài mã kết quả dưới dạng tổng kiểm tra.

Người đọc chú ý có quyền nhận thấy rằng ở giai đoạn biên soạn danh sách chương trình C, không thể tính toán trước dung lượng mà các mã chiếm trong bộ nhớ MK. Thành thật mà nói, dòng chữ “114 byte (2,8%)” đã được thêm vào sau khi biên dịch chương trình. Trên mặt ví dụ rõ ràng cùng một cái nhận xét, cái nào ở trong sơ đồ cấu trúc trong bộ lễ phục. 6.1 được biểu thị bằng một đường chấm giữa các khối “K” và “L”.

Dòng 25 hoàn toàn trống, nhưng không giống như dòng 5 và 9, nó đánh dấu sự kết thúc thực tế của danh sách. Nếu không có dòng cuối cùng này, trình biên dịch sẽ đưa ra cảnh báo nhẹ nhưng vẫn: “Cảnh báo: không có dòng mới ở cuối tệp”. dòng mớiở cuối tập tin").

Danh sách chương trình chính.

Var n1, n2: Kéo dài;

Chức năng Số lượng(x: Longint): Byte;

Var k: Byte;

Trong khi x<>0 làm

Bắt đầu

Kết thúc;

Số lượng:= k;

Bắt đầu

Writeln("Nhập hai số");

k1:= Số lượng(n1);

(số chữ số của số đầu tiên)

k2:= Số lượng(n2);

(số chữ số của số thứ hai)

Writeln("Cùng số chữ số")

Nếu k1 > k2 thì

Writeln("Số đầu tiên có nhiều chữ số hơn")

Writeln("Số thứ hai có nhiều chữ số hơn");

Bài giảng số 17. Cách sử dụng thói quen của người dùng một cách độc đáo. đệ quy

Một số thuật toán giải quyết vấn đề yêu cầu gọi một chương trình con từ phần câu lệnh của cùng một chương trình con đó.

Đệ quy là một cách tổ chức một quá trình tính toán trong đó một thủ tục hoặc hàm tham chiếu đến chính nó trong quá trình thực thi các toán tử cấu thành của nó. sử dụng đệ quy nên được giải quyết Đặc biệt chú ý để thoát khỏi chương trình con vào đúng thời điểm.Đệ quy rất hữu ích khi một nhiệm vụ cần được chia thành các nhiệm vụ con. Khi sử dụng thủ tục và hàm đệ quy cần đặt dòng ở đầu

nếu nhấn phím thì dừng lại; -để làm gián đoạn việc treo nếu nó xảy ra. Đã nhấn phím là hàm trả về kết quả ĐÚNG VẬY, nếu một phím được nhấn trên bàn phím và SAI- nếu không thì.

Ví dụ 1. Hãy xem xét một chương trình tính các phần tử của dãy Fibonacci bằng cách sử dụng đệ quy thủ tục.

thủ tục fibon(n,fn1,fn:integer);{thủ tục đệ quy}

nếu n > 0 thì

writeln(fn1+fn);

fibon(n-1,fn,fn1+fn);

var n,a,b: số nguyên;

write("nhập số phần tử của dãy Fibonacci: ");

write("...hai số sau: ");

Fibon(n,a,b);

Ví dụ 2. Chương trình hiển thị các chữ số của số nguyên dương theo thứ tự ngược lại.

chương trình rekurs2;

thủ tục đảo ngược(n:số nguyên);(thủ tục đệ quy)

Nếu nhấn phím thì dừng lại;

Viết(n mod 10);

nếu (n div 10)<>0 thì

Đảo ngược(n div 10);

writeln("số vvedi<= : ", maxint);

Đảo ngược(n);

Bài giảng số 18. Đầu vào – đầu ra của dữ liệu. Các tập tin

Tệp là một tập hợp dữ liệu được lưu trữ trong bộ nhớ ngoài của máy tính dưới một tên nhất định.

Bất kỳ tập tin nào cũng có ba tính năng đặc trưng:

1. Tệp có tên, cho phép chương trình hoạt động đồng thời với nhiều tệp.

2. Tệp chứa các thành phần cùng loại. Loại thành phần tệp có thể là bất kỳ loại nào.

3. Độ dài của tệp mới tạo không được chỉ định dưới bất kỳ hình thức nào khi nó được khai báo và chỉ bị giới hạn bởi dung lượng của các thiết bị bộ nhớ ngoài.

Để chương trình tìm thấy tệp cần thiết, bạn cần biết đường dẫn hoặc tuyến đường đến tệp.

Đường dẫn là danh sách các tên thư mục con, được phân tách với nhau bằng dấu gạch chéo ngược, theo sau là tên tệp thực.

Ví dụ:

c:\catalog1\catalog2\file1.txt.

Mỗi tên thư mục tương ứng với một mục trong thư mục con có tên đó. Dấu ".." tương ứng với một mục nhập vào siêu thư mục. Độ dài đường dẫn tối đa được phép là 79 ký tự.

Trong chương trình TP, tên tệp được chỉ định dưới dạng hằng văn bản được đặt trong dấu nháy đơn, có thể là giá trị của biến chuỗi:

"\turbo\pas\table.txt".

Thiết bị. Việc sử dụng các tệp trong TR là do nhu cầu trao đổi dữ liệu với môi trường máy tính, phần cứng của nó: màn hình, bàn phím, máy in, các kênh đầu vào-đầu ra. Tất cả chúng đều được coi trong TP là các tệp có thể được xử lý theo cách tương tự như với các tệp thông thường. Các tập tin trên thiết bị bên ngoài thường được gọi là tập tin vật lý hoặc bên ngoài . Các thiết bị được truy cập bằng các tên đặc biệt bị cấm sử dụng cho các tệp thông thường - cái gọi là tên các thiết bị logic máy tính

­ CON – bảng điều khiển . Sử dụng bảng điều khiển, thông tin đầu ra được gửi đến màn hình hiển thị và thông tin đầu vào được nhận từ bàn phím;

­ PRNđây là tên máy in . Nếu một số máy in được kết nối với máy tính thì chúng sẽ được truy cập bằng tên logic: LPT1, LPT2, LPT3.

­ COM1, COM2, COM3 –đây là những thiết bị để kết nối với cổng nối tiếp . Được sử dụng để giao tiếp với các máy tính khác và để kết nối chuột.

­ NUL - số không hoặc trống thiết bị. Thường được các lập trình viên sử dụng để gỡ lỗi chương trình. Cho phép bạn tránh tạo một tập tin riêng biệt. Khi sử dụng để xuất thông tin thì thông tin không được xuất ra ở bất cứ đâu nhưng được thông báo là xuất thành công.

Truy cập vào các tập tin. Bất cứ lúc nào, chương trình chỉ có thể truy cập được một phần tử tệp được tham chiếu bởi con trỏ vị trí tệp hiện tại. Nó xác định vị trí trong tệp từ đó dữ liệu được đọc hoặc ghi. Khi mở hoặc tạo một tập tin con trỏ được đặt ở đầu nó. Con trỏ hoạt động giống như một con trỏ, di chuyển trong khi chỉnh sửa văn bản, luôn hiển thị vị trí hiện tại. Khi đọc dữ liệu từ một tập tin, con trỏ cuối cùng sẽ đến cuối tập tin. Theo phương pháp truy cập các phần tử, họ phân biệt tập tin truy cập tuần tự hoặc trực tiếp.

// Công việc thí nghiệm số 6

// Nhiệm vụ cá nhân số 2

#include "stdafx.h"

#bao gồm

#include "conio.h"

#include "math.h"

#include "windows.h"

sử dụng không gian tên std;

int _tmain(int argc, _TCHAR* argv)

setlocale(LC_ALL, "Nga");

int nrow, ncol, i, j, summ;

cout<<"Лабораторная работа № 6\n";

cout<<"\nГорошко А.А., БАС-051\n";

cout<<"\nВариант № 6\n";

cout<<"\n\nИндивидуальное задание № 2:\n";

cout<<"\nСоставить программу с использованием двумерных динамических\n";

cout<<"\nмассивов для решения задачи индивидуального задания №1.\n";

cout<<"\n\nРабота программы:\n";

cout<<"\nВведите количество строк и столбцов:\n\n"<< "i = " ;

cin >> nrow;

cout<< "j = ";

cin >> ncol;

int **A = int mới *;

cout<<"\nВведите элементы массива: \n\n";

với (i = 0; tôi< nrow; i++)

A[i] = int mới;

với (j = 0; j< ncol; j++)

cout<< "A[" << i << "][" << j << "] = ";

cin >> A[i][j];

cout<<"\n1) ";

với (i = 0; tôi< nrow; i++)

cờ bool = sai;

với (j = 0; j< ncol; j++)

tổng hợp += A[i][j];

nếu(A[i][j]< 0)

nếu (cờ == đúng)

cout<<"Сумма элементов строки "<< i;

cout<<" с отрицательным элементом"<< " = " << summ << "\n\n";

cout<<"\n2) ";

với (i = 0; tôi< nrow; i++)

int tempValue; // biến tạm thời

tempValue = A[i];

// tìm kiếm phần tử nhỏ nhất trong dòng

với (j = 0; j< ncol; j++)

nếu(A[i][j]< tempValue)

tempValue = A[i][j];

// tìm kiếm giá trị lớn nhất trong cột của phần tử

tempValue = A;

vì(j = 0; j< nrow; j++)

if(A[j] > tempValue)

tempValue = A[j];

cout<<"Седловая точка: "<< "As[" << max << "][" << min << "]";

2.4. Kết quả của chương trình:

Phòng thí nghiệm số 7. Hàm và nạp chồng trong C++

Mục đích công việc và nội dung: củng cố kiến ​​thức về hàm và nạp chồng, soạn chương trình có hàm và nạp chồng.

Tiến triển

Thông tin cơ bản về hàm trong C++. Hàm là một nhóm các câu lệnh thực hiện một hành động hoàn chỉnh. Bạn có thể gọi một hàm theo tên, truyền giá trị cho nó và nhận kết quả từ nó.

Các hàm cần thiết để đơn giản hóa cấu trúc chương trình. Việc truyền các đối số khác nhau cho một hàm cho phép bạn viết hàm đó một lần và sử dụng nhiều lần cho các dữ liệu khác nhau. Để sử dụng một hàm, bạn không cần biết nó hoạt động như thế nào - chỉ cần biết cách gọi nó.

Để sử dụng một chức năng, bạn cũng chỉ cần biết giao diện của nó. Giao diện của một hàm được viết tốt được xác định bởi tiêu đề của nó, bởi vì nó chỉ định mọi thứ cần thiết để gọi nó: tên của hàm, loại kết quả mà nó trả về cũng như số lượng đối số và loại nó cần truyền.

Định dạng đơn giản nhất tiêu đề (nguyên mẫu)Đặc trưng:

tên loại ([danh sách tham số]);

Những gì có thể bỏ qua được viết trong dấu ngoặc vuông. Ví dụ: tiêu đề hàm chính thường trông như thế này:

Điều này có nghĩa là không có tham số nào được truyền cho hàm này từ bên ngoài và nó trả về một giá trị int duy nhất (mã thoát). Hàm có thể không trả về bất kỳ giá trị nào, trong trường hợp đó phải chỉ định loại void. Ví dụ: đây là tiêu đề của một hàm thư viện tiêu chuẩn tính toán sin của một góc:

tội lỗi kép(gấp đôi);

Ở đây viết rằng hàm này có tên sin, tính giá trị của sin loại double, và để làm được điều này bạn cần truyền cho nó một đối số loại double. Và đây là tiêu đề của hàm memcpy, sao chép một khối bộ nhớ dài n byte, bắt đầu từ địa chỉ src, tới địa chỉ đích:

void *memcpy (void *dest,const void *src,size t n);

Hàm này trả về một con trỏ có kiểu không xác định về đầu vùng nhớ đã được sao chép vào. Ý nghĩa chính xác của từng tham số hàm được mô tả trong tài liệu hàm. Tên của các tham số khi viết nguyên mẫu hàm chỉ mang ý nghĩa trang trí thuần túy, tức là chúng ta có thể cần chúng chứ không phải trình biên dịch nên có thể bỏ qua:

void *memcpy(void *, const void *. size t);

Một hàm được viết mù chữ, cùng với các đối số, cũng sử dụng các biến toàn cục có thể truy cập được từ bất kỳ khối nào của tệp hiện tại. Vì điều này không được phản ánh trong tiêu đề nên việc sử dụng chức năng này cũng đòi hỏi phải kiểm tra văn bản.

Mọi thứ được truyền đến và đi từ hàm phải được phản ánh trong tiêu đề của nó. Đây không phải là yêu cầu về cú pháp mà là yêu cầu về văn phong.

Tiêu đề chỉ định khai báo hàm. Định nghĩa của một hàm, ngoài tiêu đề, còn bao gồm phần thân của nó, tức là những câu lệnh được thực thi khi hàm được gọi, ví dụ:

int sum(int a,int b) /* hàm tìm tổng của hai giá trị */

( return a + b; // thân hàm

Văn bản chương trình có thể chứa một số lượng khai báo tùy ý của cùng một hàm và chỉ có một định nghĩa (trong trường hợp này, các hàm không khác với các đối tượng chương trình khác). Phần thân của hàm là một khối được bao bọc trong cặp dấu ngoặc nhọn. Để trả về kết quả được tính trong hàm, hãy sử dụng câu lệnh return. Sau đó, một biểu thức được chỉ định, kết quả của biểu thức này được tính toán và chuyển đến điểm gọi hàm. Kết quả, nếu cần, sẽ được chuyển đổi theo quy tắc chung thành loại được chỉ định trong tiêu đề. Một hàm có thể có nhiều câu lệnh trả về, điều này được xác định bởi thuật toán.

Để gọi một hàm, bạn cần chỉ định tên của nó và truyền cho nó một tập hợp các đối số tương ứng với các đối số được chỉ định trong tiêu đề của nó.

Lệnh gọi hàm trả về giá trị của một kiểu nhất định (nghĩa là không phải kiểu void) có thể được viết ở bất kỳ đâu mà cú pháp cho phép biểu thức - ở phía bên phải của toán tử gán, như một phần của biểu thức, trong chuỗi đầu ra, v.v. Dưới đây là ví dụ về cách gọi các hàm trên:

y gấp đôi, x1 = 0,34, x2 = 2;

cout<< y << ‘ ‘ << sin (x2) << endl;

y = sin(x1 + 0,5) - sin(x1 - 0,5);

char *cite = “Không bao giờ nói không bao giờ”;

memcpy(b, trích dẫn, strlen(trích dẫn) + 1);

int tổng, a = 2;

tổng = tổng(a, 4);

Trong định nghĩa, trong khai báo và khi gọi cùng một hàm thì kiểu và thứ tự của các tham số phải trùng nhau. Không cần phải khớp tên tham số.

Ví dụ7 .1. Truyền tham số của các loại tiêu chuẩn cho hàm

Viết chương trình hiển thị bảng các giá trị của hàm Ch x (cosine hyperbol) cho một đối số thay đổi trong giới hạn xác định với một bước nhất định. Các giá trị của hàm được tính bằng cách khai triển chuỗi Taylor với độ chính xác r.

Thuật toán của chương trình: đối với mỗi chuỗi giá trị đối số, giá trị của hàm được tính toán và sau đó hiển thị trên màn hình. Rõ ràng, sẽ hợp lý khi xây dựng phép tính tổng của chuỗi cho một giá trị đối số dưới dạng một hàm riêng biệt.

Việc phát triển bất kỳ chức năng nào cũng được thực hiện theo trình tự giống như việc phát triển toàn bộ chương trình. Đầu tiên, giao diện của hàm được xác định, nghĩa là giá trị nào được cung cấp cho nó làm đầu vào và kết quả sẽ là gì. Sau đó, các cấu trúc dữ liệu trong đó các giá trị trung gian này sẽ được lưu trữ sẽ được nghĩ ra; sau đó một thuật toán, chương trình và các ví dụ kiểm tra được biên soạn.

Hàm tính tổng của chuỗi của chúng ta cần lấy giá trị đối số và độ chính xác từ bên ngoài. Đặt các giá trị này cũng như kết quả thuộc loại double. Do đó, tiêu đề hàm có thể trông như thế này:

cosh gấp đôi (gấp đôi x, gấp đôi eps);

Để tính tổng của một chuỗi, bạn sẽ cần hai biến trung gian - để lưu phần tử tiếp theo của chuỗi và số của nó. Các biến này phải được khai báo bên trong hàm vì chúng không cần thiết ở bên ngoài hàm.

bao gồm

#bao gồm

cosh gấp đôi (gấp đôi x, gấp đôi eps); // nguyên mẫu hàm

gấp đôi Xn, Xk, dX, Eps;

printf(“ | X | Y |\n");

vì (gấp đôi x = Xn; x<= Xk; x += dX)

printf(“|%9.2]f |%14.6g |\n”\ x. cosh(x. eps));

cosh gấp đôi(double x. double eps)

const int Maxlter - 500; /*số lần lặp tối đa*/

đôi ch = 1. y = ch; /* thành viên đầu tiên của chuỗi và sự khởi đầu. giá trị số tiền */

cho (Int n = 0; fabs(ch) > eps; n++)

ch *- x * x /((2 * n + 1)*(2 * n + 2)); // thành viên chuỗi

y += ch; // thêm một thành viên của chuỗi vào tổng

nếu (n > Maxlter)

đặt(“ Hàng phân kỳ!\n");

Bằng cách sử dụng một hàm, chương trình trở nên rõ ràng và gọn gàng hơn vì nhiệm vụ được chia thành hai: tính toán hàm và in bảng. Ngoài ra, nếu cần, hàm chúng tôi đã viết có thể được chuyển mà không cần thay đổi sang chương trình khác hoặc được đặt trong thư viện.

Nếu một định nghĩa hàm được đặt sau lệnh gọi của nó thì một nguyên mẫu (tiêu đề) sẽ được đặt trước hàm mà nó được thực thi. Thông thường, tiêu đề của tất cả các hàm được sử dụng trong chương trình được đặt ở đầu tệp hoặc trong một tệp tiêu đề riêng. Tiêu đề này cần thiết để trình biên dịch có thể kiểm tra xem hàm có được gọi chính xác hay không. Tiêu chuẩn tập tin tiêu đề, mà chúng tôi kết nối với các chương trình, chứa các nguyên mẫu của các hàm thư viện cho mục đích chính xác này.

Trong chương trình I/O này, chúng tôi không sử dụng các lớp mà sử dụng các hàm kế thừa từ thư viện ngôn ngữ C, vì với sự trợ giúp của chúng, kết quả đầu ra được định dạng sẽ được viết gọn hơn. Đặc tả định dạng được sử dụng để xuất các số thực trên một phạm vi giá trị rộng. Số đầu tiên của công cụ sửa đổi (14) chỉ định, như đối với các thông số kỹ thuật khác, độ rộng của trường được phân bổ cho số và số thứ hai (6) chỉ định không phải độ chính xác, như ở định dạng f, mà là số chữ số có nghĩa. Trong trường hợp này, số được hiển thị ở định dạng f hoặc ở định dạng e (có thứ tự), tùy thuộc vào số nào ngắn hơn.

Khi viết hàm của chúng ta, vấn đề nảy sinh là làm thế nào để báo hiệu rằng chuỗi phân kỳ. Hãy xem xét các cách hiện có để giải quyết vấn đề nhận được từ một chương trình con một dấu hiệu về sự kết thúc bất thường của nó. Mỗi người trong số họ đều có ưu và nhược điểm.

Đầu tiên, bạn có thể thực hiện như trong chương trình trên: hiển thị tin nhắn văn bản, tạo một số giá trị cụ thể hàm (thường là 0) và thoát khỏi hàm. Nhược điểm của phương pháp này là nó in thông báo chẩn đoán bên trong hàm. Điều này là không mong muốn và đôi khi (ví dụ: khi hàm này là một phần của thư viện) thì điều đó hoàn toàn không thể chấp nhận được. Hãy thử đặt các giá trị đối số lớn và độ chính xác cao làm đầu vào. Bạn sẽ thấy rằng 500 lần lặp là không đủ để đạt được nó và bảng kết quả bị “làm hỏng” bởi thông báo rằng chuỗi phân kỳ.

Một giải pháp hiệu quả hơn là hình thành nó trong một hàm và chuyển nó ra bên ngoài bằng dấu hiệu hoàn thành thành công việc tính toán số tiền cần được phân tích trong chương trình gọi. Cách tiếp cận này thường được sử dụng trong các hàm tiêu chuẩn. Giá trị trả về không có trong tập giá trị hợp lệ sẽ được sử dụng làm dấu hiệu (ví dụ: một số âm khi tìm kiếm số phần tử trong một mảng hoặc số 0 cho một con trỏ) hoặc một tham số lỗi riêng biệt.

Thông thường, tham số lỗi là một giá trị nguyên, các giá trị khác 0 báo hiệu các lỗi khác nhau trong hàm. Nếu chỉ có một lỗi xảy ra thì tham số có thể được gán kiểu boo!. Tham số được chuyển đến chương trình gọi và phân tích cú pháp ở đó. Đối với vấn đề của chúng tôi, giải pháp này trông như thế này:

Danh sách 7.1

#bao gồm

#bao gồm

Cosh kép(double x, double eps, int &err);

gấp đôi Xn, Xk, dX, Eps, y;

printf("Nhập Xn, Xk, dX, eps \n");

scanf("%lf%lf%lf%lf.&Xn, &Xk, &dX, &eps);

printf(.............\n");

printf(“ | X | Y |\n");

printf(...........\n");

vì (gấp đôi x = Xn; x<= Xk; x += dX)

y = cosh(x, eps, err);

if (err) printf(“|%9.2]|Chuỗi phân kỳ!|\n”,);

printf(“|%9.2]f |%14.6g |\n”\ x, y);

printf("..................\n ”);

cosh kép(double x, double eps, int err)

const int Maxlter = 500;

đôi ch - 1. y - ch;

for (int n - 0: fabs(ch) > eps; n++) (ch *- x * x /((2 * n + l)*(2 * n + 2));

nếu (n > Maxlter);

for(double x=Xn; x<-Xk; x+-dX)

y"cosh(x.eps. lỗi);

if (err) prmtf("|X9.21f|Chuỗi phân kỳ!|\n và.x);

else printf(M H!9.21f |*14.6g |\n\ x. y);

cosh kép(double x. double eps.int &err)

const int Maxlter - 500;

đôi ch = 1. y = ch;

for (int n = 0: fabs(ch) > eps; n++)

ch *= x * x /((2 * n + l)*(2 * n + 2));

nếu (n > Maxlter),

Nhược điểm của phương pháp này là tăng số lượng tham số của hàm. Dấu & trước tham số trứng là dấu hiệu truyền tham số theo tham chiếu. Phương thức này cho phép các giá trị được truyền từ một hàm đến chương trình gọi.

Cơ chế truyền tham số cho hàm khá đơn giản. Khi chúng ta viết một biểu thức có dạng double x trong danh sách các tham số của hàm, điều này có nghĩa là giá trị của đối số tương ứng phải được truyền cho hàm khi nó được gọi. Để thực hiện việc này, một bản sao của nó được tạo trên ngăn xếp để chức năng này hoạt động. Đương nhiên, việc thay đổi bản sao này không thể có bất kỳ ảnh hưởng nào đến ô nhớ nơi tham số được lưu trữ. Nhân tiện, đây là lý do tại sao thay vì tham số như vậy, bạn cũng có thể chỉ định một biểu thức khi gọi, ví dụ:

y = cosh(x + 0,2.eps / 100. err);

Biểu thức được đánh giá và kết quả của nó được ghi vào ngăn xếp trong không gian được phân bổ cho tham số tương ứng.

Một liên kết, về mặt cú pháp là từ đồng nghĩa với tên của một đối tượng, đồng thời chứa địa chỉ của nó. Do đó, một tham chiếu, không giống như một con trỏ, không cần phải chuyển tiếp để lấy giá trị của đối tượng. Nếu chúng ta chuyển một tham chiếu đến một hàm, nghĩa là chúng ta viết một biểu thức có dạng double Seps trong danh sách các tham số và khi gọi, chúng ta thay thế một đối số vào vị trí của nó, ví dụ: eps Fact, do đó chúng ta chuyển địa chỉ của biến thực tế eps cho hàm. Địa chỉ này được xử lý theo cách tương tự như các tham số khác: một bản sao của nó được tạo trên ngăn xếp. Hàm làm việc với một bản sao của địa chỉ có quyền truy cập vào ô bộ nhớ trong đó giá trị của biến thực tế eps được lưu trữ và do đó có thể thay đổi giá trị đó.

Bạn cũng có thể truyền con trỏ tới một hàm; trong trường hợp này, bạn sẽ phải sử dụng các thao tác chuyển tiếp và lấy địa chỉ một cách rõ ràng. Đối với hàm của chúng ta, việc sử dụng con trỏ để truyền tham số thứ ba sẽ như sau:

// nguyên mẫu hàm;

cosh kép(double x, double eps, int * err);

- // gọi hàm;

y = cosh(x, eps, &еrr); // & - lấy địa chỉ

// gọi err bên trong hàm;

*err = 0; // * - mặc quần áo chết

Nguyên mẫu (và tất nhiên cả định nghĩa hàm) nêu rõ rằng tham số thứ ba sẽ là một con trỏ tới một số nguyên. Khi được gọi, địa chỉ của biến err sẽ được chuyển vào vị trí của nó. Để thay đổi giá trị của biến này bên trong hàm, thao tác lấy giá trị theo địa chỉ được sử dụng.

Vì thế, cho dữ liệu đầu vào Hàm sử dụng các tham số truyền theo giá trị, chuyển kết quả nó hoạt động bằng cách trả về một giá trị và/hoặc truyền tham số bằng tham chiếu hoặc con trỏ. Trên thực tế, việc truyền theo giá trị có một nhược điểm nghiêm trọng: việc đặt một bản sao của dữ liệu lớn lên ngăn xếp (ví dụ: các cấu trúc bao gồm nhiều trường) sẽ lãng phí cả thời gian và không gian sao chép. Ngoài ra, ngăn xếp có thể bị tràn. Do đó, một cách an toàn hơn, hiệu quả hơn và thông minh hơn là truyền dữ liệu đầu vào bằng tham chiếu liên tục để loại bỏ khả năng vô tình thay đổi một tham số trong hàm.

Đối với chương trình của chúng tôi, việc truyền dữ liệu đầu vào qua một liên kết không đổi trông như thế này:

// nguyên mẫu hàm;

cosh kép(const double &x. const double &eps. int &err);

- // gọi hàm;

y = cosh(x, eps, err); /* quyền truy cập vào x và eps trong hàm không thay đổi */

Do đó, dữ liệu đầu vào của hàm phải được truyền theo giá trị hoặc theo tham chiếu không đổi, kết quả hoạt động của nó - thông qua giá trị trả về và, nếu cần, truyền nhiều hơn một giá trị - thông qua các tham số bằng tham chiếu hoặc con trỏ.

Một cách khác để báo cáo lỗi trong hàm là viết hàm sao cho tham số lỗi được chuyển qua giá trị trả về. Điều này chủ yếu được sử dụng cho các chức năng đầu ra. Ví dụ: chức năng thư viện tiêu chuẩn

int fputcdnt ch. TẬP TIN *f);

ghi ký tự ch vào dòng f. Nếu có lỗi, nó trả về EOF, nếu không nó sẽ trả về ký tự đã viết. Trong trường hợp này, nếu cần chuyển bất kỳ kết quả nào khác của hàm tới điểm gọi *, chúng sẽ được chuyển qua danh sách các tham số.

Thông thường trong các hàm thư viện, trong trường hợp xảy ra lỗi, một giải pháp đơn giản hơn sẽ được sử dụng: trong trường hợp có lỗi, giá trị bằng 0 sẽ được trả về, mặc dù số 0 có thể được bao gồm trong tập hợp các giá trị kết quả hợp lệ. Trong trường hợp này, người lập trình không có cách nào để phân biệt giá trị sai với giá trị đúng. Ví dụ: các hàm mà bạn đã biết atoi, atol và atof được triển khai theo cách này. Nếu không thể chuyển đổi chuỗi thành một số có kiểu thích hợp, chúng sẽ trả về 0 và giá trị tương tự sẽ được trả về nếu chuỗi chứa ký tự 0.

Ném một ngoại lệ. Hãy sử dụng một công cụ C++ có tên các giá trị tham số mặc định. Có thể bất tiện khi chỉ định độ chính xác cần thiết để tính tổng của chuỗi mỗi lần bạn gọi hàm cosh. Tất nhiên, bạn có thể xác định độ chính xác như một hằng số bên trong hàm, đặt giá trị tối đa được phép, nhưng đôi khi điều này có thể là quá mức cần thiết, vì vậy bạn nên duy trì khả năng đặt độ chính xác thông qua các tham số. Để thực hiện việc này, trong định nghĩa (nếu nó cao hơn bất kỳ lệnh gọi hàm nào trong văn bản) hoặc trong nguyên mẫu hàm, giá trị mặc định của nó được biểu thị sau tên tham số, ví dụ:

cosh kép(double x. double eps - DBL EPSILON);

DBL EPSILON là hằng số được xác định trong tệp . Giá trị của nó bằng số tối thiểu, khi cộng với số 1 sẽ cho kết quả không bằng 1. Bây giờ hàm của chúng ta có thể được gọi với một tham số, ví dụ:

Một hàm có thể có nhiều tham số có giá trị mặc định. Chúng phải ở cuối danh sách các tham số.

Hàm nguyên mẫu sử dụng tham số lỗi và giá trị chính xác mặc định trông như thế này:

cosh kép(const double x.int & err. const double eps = DBL EPSILON);

Cuộc gọi hàm sẽ thay đổi tương ứng. Việc chỉ định từ khóa const trước tham số trong trường hợp này (khi truyền theo giá trị) chỉ được sử dụng để chỉ rõ tham số nào được nhập vào. Trong trường hợp truyền theo tham chiếu, việc chỉ định const cũng giúp có thể truyền một hằng số thay cho tham số này.

Chúng tôi đã định dạng phép tính tổng của chuỗi dưới dạng một hàm, nhưng nhiệm vụ hiển thị bảng giá trị của hàm bản thân nó khá điển hình và có thể gặp trong các tác vụ khác. Do đó, sẽ là hợp lý nếu chính thức hóa lời giải của nó dưới dạng hàm.

Ví dụ7 .2. Truyền tên hàm cho hàm

Hãy gọi hàm hiển thị bảng giá trị print table. Trước hết, bạn cần xác định giao diện của nó. Để hiển thị bảng, hàm của chúng ta sẽ cần biết phạm vi và bước thay đổi các giá trị đối số, cũng như hàm chúng ta thực sự sẽ tính toán. Hàm tính tổng của một chuỗi phải đạt độ chính xác, do đó độ chính xác phải được đưa vào danh sách các tham số của hàm printtable gọi nó. Hàm print nt tabl không trả về bất kỳ giá trị nào, nghĩa là void phải được chỉ định trước tên của nó.

Để truyền tên hàm cho hàm, bạn nên chỉ định loại của nó trong danh sách tham số trước tên tham số. Cho đến thời điểm này, chúng ta đã chuyển các giá trị của loại tiêu chuẩn cho hàm, nhưng bây giờ chúng ta sẽ cần xác định loại của riêng mình. Loại của hàm được xác định bởi loại giá trị trả về và loại tham số của hàm. Đối với chức năng của chúng tôi, nó trông như thế này:

gấp đôi (*vui vẻ)(gấp đôi, gấp đôi);

Điều này mô tả một con trỏ có tên fun tới một hàm nhận hai đối số kiểu double và trả về một giá trị cùng kiểu. Thông thường, nếu mô tả về một loại phức tạp, để cải thiện khả năng đọc của chương trình, một từ đồng nghĩa sẽ được cung cấp cho loại đó bằng từ khóa typedef:

typedef double (*Pfun)(double, double);

Câu lệnh này chỉ định loại Pfun, loại này có thể được sử dụng cùng với các loại tiêu chuẩn khi xác định các biến. Do đó, tiêu đề của hàm in bảng sẽ trông như sau:

void print_tabl(Pfun fun. double Xn, double Xk. double dX. double eps);

Bây giờ chúng ta viết văn bản chương trình, giảm thiểu chẩn đoán lỗi (nếu vượt quá số lần lặp tối đa cho phép, hàm sẽ kết thúc, trả về 0 và chương trình gọi hiển thị giá trị này):

Danh sách 7.2

#bao gồm

#bao gồm

typedef double (*Pfun)(const double, const double);

void print_tabl(Pfun fun, const double Xn, const double Xk, const double eps);

cosh kép(const double x, const double eps);

gấp đôi Xn, Xk, dX, eps;

prlntf(Nhập Xn, Xk, dX, eps \n");

scanf("%lf%lf%lf%lf.” &Xn, &Xk, &dX, &eps);

bảng in (cosh, Xn, Xk, dX, eps);

void print_tabl(Pfun fun, const double Xn, const double Xk, const double dX, const double eps)

pnintf(" ......................\n");

printf("| X | Y |\n");

printfC.................................\n");

vì (gấp đôi x = Xn; x< = Xk; x += dX);

printf("|%9.2lf | %14.6g | \n”\,x,fun(x, eps));

printf(" ...................\n);

cosh kép(const double x, const double eps)

const int Maxlter = 500;

đôi ch = 1, y = ch;

for (int n = 0; fabs(ch) > eps; n++)

ch *= x * x /(2 * n + l)/(2 * n * 2);

nếu (n> Maxlter) trả về 0;

Hàm print tabl được thiết kế để in một bảng giá trị của bất kỳ hàm nào nhận vào hai đối số thuộc loại double và trả về một giá trị cùng loại.

Do đó, cùng với tính tổng quát hơn, chúng tôi cũng đạt được cấu trúc chương trình tốt hơn, chia nó thành hai nhiệm vụ phụ không liên quan đến nhau về mặt logic: tính toán hàm và hiển thị bảng. Trong chương trình chính, tất cả những gì còn lại là nhập dữ liệu ban đầu và gọi hàm.

Ví dụ7 .3. Truyền mảng một chiều cho hàm

Cho hai mảng mỗi mảng có n số nguyên. Xác định xem cái nào có nhiều yếu tố tích cực hơn .

Để giải quyết vấn đề này, bạn sẽ cần đếm số phần tử dương trong hai mảng, nghĩa là thực hiện các hành động giống nhau cho cả hai mảng. Vì vậy, những hành động này phải được đặt trong một hàm. Giao diện hàm: dữ liệu đầu vào - một mảng và số phần tử của nó, kết quả - số phần tử dương trong mảng. Vì vậy, tiêu đề hàm sẽ trông như sau:

int n posit(const int *a, const int n);

Tên của mảng là một con trỏ tới phần tử 0 của nó, do đó mảng được truyền tới các hàm thông qua con trỏ. Số phần tử trong mảng phải được truyền dưới dạng tham số riêng biệt vì, không giống như các chuỗi ký tự sử dụng dấu kết thúc dòng, mảng nhìn chung không có dấu hiệu nào cho thấy sự kết thúc của mảng.

Danh sách 7.3

#bao gồm

int n posit(const int *a, const int n);

cout ""Nhập số phần tử:”:cin "n;

int *a = int mới[n];

int*b = int mới[n];

cout ""Nhập các phần tử của mảng đầu tiên: ";

với (i = 0; tôi< n; i++) cin » a[i];

cout ""Nhập các phần tử của mảng thứ hai: ";

với (i = 0; tôi< n; i++) cin » b[i];

if (n posit(a, n) > n posit(b, n))cout « "Trong phần đầu tiên có nhiều điểm tích cực hơn” « endl;

khác nếu(n thừa(a, n)< n_posit(b, n)) cout « " Во втором положительных больше" « endl;

int n posit(const int *a, const int n)

vì (int i = 0; tôi< n; i++)

if (a[i] > 0) count++;

Trong chương trình này, không gian cho mảng được phân bổ trong vùng bộ nhớ động, vì tác vụ không chỉ định số phần tử cụ thể. Tuy nhiên, hàm nposit có thể được sử dụng mà không cần thay đổi đối với các mảng “thông thường”, bởi vì đối với mỗi mảng, tên cũng là một con trỏ tới phần tử 0, chỉ là hằng số. Ví dụ: hãy mô tả một mảng gồm 10 phần tử và khởi tạo sáu phần tử đầu tiên trong số chúng (những phần tử còn lại sẽ được gán giá trị 0):

int x = (2, 3. -1, -10, 4, -2);

cout « n_posit(x. 10); // giá trị 3 sẽ được hiển thị

Hãy xem xét một cách để phân tích kết quả của một hàm. Hàm được gọi như một phần của biểu thức trong điều hành có điều kiện. Để lặp qua cả ba biến thể của kết quả, bạn phải gọi nó hai lần cho mỗi mảng, tất nhiên, điều này là không hợp lý đối với các mảng lớn. Để tránh các lệnh gọi lặp lại, bạn có thể tạo hai biến lưu trữ kết quả xử lý cả hai mảng, sau đó sử dụng các biến này trong câu lệnh có điều kiện:

int n posit a = n posit(a. n), n posit b = n posit(b, n);

if (n posit a > n posit b) cout « "Có nhiều mặt tích cực hơn ở cái đầu tiên" « endl;

ngược lại nếu (n đặt a< n posit b) cout « " Во втором положительных больше" « endl;

else cout " " Cùng một lượng " " endl;

Các trình biên dịch hiện đại có khả năng tối ưu hóa sâu rộng và tự giám sát tình huống tương tự, chuyển đổi mã chương trình, nhưng điều này không có nghĩa là bạn không nên chú ý đến hiệu quả của chương trình của mình. Tuy nhiên, tiêu chí chính khi chọn phương án viết chương trình vẫn là tính đơn giản về cấu trúc và khả năng đọc của nó.

Ví dụ7 .4. Truyền chuỗi cho hàm

Viết chương trình xác định mỗi dòng của một file văn bản có bao nhiêu số. Độ dài mỗi dòng không vượt quá 100 ký tự.

Nhiệm vụ này có thể được chia thành hai: nhập dữ liệu từ một tệp và phân tích nó. Đối với mỗi dòng, việc kiểm tra được thực hiện riêng biệt, do đó, việc chỉ định việc tìm kiếm và đếm số lượng trong một dòng là một hàm là hợp lý. Chúng ta sẽ cung cấp một chuỗi làm đầu vào cho hàm và ở đầu ra chúng ta sẽ nhận được số lượng các số trong chuỗi này.

Sự khác biệt giữa việc truyền một chuỗi cho một hàm và truyền một mảng thông thường là bạn không thể truyền thứ nguyên của chuỗi dưới dạng một tham số riêng biệt mà thay vào đó xác định phần cuối của chuỗi bên trong hàm bằng ký tự null. Để đơn giản, chúng ta giả sử rằng các số có thể là số nguyên hoặc số thực có điểm cố định và không trống. phần phân đoạn. Việc mở rộng chương trình sang các loại số khác được cung cấp cho bạn dưới dạng bài tập độc lập.

Danh sách 7.4

#bao gồm

#bao gồm

int num num(const char *str);

ifstream fth("test.txt".ios::in|ios::nocreate);

cout ""Không có tệp test.txt" "endl;

const int len=101;

while (fin.getline(str. len))

cout " "Dòng " " " 1 " " chứa " " " num num(str) " " số " " endl;

int num num(const char *str)

if (isdigit(*str) && ! isdigit(*(str + 1))) && *(str + 1) != ".") count++;

Bộ đếm số trong hàm được tăng lên mỗi khi số kết thúc, nghĩa là nếu số đó không có số hoặc dấu chấm theo sau. Vòng lặp kết thúc khi đạt đến ký tự null.

Ví dụ7 .5. Truyền mảng hai chiều cho hàm

Viết chương trình xác định hàng nào của ma trận nguyên m x n chứa chuỗi phần tử giống nhau dài nhất.

Theo chuỗi, chúng tôi muốn nói đến các phần tử được sắp xếp thành một hàng. Thật thuận tiện khi xây dựng lời giải cho bài toán chính dưới dạng hàm, chỉ để lại dữ liệu đầu vào và đầu ra kết quả cho chương trình chính. Để dễ dàng gỡ lỗi, mảng được nhập vào chương trình từ một tệp văn bản. Dòng đầu tiên của file chứa các giá trị cho loại, mỗi dòng tiếp theo là tập hợp các số cho một hàng của ma trận. Bộ nhớ cho mảng được phân bổ trong một vòng lặp sao cho cả hai chiều của mảng có thể được chỉ định dưới dạng biến.

Danh sách 7.5

#bao gồm

int ser bằng(int **a, const int m, const int n);

if luồng vây ("matrix.txt", los :: TRONG | ios::nocreate);

nếu (! Vây)

cout ""Không có tập tin Matrix.txt" "endl;

int m, n, 1, j;

int **a = int mới *[m]; // cấp phát bộ nhớ

với (d = 0;i< m; i++)

a[i] = int mới [n];

( tôi = 0; tôi< m; i++) // ввод нассива

với (j = 0 ; j< n) fin » a[i][j];

int line = ser_equals(a, m, n); // gọi hàm

if (line >= 0)cout " " Chuỗi dài nhất trong dòng " " line;

else cout ""Không có chuỗi phần tử giống hệt nhau";

int ser_equals ( int **a, const int m, const int n)

int I,j, đếm, dòng = -1, số lượng tối đa = 0;

cho (1 - 0; 1< m; 1++)

với (j = 0; j< n - 1; j++)

nếu (a[i][j] == a[l])

nếu (đếm > số tối đa)

số lượng tối đa = số lượng;

nếu (đếm > số tối đa)

số lượng tối đa = số lượng;

Thuật toán của hàm rất đơn giản: trong mỗi dòng, việc so sánh các phần tử lân cận được thực hiện (toán tử 2). Nếu chúng bằng nhau thì chúng ta ở trong chuỗi, đồng thời tăng độ dài hiện tại của nó. Nó được tích lũy trong biến đếm, được đặt lại về 0 trước khi xử lý mỗi hàng (toán tử 1). Nếu các phần tử không bằng nhau, điều này có nghĩa là phần cuối của chuỗi hoặc chỉ là một phần tử duy nhất (toán tử 3). Trong trường hợp này, bạn cần xem liệu chuỗi này có dài nhất trong số những chuỗi được xem xét hay không và nếu có thì hãy nhớ độ dài của nó và số dòng mà nó xuất hiện (toán tử 4). Để chuẩn bị cho việc phân tích dãy tiếp theo, bộ đếm phải được đặt lại về 0 trên cùng một dòng. Một kiểm tra tương tự sau vòng lặp dòng (câu lệnh 5) được thực hiện cho chuỗi nằm ở cuối dòng, vì trong trường hợp này nhánh else sẽ không được thực thi.

Nếu không có chuỗi phần tử giống hệt nhau trong mảng, hàm sẽ trả về giá trị bằng -1.

Ví dụ7 .6. Truyền cấu trúc cho hàm

Viết chương trình bổ sung tệp nhị phân được tạo trong nhiệm vụ 6.3 với thông tin về nhân viên được nhập từ bàn phím.

Nhiệm vụ này có thể được chia thành hai phần: nhập thông tin về nhân viên vào cấu trúc và thêm thông tin này vào tệp nhị phân, vì vậy chương trình của chúng ta sẽ có hai chức năng. Hàm đầu tiên trả về cấu trúc được tạo mà không nhận bất cứ thứ gì từ bên ngoài. Cái thứ hai nhận cấu trúc và tên tệp và trả về dấu hiệu cho thấy việc bổ sung đã thành công.

Để kiểm tra tính chính xác của việc nhập dữ liệu vào tập tin nhị phân Hãy viết một hàm khác sẽ hiển thị nó trên màn hình dựa trên số bản ghi đã nhập.

Danh sách 7.6

#bao gồm

#bao gồm

#bao gồm //#bao gồm

const int l tên = 30;

tên nhân vật;

Người đàn ông đọc dữ liệu();

int add2binfilet (const Man &man, const char* tên tệp);

in int từ bin(const char * tên tệp);

tên tệp char = "dbase.bin";

if (append2binfile(đọc dữ liệu(), tên tệp) != 0)

put(" Lỗi ghi vào tập tin ");

đặt("Tiếp tục (y/n)?");

if ((y n == "y")||(y n == "Y"))contin = true;

trong khi(tiếp tục); in từ bin(fi1ename);

intappend2binfile(const Man &man, const char* tên tệp)

if ((fout-fopen(filename, "ab"))NULL) return 1;

int thành công = fwrite(&man, sizeof(man), 1, fout), fclose(fout);

if (thành công == 1) trả về 0;

int in from_bin(const char * tên tệp)

int số; đàn ông đàn ông; TẬP TIN *f;

if ((f = fopenCfilename, "rb")) == NULL) trả về 1;

fsef((f. 0. TÌM KIẾM);

int n record - ftell(f) / sizeof(man);

đặt("Nhập số bản ghi hoặc -1; ");

scanf("&i", &num);

nếu (số< 0 || num >= n bản ghi) phá vỡ ;

fseek(f. num * sizeof(man). SEEK SET) ;

fread(&man, sizeof(man), 1, f);

// CharToOem(người đàn ông,tên, người đàn ông,tên);

printf("%30s%5i%10,2f\n, man.name, man.năm sinh. man.pay);

tên nhân vật;

đặt("Nhập họ của I.O. ");

nếu (strlen(tên)< 1_name)

for (int i = strlen(name); i< l name; i++)

tên = 0;

// OemToChar(name.name);

strncpy(man.name.name.l_name + 1);

đặt("Nhập năm sinh “);

while ((man.birth_year = atoi(buf)) ==0);

đặt(“Nhập mức lương ");

while (!(man.pay = atof(buf)));

Hàm nhập dữ liệu đọc cung cấp khoảng trắng để điền vào phần còn lại của biến chuỗi pache sao cho định dạng tên giống hệt với định dạng đầu vào trong tệp văn bản.

Một điều cần chú ý là cách chức năng này kiểm tra tính chính xác của đầu vào. thông tin số. Việc đọc được thực hiện thành một chuỗi đệm, sau đó được chuyển đổi bằng cách sử dụng hàm atoi() và atof() thành số. Nếu các hàm trả về 0 thì việc chuyển đổi không thành công (ví dụ: các chữ cái được nhập thay vì số) và thông tin sẽ được yêu cầu lại. Điều kiện để lặp lại chu kỳ 3 và 4 được viết thành hai phiên bản khác nhau để bạn có thể tự đánh giá xem phiên bản nào dễ hiểu hơn đối với bạn (các chuyên gia sẽ thích phiên bản thứ hai, ngắn gọn hơn).

Một cấu trúc, không giống như mảng, có thể là giá trị trả về của hàm. Trong chương trình này, một cấu trúc được truyền cho hàm thông qua một tham chiếu không đổi; bạn cũng có thể chuyển nó theo giá trị, điều này tệ hơn một chút, vì trong trường hợp này, thời gian được dành cho việc sao chép và cần thêm không gian trên ngăn xếp tham số.

Ví dụ7 .7. Hàm đệ quy

Viết chương trình sắp xếp thứ tự mảng bằng phương thức sắp xếp nhanh chóng, sử dụng đệ quy.

Một hàm trong đó có lệnh gọi chính nó được gọi là đệ quy. Bất kỳ hàm nào trong chương trình C++ đều có thể được gọi đệ quy. Điều này phân bổ một vùng bộ nhớ mới trên ngăn xếp để chứa các bản sao của tham số, cũng như các biến tự động và biến đăng ký, để trạng thái trước đó của hàm đang được thực thi được giữ nguyên.

Một phiên bản có thể có của chương trình sắp xếp được hiển thị bên dưới.

Danh sách 7.7

bao gồm

void qsort(float* mảng, intleft, int phải);

const int n = 10;

cout ""nhập phần tử mảng:";

với (1 = 0; tôi< n; i++) cin » arr[i];

i = o;r = n - 1; /* viền trái và phải của đoạn đầu tiên */

qsort(arr, 1, r); // 1

với (i = 0; tôi< n; i++) cout « arr[i] « ‘ ‘;

void qsort(mảng float*, int left, int right)

int i = trái, j = phải;

float giữa = mảng[(trái + phải) / 2];

trong khi tôi< j)

trong khi (mảng[i]< middle) i++;

trong khi (giữa< array[j]) j--;

tạm thời = mảng [i];

mảng[i] = mảng[j];

mảng[j] = tạm thời;

nếu (trái< j) qsort(array, left, j);

nếu tôi< right) qsort(array, I, right);

Quy trình phân tách được triển khai ở đây dưới dạng hàm được gọi đệ quy qsort(), phần thân của hàm này chứa hai lệnh gọi đến chính nó: trong toán tử 2 - để sắp xếp nửa bên trái của đoạn hiện tại và trong toán tử 3 - để sắp xếp nửa bên trái của đoạn hiện tại. nửa bên phải.

Tuy nhiên, đệ quy cũng có nhược điểm: thứ nhất, chương trình như vậy khó gỡ lỗi hơn, vì cần phải kiểm soát độ sâu của lệnh gọi đệ quy, thứ hai, với độ sâu lớn, ngăn xếp có thể bị tràn và thứ ba, việc sử dụng đệ quy tăng lên chi phí chung (ví dụ: trong trường hợp này, không phải hai số được lưu trữ trên ngăn xếp đại diện cho ranh giới của đoạn, mà còn hơn thế nữa, chưa kể chi phí liên quan đến việc gọi hàm).

Ví dụ 7.8.Dự án nhiều tệp - định dạng văn bản

Viết chương trình định dạng văn bản đọc từ tệp unformt.txt và bao gồm các dòng có độ dài giới hạn. Các từ trong một dòng cách nhau một số khoảng trắng tùy ý. Chương trình phải đọc từng dòng tệp đầu vào, định dạng từng dòng và xuất kết quả ra tệp đầu ra có định dạng-.txt. Định dạng bao gồm việc căn chỉnh các đường viền của văn bản ở bên trái và bên phải bằng cách phân bổ đều khoảng cách giữa các từ liền kề, cũng như thụt lề bên trái của trang theo các vị trí lề, nghĩa là văn bản thu được phải ở vị trí lề + 1 .. lề + maxljine. Ngoài ra chương trình còn phải đếm tổng số từ trong văn bản .

Thuật toán để giải quyết vấn đề:

    Đối với mỗi dòng 11, hãy làm như sau:

Tính toán lượng khoảng cách (số lượng khoảng trắng) phải được cung cấp giữa các từ liền kề để phân bổ đều các từ trong một dòng.

Xuất từng từ từ dòng 1 i n ra file đầu ra, chèn số khoảng trắng cần thiết giữa các từ, đồng thời tăng bộ đếm từ lên một.

4. Sau khi xử lý dòng cuối cùng tập tin đầu vào, hiển thị giá trị của bộ đếm từ và đóng tập tin đầu ra.

Chia nhỏ thành các nhiệm vụ phụ.

Do chi tiết hóa thuật toán được mô tả, chúng tôi xác định các thông số kỹ thuật của các hàm chúng tôi cần:

J void Deflnter (const char* pline, int & base int. int & add int. int & inter)

xác định cho dòng được trỏ đến bởi pline số lượng khoảng trắng giữa các từ, giá trị bắt buộc của khoảng chính cơ sở int cho mỗi khoảng (số lượng khoảng trắng) và giá trị của khoảng bổ sung cộng int, được định nghĩa là phần còn lại của phép chia tổng số khoảng trắng trong dòng bằng số khoảng trắng giữa các từ; giá trị cuối cùng phải được phân bổ đều bằng cách thêm một khoảng trắng vào mỗi khoảng trắng được thêm vào đầu tiên;

void GetLine(FILE* finp. char* pline)

đọc dòng tiếp theo từ tệp đầu vào thành một mảng các ký tự có địa chỉ pi ine, loại bỏ khoảng trắng ở đầu dòng;

void Putlninterval (FILE* fout. const int k)

hiển thị khoảng tiếp theo bao gồm k dấu cách;

int PutWord (FILE*fout,const char*pline.const int startpos)

xuất từ ​​tiếp theo vào tệp đầu ra, bắt đầu từ vị trí bắt đầu của dòng pi ine hiện tại; trả về số vị trí trong dòng pi i ne theo sau ký tự cuối cùng được truyền hoặc 0 nếu đến cuối dòng;

int SearchNextWord"(const char*pline.const int curpos)

trả về số vị trí mà từ tiếp theo trong chuỗi bắt đầu pi 1 n hoặc 0 nếu đến cuối chuỗi (việc tìm kiếm bắt đầu ở vị trí giới hạn).

Mô-đun hóa.

Chương trình của chúng tôi sẽ nằm trong hai tệp nguồn: task7_7.cpp - với chức năng chỉnh sửa chính. cpp - với việc triển khai các chức năng trên, cũng như tệp tiêu đề edit.h với giao diện của các chức năng này. Dưới đây là nội dung của các tập tin này.

Danh sách 7 .8

// Tệp Task7_7.cpp

#bao gồm

#bao gồm

#bao gồm

#include "edit.h"

// Biến toàn cục

const int maxljine = 63;

const int lề = 5;

dòng char;

int b i, a i, start, next, inter;

printf("Chương trình Task7 7 đang chạy.\n");

if(!(finp = fopen("unformt.txt","W")))

printf("Không tìm thấy file unformt.txt.\n");

printf("Tập tin unformt.txt đang được đọc \n");

if(!(fout = fopen(“formatd.txt", "w")))

printf("Tệp format.txt chưa được tạo.\n");

printf("Ghi vào file format.txt.\n");

while(GetLine(finp. line))

DefInter(line, b i, a_i, inter);

PutInterval(fout, lề);

next = PutWord(fout, line, start, nword);

vì (int i = 0; tôi< inter; i++)

start = SearchNextWord(line. next);

Putlninterval(fout.. b i);

if (a j) ( a i--; Putlninterval(fout. 1);

next = PutWord(fout. line, start, nword);

if (!next) phá vỡ;

fprintf(fout. “\n");

printf (“\nSố từ - %d\n”, nword);

printf("Công việc đã hoàn thành\n");

///////////////////////////////////////////////////

// Chỉnh sửa tệp.h

// Nguyên mẫu hàm

void DefInter(const char* pline, Int& base int, int& add int,

int GetLine(FILE*. char*);

void Putlninterval(FILE*. const int);

int PutWord(FILE*. const char*, const int. int&);

int SearchNextWord(const char*, const int);

// Biến toàn cục

dòng extern const int maxl;

///////////////////////////////////////////////////

// File Edit.cpp

#bao gồm

#bao gồm

#include "edlt.h"

int GetLine(FILE* finp. char* pline)

while ((c = fgetc(finp)) == " ") i++;

nếu(c == EOF) trả về 0;

fseek(finp. -1, TÌM KIẾM HIỆN TẠI);

fgets(pline. Dòng Maxl -i + 1 , vây);

đường thẳng = 0;

int SearchNextWord(const char* pline, const int curpos)

while(pline[i] !="")

if (pline[i] ==;\n”) trả về 0:

while (pline[i] == ’ ‘&& pline == " ") i++;

void DefInter(const char* pline. int& base int. int& add int. int& inter)

int k = 0, kết thúc;

end = strlen(pline) - 1;

while ((pline == " ") || (pline == '\n") || (pline == "\r")) end--;

for (unsigned int i= 0; i< end; i++)

nếu (pline[i] == " ")

nếu (pline != " ") liên++;

số tiền trống int * đến + dòng maxl - kết thúc;

base int = số tiền trống/inter;

addjnt = số tiền trống % liên;

int PutWord (FILE* fout, const char* pline, const int startpos, int& n)

int i = startpos;

trong khi ((c - pline) !=" ')

fprintf(fout, "%c", c);

nếu ((c = "\n"> || (c == "\0"))

void Putlninterval(FILE* fout, const int k)

vì (int i=0; tôi

///////////////////////////////////////////////////

Chúng tôi đã viết tên hàm ở đây (để đa dạng) theo phong cách Microsoft, sử dụng chữ in hoa để làm nổi bật những phần có ý nghĩa của tên. Hằng số maxl 1 phải được đặt lớn hơn độ dài dòng tối đa của tệp nguồn. Như một bài tập cho riêng bạn, hãy sửa đổi chương trình để bạn có thể định dạng văn bản thành một cột hẹp hơn trong tệp nguồn.

Quá tải chức năng. Nạp chồng hàm là việc sử dụng nhiều hàm có cùng tên nhưng có danh sách tham số khác nhau. Các hàm quá tải phải khác nhau về loại của ít nhất một tham số hoặc về số lượng tham số hoặc cả hai. Quá tải là một loại đa hình và được sử dụng trong trường hợp cùng một hành động được triển khai khác nhau cho các loại hoặc cấu trúc dữ liệu khác nhau. Bản thân trình biên dịch sẽ xác định phiên bản nào của hàm sẽ được gọi, được hướng dẫn bởi danh sách các đối số.

Nếu thuật toán không phụ thuộc vào kiểu dữ liệu, tốt hơn là nên triển khai nó không phải dưới dạng một nhóm các hàm quá tải cho nhiều loại khác nhau mà dưới dạng một mẫu hàm. Trong trường hợp này, chính trình biên dịch sẽ tạo văn bản hàm cho các kiểu dữ liệu cụ thể mà lệnh gọi được thực hiện và lập trình viên sẽ không phải hỗ trợ một số hàm gần như giống hệt nhau.

Các chức năng quá tải nhỏ thuận tiện sử dụng khi gỡ lỗi chương trình. Giả sử bạn cần in trung gian nhiều loại khác nhau: ở một nơi bạn muốn hiển thị cấu trúc trên màn hình, ở một nơi khác - một vài giá trị số nguyên với phần giải thích hoặc một mảng thực. Việc sắp xếp ngay bản in dưới dạng hàm sẽ dễ dàng hơn, ví dụ như sau:

void print(char* str. const int i. const int j)

cout " str " " I" " oct " setw(4) " i " " |" « setw(4) « j « " |’ « endl;

in void(float mas, const int n)

cout ""Mảng:" "endl;

cout.setf(ios::fixed);

cout.precision(2);

vì (int i = 0; tôi< n; i++)

cout « mas[i] « " ";

nếu ((i + 1) % 4 == 0) cout « endl;

in void(Man m)

cout.setf(i os::fixed);

cout.precision(2);

cout « setw(40) « m.name « " ‘ « năm sinh <<" ‘<< m.pay «endl;

Hàm đầu tiên hiển thị một chuỗi và hai số nguyên ở dạng bát phân, được phân tách bằng các thanh dọc để dễ đọc. Mỗi số được phân bổ 4 vị trí (hành động của bộ điều khiển setw chỉ áp dụng cho trường đầu ra gần nhất).

Trong hàm thứ hai, để xuất ra các giá trị thực của bốn số trên mỗi dòng, loại đầu ra được đặt thành một điểm cố định và độ chính xác là hai chữ số thập phân. Để thực hiện việc này, hãy sử dụng các phương thức đặt cờ setf, đặt độ chính xác và hằng số f1xed, được xác định trong lớp 1os. Độ chính xác chỉ áp dụng cho số thực và tiếp tục cho đến lần cài đặt tiếp theo.

Chức năng thứ ba hiển thị các trường của cấu trúc quen thuộc với chúng ta từ buổi hội thảo thứ sáu để chúng không dính vào nhau. Trình thao tác setw đặt độ rộng của trường tiếp theo. Điều này sẽ khiến họ xuất hiện thụt vào từ mép màn hình. Việc gọi các hàm này trong một chương trình có thể trông như thế này, ví dụ:

print("Sau vòng lặp", 1, n);

Tên của hàm sẽ làm rõ ngay chức năng của nó; ngoài ra, nếu cần, một lệnh gọi hàm sẽ dễ dàng được nhận xét hoặc di chuyển đến nơi khác hơn là một nhóm các câu lệnh in. Tất nhiên, in trung gian không phải là phương pháp gỡ lỗi duy nhất, nhưng nó là phương pháp phổ biến vì không phải lúc nào cũng có sẵn trình gỡ lỗi.

Khi viết các hàm quá tải, cần chú ý chính để đảm bảo rằng không có sự mơ hồ trong quá trình tìm kiếm phiên bản mong muốn của hàm bằng cách gọi nó.

Sự mơ hồ có thể phát sinh vì nhiều lý do. Thứ nhất, do các chuyển đổi kiểu mà trình biên dịch thực hiện theo mặc định. Ý nghĩa của quy tắc chuyển đổi kiểu số học là các kiểu ngắn hơn được chuyển đổi thành kiểu dài hơn. Nếu có thể đạt được sự trùng khớp giữa các tham số hình thức và đối số hàm ở cùng một bước theo nhiều cách, thì lệnh gọi được coi là không rõ ràng và thông báo lỗi sẽ được đưa ra.

Sự mơ hồ cũng có thể phát sinh do các tham số và tham chiếu mặc định. Hãy xem xét việc tạo các hàm quá tải bằng một ví dụ.

Ví dụ7 .9. Quá tải chức năng

Viết chương trình tạo cơ sở dữ liệu về nhân viên theo yêu cầu, tạo ra danh sách nhân viên sinh sớm hơn một năm nhất định hoặc có mức lương lớn hơn mức lương được nhập từ bàn phím.

Chúng tôi sẽ xây dựng các tùy chọn để chọn từ cơ sở dữ liệu cho các truy vấn khác nhau dưới dạng hàm quá tải. Vì sự lười biếng tự nhiên và để đơn giản, chúng tôi đang xem xét một cơ sở dữ liệu có số lượng trường tối thiểu; trong các tình huống thực tế có thể có nhiều hơn, theo đó, sẽ có nhiều lựa chọn hơn cho các hàm bị quá tải. Chúng tôi cũng sẽ xây dựng công thức đọc cơ sở dữ liệu từ một tệp dưới dạng một hàm riêng biệt - vừa để cấu trúc chương trình tốt hơn vừa giúp dễ dàng hơn, nếu cần, thay thế hàm này bằng một hàm khác, chẳng hạn như đọc từ tệp nhị phân.

Danh sách 7.9

#bao gồm

#bao gồm

#bao gồm

#bao gồm

const int l tên = 30, l năm = 5, l trả = 10, l buf = l tên + 1 năm + l trả;

tên nhân vật;

int read dbase(const char * tên tệp, Man dbase, const int l dbase, int &n record);

void printtMan m);

void select(Man dbase. const int n record. const int Year);

void select(Man dbase. const int n ecord. const float pay);

const int l dbase = 100;

Người đàn ông dbase;

int n bản ghi = 0;

if(đọc dbase(“txt6.txt”.dbase.1 dbase.n record) !=0) return 1;

cout<<”---------------------“<< endl;

cout<< ”1 - Cведения по году рождения“ << endl;

cout<<”2 - Сведения по окладу“<

cout<<”3 - выход“<< endl;

cin >> tùy chọn;

Trường hợp 1:<< ”Введите год” >> cin >> năm;

chọn(dbase.n_record.year); phá vỡ;

Trường hợp 2:<< ”Введите оклад” >> cin >> trả tiền;

chọn(dbase.n_record.pay);

Trường hợp 3: trả về 0;

mặc định: cout<< ”Надо вводить число от 1 до 3” << endl;

Chọn trống (man dbase, const int n record, const int năm)

cout<< ”Ввод сведений по году рождения” << endl;

bool thành công = sai;

for(int i = 0; i< n record; i++)

if(dbase[i],năm sinh >= năm)

In(dbase[i]);

if (! thành công) Cout<<”Таких сотрудников нет”<

Chọn vô hiệu (man dbase, const int n_record, const float pay)

cout<< ”Ввод сведений по окладу” << endl;

bool thành công = sai;

for(int i = 0; i< n record; i++)

if(dbase[i],tiền sinh >= trả)

print(dbase[i]);

if (!success) cout<<”Таких сотрудников нет”<

In vô hiệu (Man m)

cout.setf(iod::fixed);

cout.precision(2);

cout « setw(40) « m.name « " " « m.birth _năm " " " " m.pay «endl;

int đọc dbase(const char * tên tệp. Man dbase. const int l dbase. int &n record) (

char buf ;

if luồng fin(filename. ios::in | ios:mocreate);

cout ""Không có tập tin" "tên tập tin" endl;

while "(fin.getline(buf, 1 buf))

strncpy(dbase[i]name,buf, l name);

dbase[i].name = "\0";

dbase[i].năm sinh = atoi(&buf);

dbase[i].pay = atof(&buf);

nếu (i > l dbase)

cout ""Tập tin quá dài";

Quy tắc mô tả hàm nạp chồng:

1. Các hàm quá tải phải nằm trong cùng một phạm vi, nếu không việc ẩn sẽ xảy ra giống như các tên biến giống hệt nhau trong các khối lồng nhau.

2. Các hàm quá tải có thể có các tham số mặc định và các giá trị của cùng một tham số ở các hàm khác nhau phải giống nhau. Các biến thể khác nhau của hàm nạp chồng có thể có số lượng tham số mặc định khác nhau.

3. Các hàm không thể bị quá tải nếu mô tả các tham số của chúng chỉ khác nhau ở công cụ sửa đổi const hoặc cách sử dụng tham chiếu.

Các mẫu chức năng Hãy đặt một mẫu hàm và để trình biên dịch tự tạo bao nhiêu hàm quá tải cho số lượng loại dữ liệu chúng ta cần gọi mẫu. Điều này sẽ chỉ hoạt động nếu thuật toán được triển khai độc lập với kiểu dữ liệu. Do đó, các lĩnh vực ứng dụng nạp chồng hàm và mẫu là khác nhau: chúng tôi sử dụng các hàm nạp chồng để thiết kế các hành động giống nhau về tên nhưng khác nhau về cách triển khai và các mẫu được sử dụng cho các hành động giống hệt nhau trên dữ liệu thuộc các loại khác nhau.

Một mẫu hàm được định nghĩa như sau:

bản mẫu tên loại ([danh sách tham số])

/*nội dung hàm */

Mã định danh Loại, xác định cái gọi là loại tham số có thể được sử dụng cả trong phần còn lại của tiêu đề và trong phần thân của hàm. Kiểu được tham số hóa chỉ là một tên giả mà trình biên dịch sẽ tự động thay thế bằng tên của kiểu dữ liệu thực khi tạo một phiên bản cụ thể của hàm. Nói chung, một mẫu hàm có thể chứa một số loại được tham số hóa .

Quá trình tạo ra một phiên bản cụ thể của một hàm được gọi là sự khởi tạo mẫu hoặc sáng tạo sao chép chức năng. Có hai cách có thể để khởi tạo một mẫu: a) rõ ràng, khi một tiêu đề hàm được khai báo trong đó tất cả các kiểu được tham số hóa được thay thế bằng các kiểu cụ thể đã biết tại thời điểm đó trong chương trình, b) ngầm, trong đó việc tạo một phiên bản hàm tự động diễn ra nếu gặp phải lệnh gọi hàm thực tế.

Các mẫu cũng có thể bị quá tải, cả với các mẫu và các chức năng thông thường.

Ví dụ7 .10. Mẫu chức năng

Viết chương trình xác định số phần tử lớn nhất trong mảng một chiều thuộc nhiều dạng số học khác nhau.

Tìm mức tối đa là một nhiệm vụ rất phổ biến. Đối với điều này, một mẫu đơn giản với một tham số loại là đủ. Hai đối số sẽ được truyền cho chính hàm: một con trỏ tới mảng và độ dài của mảng này.

Danh sách 7.10

#bao gồm<1ostream.h>

#bao gồm

bản mẫu T Max(T *b. int n);

const int n = 20;

cout "Nhập " " n " " số nguyên: " " endl;

với (i = 0; tôi< n; i++) cin » b[i];

cout " Max(b, n) " endl;

nhân đôi a = (0,22, 117,2, -0,08, 0,21, 42,5);

cout " Max(a, 5) " endl;

char *str = "Mẫu tuyệt vời tinh vi";

cout " Max(str. strlen(str)) " endl;

bản mẫu T Max(T *b, int n)

vì (int i = 1; tôi< n; i++)

if (b[i] > b) imax = i;

Mẫu hàm được đặt tên là Max. Sau từ khóa tempiate, tất cả các tham số mẫu được liệt kê trong dấu ngoặc nhọn. Trong trường hợp này chỉ có một tham số. Khi một mẫu được khởi tạo (trong trường hợp này là ngầm), tức là khi trình biên dịch tạo ra một biến thể cụ thể của hàm, loại này sẽ được thay thế bằng một loại tiêu chuẩn cụ thể hoặc loại do người dùng xác định. Sự so khớp được thực hiện khi hàm được gọi, theo kiểu đối số hoặc theo kiểu được chỉ định rõ ràng. Ví dụ: lệnh gọi hàm cuối cùng có thể được viết như thế này:

cout « Max (str, strlen(str));

Phương thức này được sử dụng trong trường hợp loại không được xác định bởi loại toán tử gọi hàm.

Tương tự như các tham số hàm thông thường, bạn có thể đặt giá trị mặc định của tham số mẫu.

Khi làm việc với một dự án có nhiều tệp, bạn phải nhớ rằng nếu một số mẫu hàm được khởi tạo trong một số tệp nguồn thì định nghĩa của mẫu này phải được lặp lại trong mỗi tệp này. Do đó, thông thường định nghĩa mẫu được đặt trong tệp tiêu đề và được đưa vào đúng vị trí với lệnh #include.

Thiết bị và vật liệu.Để thực hiện công việc trong phòng thí nghiệm, bạn cần một máy tính cá nhân có các đặc điểm sau: Bộ xử lý tương thích Intel Pentium với tần số xung nhịp 800 MHz trở lên, RAM - ít nhất 64 MB, dung lượng ổ đĩa trống - ít nhất 500 MB, đầu đọc CD-ROM , loại màn hình Super VGA (số lượng màu từ 256) có đường chéo tối thiểu 15. Phần mềm - hệ điều hành Windows2000/XP trở lên, môi trường phát triển ứng dụng Microsoft Visual Studio.

Những chỉ dẫn an toàn. Các biện pháp phòng ngừa an toàn khi thực hiện công việc trong phòng thí nghiệm trùng với những biện pháp được chấp nhận chung cho người dùng máy tính cá nhân; không tự ý sửa chữa máy tính cá nhân, cài đặt hoặc gỡ bỏ phần mềm; trong trường hợp máy tính cá nhân gặp trục trặc, hãy thông báo cho nhân viên bảo trì phòng thí nghiệm (người vận hành, quản trị viên); tuân thủ các quy tắc an toàn khi làm việc với thiết bị điện; không chạm vào ổ cắm điện bằng vật kim loại; Nơi làm việc của người sử dụng máy tính cá nhân phải được giữ sạch sẽ; Không được phép ăn uống gần máy tính cá nhân.

Phương pháp và quy trình thực hiện công việc. Trước khi thực hiện công việc trong phòng thí nghiệm, mỗi học sinh nhận được một bài tập riêng. Việc bảo vệ công việc trong phòng thí nghiệm chỉ diễn ra sau khi hoàn thành (bài tập cá nhân). Khi bảo vệ bài tập trong phòng thí nghiệm, học sinh trả lời các câu hỏi kiểm tra ở cuối bài và giải thích nhiệm vụ cá nhân đã hoàn thành. Tiến độ bảo vệ công việc thí nghiệm do giáo viên kiểm soát.

1. Làm việc thông qua các ví dụ được đưa ra trong bài tập thí nghiệm.

2. Hoàn thành nhiệm vụ theo lựa chọn của nhiệm vụ cá nhân số 1 của bài thí nghiệm số 6, định dạng từng mục của nhiệm vụ dưới dạng hàm. Tất cả dữ liệu cần thiết cho các hàm phải được truyền cho chúng dưới dạng tham số. Việc sử dụng các biến toàn cục trong các hàm là không được phép.

3. Hoàn thành các nhiệm vụ theo lựa chọn của nhiệm vụ cá nhân số 1 của bài tập thí nghiệm số 6, định dạng từng mục của nhiệm vụ dưới dạng mẫu chức năng. Tất cả dữ liệu cần thiết cho các hàm phải được truyền cho chúng dưới dạng tham số. Việc sử dụng các biến toàn cục trong các hàm là không được phép.

4. Theo tùy chọn, sử dụng đệ quy trực tiếp, viết và thực hiện chương trình. Số tùy chọn được xác định theo công thức
, Ở đâu
- Mã số học sinh theo danh sách của giáo viên.

Lựa chọn:


    Viết chương trình đánh giá hàm Ackermann cho tất cả các đối số nguyên không âm TP:

    MỘTb

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:


P= 0,1,2,… . Lấy câu trả lời gần đúng thỏa mãn điều kiện
, Ở đâu = 0,0001.

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

    Đối với các giới hạn tích hợp nhất định MỘTb tính giá trị của tích phân xác định có dạng sau:

1. Tên công việc thí nghiệm.

3. Trả lời các câu hỏi trắc nghiệm của công việc thí nghiệm.

4. Xây dựng nhiệm vụ cá nhân và trình tự thực hiện.

Một báo cáo bằng văn bản về việc hoàn thành công việc trong phòng thí nghiệm được nộp cho giáo viên.

Câu hỏi để bảo vệ công việc của bạn

1. Hàm trong C++ là gì? Bạn cần gì để sử dụng nó?

2. Cho ví dụ về tiêu đề hàm.

3. Định nghĩa hàm số bao gồm những gì?

4. Sự khác biệt giữa một chức năng và các đối tượng phần mềm khác là gì?

5. Hàm được gọi như thế nào?

6. Mô tả các phương pháp hiện có để giải quyết vấn đề thu được dấu hiệu kết thúc bất thường của nó từ một chương trình con.

7. Cơ chế truyền tham số cho hàm là gì?

8. Phương thức truyền dữ liệu đầu vào.

9. Tính năng C++ được gọi là giá trị tham số mặc định là gì?

10. Tên hàm được truyền vào hàm như thế nào?

11. Mảng một chiều được truyền vào hàm như thế nào?

12. Chuỗi được truyền vào hàm như thế nào?

13. Làm thế nào để truyền mảng hai chiều vào một hàm?

14. Cấu trúc được chuyển thành chức năng như thế nào?

15. Hàm nào được gọi là đệ quy? Ưu điểm và nhược điểm của đệ quy.

16. Thế nào gọi là nạp chồng hàm? Liệt kê các quy tắc mô tả các hàm nạp chồng.

17. Phạm vi của mẫu.

18. Khởi tạo là gì? Các phương pháp khởi tạo một mẫu.