Cấu trúc điều khiển trong C++: Mảng, Struct, Chuỗi và Vòng lặp (ĐH Bách Khoa)

Bài 2 về cấu trúc điều khiển trong kỹ thuật lập trình. Tìm hiểu các lệnh điều kiện, vòng lặp và ứng dụng thực tế. Nắm vững kiến thức để xây dựng chương trình hiệu quả.

Trường đại học

Hochiminh University of Technology

Người đăng

Ẩn danh

Thể loại

Bài giảng
51
2
0

Phí lưu trữ

30 Point

Mục lục chi tiết

Outcomes

Today’s outline

Structured data types

Control structures while statement

while statement

for statement

do-while statement

Structure programming

Don Woods & Philip Wankat Structure programming

Issues

Problem solving - example

Summarise

Tóm tắt

I. Hướng Dẫn Cấu Trúc Điều Khiển C Nền Tảng Mảng Vòng Lặp

Cấu trúc điều khiển là xương sống của mọi chương trình máy tính, và trong C++, chúng đóng vai trò quyết định đến logic và hiệu suất của mã nguồn. Nắm vững cấu trúc điều khiển C++ không chỉ là yêu cầu cơ bản mà còn là chìa khóa để giải quyết các bài toán phức tạp. Bài viết này tập trung phân tích ba thành phần cốt lõi: Mảng (Array), Chuỗi (String) và Vòng lặp (Loop). Đây là những khái niệm nền tảng trong lập trình C++ cơ bản, cho phép lưu trữ, quản lý và xử lý các tập hợp dữ liệu một cách hiệu quả. Mảng cung cấp khả năng lưu trữ một chuỗi các phần tử cùng kiểu dữ liệu, trong khi chuỗi là một dạng mảng chuyên biệt để xử lý văn bản. Vòng lặp, bao gồm vòng lặp for, vòng lặp while, và vòng lặp do-while, là công cụ không thể thiếu để thực thi lặp lại các đoạn mã, đặc biệt là khi duyệt qua các phần tử của mảng và chuỗi. Theo tài liệu từ Đại học Bách khoa TP.HCM, việc sử dụng các cấu trúc dữ liệu có cấu trúc như mảng và các cấu trúc lặp là kỹ năng cơ bản để giải quyết vấn đề. Hiểu rõ cách khai báo mảng C++, thao tác với std::string, và vận dụng linh hoạt các vòng lặp sẽ giúp xây dựng các chương trình rõ ràng, dễ bảo trì và tối ưu hơn. Lập trình có cấu trúc, với việc sử dụng rộng rãi các vòng lặp và cấu trúc khối, là một mô hình nhằm cải thiện chất lượng và thời gian phát triển phần mềm.

1.1. Tổng quan về kiểu dữ liệu có cấu trúc trong C

Trong lập trình C++ cơ bản, bên cạnh các kiểu dữ liệu nguyên thủy (int, float, char), kiểu dữ liệu có cấu trúc (Structured data types) cho phép tổ chức và quản lý dữ liệu phức tạp hơn. Tài liệu giảng dạy định nghĩa chúng là một tập hợp các ô nhớ có thể chứa một hoặc nhiều kiểu dữ liệu khác nhau. Hai đại diện tiêu biểu nhất là Mảng (Array) và Cấu trúc (Struct). Mảng là một chuỗi các ô nhớ liên tiếp trong bộ nhớ, chứa các phần tử có cùng một kiểu dữ liệu. Ví dụ, int sNum[5]; khai báo một mảng chứa 5 số nguyên. Đây là một công cụ mạnh mẽ để làm việc với các tập hợp dữ liệu đồng nhất. Trong khi đó, Struct cho phép gom nhóm các biến và kiểu dữ liệu C++ khác nhau vào chung một thực thể. Ví dụ, một struct Student có thể chứa cả int IDchar name[50]. Việc hiểu rõ các cú pháp C++ để khai báo và sử dụng chúng là bước đầu tiên để xây dựng các ứng dụng có cấu trúc tốt.

1.2. Vai trò của thuật toán trong quá trình giải quyết vấn đề

Thuật toán là trái tim của việc giải quyết vấn đề trong lập trình. Một thuật toán tốt, kết hợp với việc sử dụng hiệu quả các cấu trúc điều khiển C++, sẽ tạo ra một chương trình hiệu quả và chính xác. Tài liệu gốc nhấn mạnh rằng, "Loop is good for performing operations on arrays, strings." (Vòng lặp rất tốt để thực hiện các thao tác trên mảng và chuỗi). Điều này cho thấy sự gắn kết chặt chẽ giữa cấu trúc dữ liệu và cấu trúc điều khiển. Ví dụ, để tìm một phần tử trong mảng, một thuật toán tìm kiếm tuyến tính sẽ sử dụng một vòng lặp để duyệt mảng trong C++ từ đầu đến cuối. Tương tự, các thuật toán sắp xếp mảng phức tạp hơn như Bubble Sort hay Quick Sort đều dựa trên các vòng lặp lồng nhau để so sánh và hoán đổi vị trí các phần tử. Việc lựa chọn đúng vòng lặp (for, while, hay do-while) cho một thuật toán cụ thể có thể ảnh hưởng đến sự rõ ràng và hiệu suất của mã nguồn.

II. Cách Tránh Các Lỗi Phổ Biến Với Vòng Lặp và Mảng C

Mặc dù mạnh mẽ, các cấu trúc điều khiển C++ như vòng lặp và mảng cũng tiềm ẩn nhiều rủi ro nếu không được sử dụng cẩn thận. Một trong những lỗi nguy hiểm và khó gỡ nhất là vòng lặp vô hạn (infinite loop). Lỗi này xảy ra khi điều kiện dừng của vòng lặp while hoặc vòng lặp for không bao giờ được thỏa mãn, khiến chương trình bị treo và tiêu tốn toàn bộ tài nguyên CPU. Tài liệu gốc cảnh báo về các trường hợp như while(true) hoặc for(;;) nếu không có lệnh break bên trong. Một vấn đề nghiêm trọng khác liên quan đến mảng là lỗi truy cập ngoài giới hạn (out-of-bounds access). Khi một chương trình cố gắng đọc hoặc ghi vào một chỉ số không tồn tại trong mảng, ví dụ sNum[5] trong một mảng chỉ có 5 phần tử (chỉ số từ 0 đến 4), hành vi của chương trình sẽ không xác định (undefined behavior). Điều này có thể dẫn đến việc ghi đè lên các vùng nhớ quan trọng khác, gây ra lỗi crash hoặc các lỗ hổng bảo mật. Việc quản lý con trỏ và mảng cũng đòi hỏi sự cẩn trọng cao độ để tránh các lỗi rò rỉ bộ nhớ hoặc con trỏ trỏ đến vùng nhớ không hợp lệ. Hiểu và phòng tránh những lỗi này là kỹ năng quan trọng để viết mã C++ ổn định và an toàn.

2.1. Nhận diện và xử lý lỗi vòng lặp vô hạn Infinite Loop

Lỗi vòng lặp vô hạn xảy ra do sai sót trong logic điều khiển. Nguyên nhân phổ biến nhất là biến đếm không được cập nhật đúng cách trong thân vòng lặp, hoặc câu lệnh điều kiện C++ luôn trả về giá trị true. Ví dụ, trong một vòng lặp while, nếu biến điều kiện không thay đổi để tiến tới điểm dừng, vòng lặp sẽ chạy mãi mãi. Để khắc phục, cần đảm bảo rằng mọi vòng lặp đều có một điều kiện dừng rõ ràng và biến điều kiện phải được cập nhật sau mỗi lần lặp. Sử dụng lệnh break là một cách hiệu quả để thoát khỏi vòng lặp khi một điều kiện đặc biệt được thỏa mãn, ngay cả khi điều kiện chính của vòng lặp vẫn đúng. Khi gỡ lỗi, việc kiểm tra giá trị của các biến điều khiển vòng lặp là bước quan trọng để xác định nguyên nhân gây ra vòng lặp vô hạn.

2.2. Nguy cơ truy cập ngoài giới hạn và quản lý bộ nhớ mảng

Truy cập phần tử mảng ngoài phạm vi được khai báo là một lỗi nghiêm trọng. C++ không tự động kiểm tra giới hạn của mảng khi truy cập, vì vậy trách nhiệm này thuộc về lập trình viên. Ví dụ trong tài liệu gốc sNum[5] = sNum[3] + sNum[4]; trong một mảng sNum[5] sẽ gây ra lỗi. Lỗi này có thể không xuất hiện ngay lập tức mà gây ra những hành vi khó lường về sau. Để phòng tránh, luôn đảm bảo chỉ số truy cập nằm trong khoảng từ 0 đến size-1. Khi sử dụng vòng lặp để duyệt mảng trong C++, điều kiện dừng của vòng lặp phải được thiết lập chính xác, ví dụ for (int i = 0; i < size; i++). Đối với cấp phát động (new float[N]), việc quản lý bộ nhớ còn phức tạp hơn, đòi hỏi phải giải phóng bộ nhớ bằng delete[] khi không còn sử dụng để tránh rò rỉ bộ nhớ. Sử dụng các container hiện đại như std::vector từ Thư viện Chuẩn (STL) là một giải pháp an toàn hơn, vì chúng tự động quản lý bộ nhớ và cung cấp các phương thức kiểm tra giới hạn như at().

III. Bí Quyết Làm Chủ Mảng Trong C Từ Khai Báo Đến Duyệt Mảng

Mảng là một trong những cấu trúc dữ liệu cơ bản và được sử dụng rộng rãi nhất trong C++. Việc làm chủ kỹ thuật làm việc với mảng là nền tảng vững chắc cho bất kỳ lập trình viên nào. Quá trình này bắt đầu từ việc khai báo mảng C++ một cách chính xác. Có hai cách chính: khai báo tĩnh, với kích thước được xác định tại thời điểm biên dịch (ví dụ: int arr[10];), và cấp phát động, với kích thước được quyết định trong lúc chạy chương trình (ví dụ: int* arr = new int[N];). Việc khởi tạo mảng cũng rất quan trọng để đảm bảo các phần tử có giá trị ban đầu hợp lệ. Sau khi khai báo, thao tác cốt lõi là truy cập phần tử mảng thông qua chỉ số. Cần lưu ý rằng chỉ số của mảng trong C++ bắt đầu từ 0. Cấu trúc mảng có thể được mở rộng thành mảng đa chiều, chẳng hạn như mảng hai chiều để biểu diễn ma trận hoặc hình ảnh. Cuối cùng, để xử lý toàn bộ dữ liệu trong mảng, việc duyệt mảng trong C++ bằng các vòng lặp là kỹ thuật không thể thiếu. Sự kết hợp giữa mảng và vòng lặp tạo nên một công cụ cực kỳ mạnh mẽ để giải quyết các bài toán xử lý dữ liệu hàng loạt. Hiểu sâu về mối quan hệ giữa con trỏ và mảng cũng mở ra khả năng tối ưu hóa hiệu suất và thao tác bộ nhớ ở cấp độ thấp.

3.1. Phương pháp khai báo và khởi tạo mảng một và đa chiều

Cú pháp để khai báo một mảng một chiều<kiểu_dữ_liệu> <tên_mảng>[<kích_thước>];. Kích thước mảng phải là một hằng số nếu khai báo tĩnh. Việc khởi tạo mảng có thể được thực hiện ngay lúc khai báo, ví dụ: int sNum[5] = {5, 6, 9, 2, 1};. Nếu không khởi tạo, các phần tử của mảng tĩnh toàn cục sẽ mặc định là 0, nhưng mảng cục bộ sẽ chứa giá trị rác. Đối với mảng đa chiều, ví dụ mảng hai chiều, cú pháp là <kiểu_dữ_liệu> <tên_mảng>[<số_hàng>][<số_cột>];. Chúng rất hữu ích để lưu trữ dữ liệu dạng bảng hoặc ma trận. Cấp phát động cung cấp sự linh hoạt hơn, nhưng đòi hỏi lập trình viên phải tự quản lý bộ nhớ. Sử dụng new để cấp phát và delete[] để giải phóng là quy tắc bắt buộc.

3.2. Kỹ thuật duyệt mảng hiệu quả với vòng lặp for each

Duyệt qua tất cả các phần tử của mảng là một thao tác phổ biến. Cách truyền thống là sử dụng vòng lặp for với một biến chỉ số: for (int i = 0; i < size; ++i) { ... sNum[i] ... }. Tuy nhiên, từ C++11 trở đi, vòng lặp for-each (range-based for loop) cung cấp một cú pháp gọn gàng và an toàn hơn để duyệt mảng. Cú pháp của nó là for (auto& element : array_name) { ... }. Vòng lặp này tự động lặp qua từng phần tử trong array_name mà không cần quản lý biến chỉ số, giúp giảm nguy cơ mắc lỗi truy cập ngoài giới hạn. Sử dụng tham chiếu (&) cho phép thay đổi giá trị của phần tử trực tiếp trong vòng lặp. Đây là phương pháp được khuyến khích khi mục tiêu chỉ đơn giản là xử lý từng phần tử của mảng theo thứ tự.

3.3. So sánh Mảng tĩnh Mảng động và std vector

Lựa chọn giữa mảng tĩnh, mảng động và std::vector phụ thuộc vào yêu cầu của bài toán. Mảng tĩnh có kích thước cố định, được cấp phát trên stack, nhanh và đơn giản nhưng thiếu linh hoạt. Mảng động, được cấp phát trên heap bằng new, cho phép kích thước thay đổi lúc chạy nhưng yêu cầu quản lý bộ nhớ thủ công. std::vector là một lớp container của Thư viện STL, kết hợp ưu điểm của cả hai. Nó quản lý bộ nhớ một cách tự động, có thể thay đổi kích thước linh hoạt (với các phương thức như push_back), và cung cấp các tính năng an toàn như kiểm tra giới hạn (at()). Trong lập trình C++ hiện đại, std::vector thường là lựa chọn ưu tiên so với mảng C-style truyền thống vì sự an toàn, tiện lợi và hiệu quả mà nó mang lại.

IV. Phương Pháp Tối Ưu Các Vòng Lặp C For While Do While

Vòng lặp là một công cụ cơ bản trong cấu trúc điều khiển C++ để thực hiện các tác vụ lặp đi lặp lại. C++ cung cấp ba loại vòng lặp chính: for, while, và do-while. Mặc dù chúng có thể thay thế cho nhau trong nhiều trường hợp, mỗi loại lại có một mục đích sử dụng tối ưu riêng. Vòng lặp for là lựa chọn lý tưởng khi số lần lặp được biết trước, chẳng hạn như duyệt qua một mảng có kích thước cố định. Cấu trúc của nó gộp cả ba phần: khởi tạo, điều kiện và cập nhật, làm cho mã nguồn trở nên gọn gàng và dễ đọc. Vòng lặp while phù hợp hơn khi số lần lặp không xác định và vòng lặp sẽ tiếp tục cho đến khi một điều kiện nào đó không còn đúng. Điều kiện được kiểm tra trước khi thực thi thân vòng lặp. Ngược lại, vòng lặp do-while đảm bảo rằng thân vòng lặp được thực thi ít nhất một lần, vì điều kiện được kiểm tra ở cuối vòng lặp. Lựa chọn này hữu ích trong các trường hợp như nhập dữ liệu từ người dùng và kiểm tra tính hợp lệ. Để kiểm soát luồng thực thi bên trong vòng lặp, C++ cung cấp lệnh break và continue. Lệnh break dùng để thoát khỏi vòng lặp ngay lập tức, trong khi continue bỏ qua phần còn lại của lần lặp hiện tại và chuyển sang lần lặp tiếp theo.

4.1. Khi nào nên sử dụng vòng lặp for và vòng lặp lồng nhau

Sử dụng vòng lặp for khi bạn biết chính xác cần lặp bao nhiêu lần. Đây là trường hợp phổ biến nhất khi làm việc với mảng hoặc khi cần thực hiện một hành động N lần. Cú pháp for (<khởi_tạo>; <điều_kiện>; <cập_nhật>) gói gọn toàn bộ logic điều khiển vòng lặp vào một dòng. Vòng lặp lồng nhau, tức là một vòng lặp nằm bên trong một vòng lặp khác, là kỹ thuật không thể thiếu để xử lý các cấu trúc dữ liệu đa chiều. Ví dụ, để duyệt qua một ma trận (mảng hai chiều), cần một vòng lặp for bên ngoài để duyệt qua các hàng và một vòng lặp for bên trong để duyệt qua các cột trong mỗi hàng. Kỹ thuật này cũng được áp dụng rộng rãi trong các thuật toán sắp xếp mảng và các bài toán xử lý hình ảnh.

4.2. Ứng dụng của vòng lặp while và do while trong thực tế

Vòng lặp while được dùng khi điều kiện dừng phụ thuộc vào một sự kiện bên ngoài hoặc một trạng thái thay đổi không đoán trước được. Ví dụ: đọc dữ liệu từ một tệp cho đến khi hết tệp, hoặc chờ người dùng nhập một giá trị cụ thể. Vòng lặp do-while đặc biệt hữu ích cho các menu hoặc các tác vụ yêu cầu nhập liệu. Vòng lặp này thực thi ít nhất một lần, cho phép chương trình hiển thị lời nhắc, nhận đầu vào, sau đó mới kiểm tra xem đầu vào có hợp lệ hay không. Nếu không, nó sẽ lặp lại quá trình. Cả hai đều là những công cụ mạnh mẽ để xử lý các câu lệnh điều kiện C++ trong một ngữ cảnh lặp lại.

4.3. Cách dùng lệnh break và continue để kiểm soát luồng lặp

Đôi khi, cần phải thay đổi luồng thực thi bình thường của một vòng lặp. Lệnh break cung cấp một lối thoát tức thì khỏi vòng lặp đang chứa nó. Nó thường được sử dụng bên trong một lệnh rẽ nhánh if-else để kết thúc vòng lặp sớm khi một điều kiện đặc biệt được đáp ứng (ví dụ: tìm thấy phần tử cần tìm). Ngược lại, lệnh continue không kết thúc vòng lặp. Thay vào đó, nó bỏ qua tất cả các câu lệnh còn lại trong lần lặp hiện tại và bắt đầu ngay lần lặp tiếp theo. Lệnh này hữu ích khi muốn bỏ qua một số giá trị hoặc trường hợp nhất định mà không cần thoát hoàn toàn khỏi vòng lặp.

V. Hướng Dẫn Thao Tác Chuỗi C Hiệu Quả Với Thư Viện String

Xử lý văn bản là một nhiệm vụ cơ bản trong lập trình, và C++ cung cấp hai cơ chế chính để làm việc với chuỗi. Kiểu chuỗi trong C++ truyền thống là chuỗi ký tự C-style, vốn là một mảng các ký tự (char) kết thúc bằng ký tự null (\0). Mặc dù hiệu quả về bộ nhớ, việc thao tác với chúng đòi hỏi sự cẩn trọng cao và sử dụng các hàm từ thư viện <cstring> như strcpy, strcat, strcmp. Các thao tác này dễ gây ra lỗi tràn bộ đệm (buffer overflow) nếu không quản lý kích thước cẩn thận. Để giải quyết những hạn chế này, C++ hiện đại khuyến khích sử dụng lớp std::string từ thư viện string. Đây là một lớp container mạnh mẽ, an toàn và tiện lợi hơn rất nhiều. std::string tự động quản lý bộ nhớ, giúp tránh các lỗi phổ biến liên quan đến con trỏ và cấp phát. Nó cung cấp một loạt các phương thức tích hợp để thực hiện các thao tác chuỗi C++ một cách dễ dàng, chẳng hạn như nối chuỗi C++ bằng toán tử + hoặc +=, so sánh chuỗi bằng các toán tử so sánh thông thường (==, <, >), và nhiều hàm xử lý chuỗi hữu ích khác như find(), substr(), length(). Việc chuyển từ chuỗi C-style sang std::string là một bước tiến quan trọng để viết mã C++ an toàn và dễ bảo trì hơn.

5.1. So sánh chuỗi C style char và lớp std string

Chuỗi ký tự C-style về cơ bản là một con trỏ và mảng ký tự (char* hoặc char[]). Chúng nhanh và tương thích ngược với C, nhưng yêu cầu quản lý bộ nhớ thủ công và dễ gây lỗi. Mọi thao tác đều phải thông qua các hàm như strlen để lấy độ dài hay strcpy để sao chép. Ngược lại, std::string là một đối tượng lớp. Nó đóng gói dữ liệu chuỗi cùng với các phương thức để thao tác, tự động xử lý việc cấp phát và giải phóng bộ nhớ. Kích thước của chuỗi có thể thay đổi linh hoạt. Về hiệu suất, std::string có thể chậm hơn một chút trong một số trường hợp do chi phí quản lý bộ nhớ động, nhưng sự an toàn và tiện lợi mà nó mang lại thường đáng giá hơn nhiều.

5.2. Các hàm xử lý chuỗi và thao tác chuỗi C phổ biến

Lớp std::string cung cấp vô số phương thức hữu ích. Để nối chuỗi C++, có thể dùng toán tử + hoặc phương thức append(). Để truy cập ký tự, có thể dùng toán tử [] hoặc phương thức at() (an toàn hơn vì có kiểm tra giới hạn). Các hàm xử lý chuỗi khác bao gồm: length() hoặc size() để lấy độ dài, find() để tìm vị trí một chuỗi con, substr() để trích xuất chuỗi con, và compare() để so sánh chuỗi một cách chi tiết. Các thao tác chuỗi C++ này làm cho việc phân tích cú pháp, định dạng và xử lý văn bản trở nên đơn giản và ít bị lỗi hơn rất nhiều so với việc sử dụng chuỗi C-style.

VI. Ứng Dụng Cấu Trúc Điều Khiển C Vào Giải Thuật Thực Tế

Lý thuyết về cấu trúc điều khiển C++ chỉ thực sự trở nên hữu ích khi được áp dụng để giải quyết các vấn đề thực tế. Sự kết hợp giữa mảng, chuỗi và vòng lặp là nền tảng cho vô số thuật toán quan trọng. Ví dụ, trong xử lý hình ảnh, một tấm ảnh có thể được biểu diễn dưới dạng một mảng đa chiều, trong đó mỗi phần tử đại diện cho một pixel. Sử dụng các vòng lặp lồng nhau là cách tiêu chuẩn để duyệt qua từng pixel, áp dụng các bộ lọc, thay đổi độ sáng hoặc thực hiện các phép biến đổi khác. Trong lĩnh vực phân tích dữ liệu, các thuật toán sắp xếp mảng như Bubble Sort, Insertion Sort, hoặc Quick Sort đều dựa trên việc lặp đi lặp lại qua các phần tử của mảng để so sánh và hoán đổi vị trí cho đến khi mảng được sắp xếp theo một thứ tự nhất định. Các câu lệnh điều kiện C++ như lệnh rẽ nhánh if-elseswitch-case trong C++ đóng vai trò quyết định logic so sánh bên trong các vòng lặp này. Ngay cả việc phân tích một dòng lệnh đầu vào, như ví dụ trong tài liệu gốc, cũng đòi hỏi kỹ năng thao tác chuỗi C++ và sử dụng vòng lặp để tách các đối số. Những ứng dụng này cho thấy việc thành thạo các cấu trúc điều khiển cơ bản là điều kiện tiên quyết để xây dựng các giải pháp phần mềm phức tạp và hiệu quả.

6.1. Ví dụ về thuật toán sắp xếp mảng sử dụng vòng lặp

Một ví dụ điển hình là thuật toán Bubble Sort (Sắp xếp nổi bọt). Thuật toán này hoạt động bằng cách lặp đi lặp lại qua danh sách, so sánh các cặp phần tử liền kề và hoán đổi chúng nếu chúng sai thứ tự. Để triển khai, cần sử dụng một vòng lặp lồng nhau. Vòng lặp ngoài kiểm soát số lần duyệt qua toàn bộ mảng, trong khi vòng lặp trong thực hiện việc so sánh và hoán đổi. Một lệnh rẽ nhánh if-else được dùng để quyết định có cần hoán đổi hay không. Mặc dù không phải là thuật toán sắp xếp mảng hiệu quả nhất, Bubble Sort là một ví dụ tuyệt vời để minh họa cách vòng lặp for và các câu lệnh điều kiện phối hợp với nhau để giải quyết một bài toán cụ thể.

6.2. Triển khai xử lý ma trận với mảng hai chiều và vòng lặp

Ma trận là một ứng dụng tự nhiên của mảng đa chiều. Một ma trận m x n có thể được biểu diễn bằng một mảng int matrix[m][n];. Các thao tác trên ma trận, như cộng hai ma trận hoặc nhân ma trận, đều yêu cầu sử dụng vòng lặp lồng nhau. Ví dụ, để cộng hai ma trận A và B, cần một vòng lặp ngoài duyệt qua các hàng (i từ 0 đến m-1) và một vòng lặp trong duyệt qua các cột (j từ 0 đến n-1). Tại mỗi vị trí (i, j), phần tử của ma trận kết quả sẽ là C[i][j] = A[i][j] + B[i][j]. Đây là minh chứng rõ ràng cho sức mạnh của việc kết hợp cấu trúc dữ liệu mảng với cấu trúc điều khiển vòng lặp để xử lý dữ liệu có cấu trúc phức tạp.

28/09/2025

Trích đoạn nội dung tài liệu

Hochiminh University of Technology Computer Science and Engineering - [CO1011 - 501127] Fundamentals of Control Structures (part 2) C++ Programming Lecturer: Duc Dung Nguyen Credits: 4 Outcomes ❖ Using array, string, and structured data types ❖ Solve the problem using loop structures ❖ Implement program with loop structures: ❖ while, for, do-while ❖ Understand the role of algorithm in problem solving process 2 Today’s outline ❖ Structured data types ❖ Array ❖ Struct ❖ Basic control structures in C/C++ ❖ Loop statements: while, for, do-while ❖ Structure programming 3 Structured data types Structured data types ❖ Can we implement a program with only basic data types? ❖ What do we need beside basic data types? ❖ A sequence of memory slots that contains a specific data type ❖ A mixture of different data types 5 Structured data types ❖ Array: a sequence of memory slots that contains a specific data type ❖ <data type> <variable name>[<Size>]; ❖ int Fibonacci[MAX_LENGTH];/* declare an integer array of MAX_LENGTH elements. This is a static declaration! */ ❖ <data type> *<variable name>;// alternative declaration, a pointer ❖ float *plotY; 6 Structured data types ❖ Array ❖ int N; cout << “Please input size of the sequence: “; cin >> N; float x[N];// compiler will fire an error here … ❖ int N; cout << “Please input size of the sequence: “; cin >> N; std::vector<float> x(N);// no error, but x is a vector class … 7 Structured data types ❖ Array: initialization ❖ At declaration time (static) ❖ int sNum[5] = {5, 6, 9, 2, 1}; ❖ float x[] = {0.2};// allocated 4 elements ❖ Dynamically allocate ❖ float *pNum; … pNum = new float[N]; 8 Structured data types ❖ Array ❖ Access array elements: ❖ <variable name>[<index>] ❖ int sNum[5]; sNum[0] = 1; sNum[1] = 1; sNum[2] = sNum[0] + sNum[1]; sNum[3] = sNum[1] + sNum[2]; sNum[4] = sNum[2] + sNum[3]; sNum[5] = sNum[3] + sNum[4];// What will happen here? 9 Structured data types ❖ String: ❖ char strName[50];// undefined string ❖ char strName[50] = “Dustin”; ❖ char strOutText[] = “This text contains 32 characters”;//33 bytes ❖ char *pStr = “Unknown”; 10 Structured data types ❖ String: ❖ strlen: length of string ❖ strcpy: copy a string ❖ strcat: concatenate strings ❖ strcmp: compare two strings ❖ strchr: locate the first occurrent of character in a string ❖ strrchr: locate the last occurrent of character in a string 11 Structured data types ❖ String: ❖ string sText;// empty string “” ❖ string sText = “A C++ class that stores characters.max_size, sText.at(index) ❖ sText += anotherText; ❖ sText.push_back, sText.pop_back, sText. 12 Structured data types ❖ Struct: ❖ struct [<struct name>] { <elements>; } [<variables>]; ❖ struct Student { int ID; char name[50];// can you use string? Why should you use? }; struct Student studentList[40]; 13 Structured data types ❖ typedef: define a data type ❖ typedef struct{ char name[30]; } StdName_t; StdName studentList[50]; ❖ typedef struct Student { int ID; char name[50];// can you use string? Why should you use? } Student_t; Student_t studentList; 14 Control structures while statement ❖ Why do we need iterations? ❖ Waiting for something to happen ❖ Operate on several objects ❖ List, array of objects ❖ String 16 while statement ❖ while loop: ❖ Execute a section of code over and over under certain conditions ❖ while (<condition>) <statement>; ❖ while (<condition>) { <statements>; } ❖ E. ❖ Do not forget stopping condition.

❖ Take care of counters. ❖ Use infinite loop wisely. 22 while statement ❖ Nested loop ❖ A loop can be nested inside a loop. ❖ while (<condition 1>) { <statements>; while (<condition 2>) { <statements>; while (<condition 3>); } <statements>; } 23 while statement <exp> N Y statement <exp> N Y statement statement statement 24 while statement ❖ Nested loop ❖ Is used to process multi-dimension arrays ❖ Access customised data ❖ Waiting for inputs ❖ etc.

25 for statement ❖ Why do you need for statement? ❖ Just another way to write iteration/loop structure! ❖ Counting is a frequent activity ❖ for: a specialised loop that package the following tasks in a statement ❖ Initialise a counter variable ❖ Modify the counter ❖ Check complete condition 26 for statement ❖ for loop: ❖ for (<initialization>; <condition>; <modification>) <statement>; ❖ for (<initialization>; <condition>; <modification>) { <statements>; } ❖ E.: ❖ for (i = 0; i < 100; ++i) cout << i << “, “; ❖ for (j = 0; j > -10; --j) cout << j << “, “; 27 for statement ❖ Flowchart initialization <cond.> N modification Y statement statement 28 for statement ❖ Initialization: set value for the counter ❖ Declare one or many counters (same type) and init them at once ❖ Initialize many counters if needed ❖ Condition: a boolean expression that must be evaluated at each loop ❖ Modification: change value of the counter at each loop 29 for statement ❖ Nested loop: #include <iostream> #include <iostream> #include <math.h> int main() { int main() { int img[12][16]; int img[12][16]; int i = 0, j; for (int i = 0; i < 12; i++) { for (; i < 12; i++) { for (int j = 0; j < 16; j++) { for (j = 0; j < 16; j++) { img[i][j] = rand() % 256; img[i][j] = rand() % 256; } } } } return 0; return 0; } } 30 for statement ❖ Breaking the rule ❖ for (<initilization>; <condition>; <modification>) { <statements>; if (<special condition>) break; <statements>; } ❖ for (<initilization>; <condition>; <modification>) { <statements>; if (<special condition>) continue; <statements>; } 31 do-while statement ❖ do-while: do first, check later ❖ A convenient way to perform some operations ❖ E.: ❖ Asking user to input some values ❖ Check if any input value was invalid ❖ Loop to input again 32 do-while statement ❖ do-while loop: ❖ do <statement>; while (<condition>); ❖ do { <statements>; } while (<condition>); ❖ E.: ❖ do { cout << “Please input a positive number” << endl; cin >> i; // should prompt user } while (i < 0); 33 do-while statement ❖ Flowchart statement Y <exp> N statement 34 do-while statement ❖ Breaking the rule ❖ do { <statements>; if (<special condition>) break; <statements>; } while (<condition>); ❖ do { <statements>; if (<special condition>) continue; <statements>; } while (<condition>); 35 do-while statement ❖ Nested loop ❖ do { <statements>; do { <statements>; while (<condition 3>) { <statements>; } } while (<condition 2>); <statements>; } while (<condition 1>); 36 Structure programming Structure programming ❖ Definition: a programming paradigm aimed at improving the clarity, quality and development time of a computer program by making extensive use of subroutines, block structures and for/while loops ❖ Structured programming languages: ALGOL, Pascal, PL/I, Ada, C/C++, etc. 38 Problem solving strategies solution Plan Act method criteria known apply to new situations Define Gather Explore Check Generalize problem information constraints unknown make sense look at the evaluate Disseminate troubleshooting problem from brainstorm against different criteria viewpoint 39 Don Woods & Philip Wankat Structure programming ❖ Loop and array ❖ Loop is good for performing operations on arrays, strings. ❖ “while”, “do-while”, “for” are exchangeable. ❖ Fixed size data should be processed using finite loops.

40 Issues ❖ Infinite loops: ❖ while (100); ❖ while (true) cin >> i; ❖ do { } while (-20); ❖ for (;;); ❖ etc. 41 Issues ❖ Infinite loops: ❖ Sometimes your code is stuck in an infinite loop due to logic errors ❖ Factors: input, user interaction, or special computation ❖ E.* ->* pointer to member 5 * / % arithmetic: scaling 6 + - arithmetic: addition 7 >> << bitwise shift 8 < > <= >= relational 9. == != equality 10 & AND 11 ^ XOR 12 | OR 13 && conjunction 14 || disjunction 15. = *= /= %= += -= >>= <<= &= ^= |= ?: assignment level expressions 16 , sequencing 44 Problem solving - example ❖ Input and draw the following figure in terminal: ❖ Input: N (number of lines) ❖ Output: (in case N = 5) * * * * * * * * * * * * * * * 45 Problem solving - example ❖ Input and draw the following figure in terminal: ❖ Input: N (number of lines) ❖ Output: (in case N = 5) * * ** ** *** *** **** **** ********* 46 Problem solving - example ❖ Input and draw the following figure in terminal: ❖ Input: N (number of lines) ❖ Output: (in case N = 5) * * ** ** * * * * * * * * * * * 47 Problem solving - example ❖ Parse strings ❖ The command line is given as follows: ❖ <program name> [<arguments>] ❖ E.txt -d ❖ Write a program that parse the command line and print list of arguments 48 Problem solving - example ❖ Math: ❖ Print prime numbers from 2 to N (N is the input value) ❖ Print Fibonacci sequence from 3 to N (N is the input value) 49 Summarise ❖ Array, string, and structured data types ❖ Understand loop structures: while, for, do-while ❖ The role of algorithm in problem solving process ❖ Using loop structures on arrays, strings ❖ Implements algorithms with loops 50

Nội dung được bảo vệ bản quyền — Tải xuống đầy đủ