Đề cương Cơ sở kỹ thuật lập trình
Bạn đang xem 20 trang mẫu của tài liệu "Đề cương Cơ sở kỹ thuật lập trình", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Tài liệu đính kèm:
- de_cuong_co_so_ky_thuat_lap_trinh.doc
Nội dung text: Đề cương Cơ sở kỹ thuật lập trình
- trêng ®¹i häc s ph¹m kü thuËt hng yªn Khoa c«ng nghÖ th«ng tin ®Ò c¬ng bµi gi¶ng M«n: C¬ së kü thuËt lËp tr×nh víi C# Hng yªn 07/2007
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm § Ò c¬ng c¬ së kü thuËt lËp tr×nh Ch¬ng 1: Tæng quan vÒ ng«n ng÷ lËp tr×nh C# 1.1 Tæng quan vÒ ng«n ng÷ lËp tr×nh C# Ngôn ngữ C# khá đơn giản, chỉ khoảng 80 từ khóa và hơn mười mấy kiểu dữ liệu được xây dựng sẵn. Tuy nhiên, ngôn ngữ C# có ý nghĩa cao khi nó thực thi những khái niệm lập trình hiện đại. C# bao gồm tất cả những hỗ trợ cho cấu trúc, thành phần component, lập trình hướng đối tượng. Những tính chất đó hiện diện trong một ngôn ngữ lập trình hiện đại và được phát triển bởi Microsoft, là phần khởi đầu cho kế hoạch .NET của họ. Tên của ngôn ngữ bao gồm ký tự thăng theo Microsoft nhưng theo ECMA là C#, chỉ bao gồm dấu số thường. Microsoft phát triển C# dựa trên C++ và Java. C# được miêu tả là ngôn ngữ có được sự cân bằng giữa C++, Visual Basic, Delphi và Java. C# theo một hướng nào đó là ngôn ngữ lập trình phản ánh trực tiếp nhất đến .NET Framework mà tất cả các chương trình .NET chạy, và nó phụ thuộc mạnh mẽ vào Framework này. Các loại dữ liệu cơ sở là những đối tượng, hay được gọi là garbage-collected, và nhiều kiểu trừu tượng khác chẳng hạn như class, delegate, interface, exception, v.v, phản ánh rõ ràng những đặc trưng của .NET runtime. So sánh với C và C++, ngôn ngữ này bị giới hạn và được nâng cao ở một vài đặc điểm nào đó, nhưng không bao gồm các giới hạn sau đây: Các con trỏ chỉ có thể được sử dụng trong chế độ không an toàn. Hầu hết các đối tượng được tham chiếu an toàn, và các phép tính đều được kiểm tra tràn bộ đệm. Các con trỏ chỉ được sử dụng để gọi các loại kiểu giá trị; còn những đối tượng thuộc bộ thu rác (garbage-collector) thì chỉ được gọi bằng cách tham chiếu. + Các đối tượng không thể được giải phóng tường minh. + Chỉ có đơn kế thừa, nhưng có thể cài đặt nhiều interface trừu tượng (abstract interfaces). Chức năng này làm đơn giản hóa sự thực thi của thời gian thực thi. + C# thì an-toàn-kiểu (typesafe) hơn C++. + Cú pháp khai báo mảng khác nhau("int[] a = new int[5]" thay vì "int a[5]"). + Kiểu thứ tự được thay thế bằng tên miền không gian (namespace). + C# không có tiêu bản. 2
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm + Có thêm Properties, các phương pháp có thể gọi các Properties để truy cập dữ liệu. + Có reflection. Tại sao phải sử dụng ngôn ngữ C# Nhiều người tin rằng không cần thiết có một ngôn ngữ lập trình mới. Java, C++, Perl, Microsoft Visual Basic, và những ngôn ngữ khác được nghĩ rằng đã cung cấp tất cả những chức năng cần thiết. Ngôn ngữ C# là một ngôn ngữ được dẫn xuất từ C và C++, nhưng nó được tạo từ nền tảng phát triển hơn. Microsoft bắt đầu với công việc trong C và C++ và thêm vào những đặc tính mới để làm cho ngôn ngữ này dễ sử dụng hơn. Nhiều trong số những đặc tính này khá giống với những đặc tính có trong ngôn ngữ Java. Không dừng lại ở đó, Microsoft đưa ra một số mục đích khi xây dựng ngôn ngữ này. Những mục đích này được được tóm tắt như sau: - C# là ngôn ngữ đơn giản - C# là ngôn ngữ hiện đại - C# là ngôn ngữ hướng đối tượng - C# là ngôn ngữ mạnh mẽ và mềm dẻo - C# là ngôn ngữ có ít từ khóa - C# là ngôn ngữ hướng module - C# sẽ trở nên phổ biến C# là ngôn ngữ đơn giản C# loại bỏ một vài sự phức tạp và rối rắm của những ngôn ngữ như Java và C++, bao gồm việc loại bỏ những macro, những template, đa kế thừa, và lớp cơ sở ảo (virtual base class). Chúng là những nguyên nhân gây ra sự nhầm lẫn hay dẫn đến những vấn đề cho các người phát triển C++. Nếu chúng ta là người học ngôn ngữ này đầu tiên thì chắc chắn là ta sẽ không trải qua những thời gian để học nó! Nhưng khi đó ta sẽ không biết được hiệu quả của ngôn ngữ C# khi loại bỏ những vấn đề trên. Ngôn ngữ C# đơn giản vì nó dựa trên nền tảng C và C++. Nếu chúng ta thân thiện với C và C++ hoậc thậm chí là Java, chúng ta sẽ thấy C# khá giống về diện mạo, cú pháp, biểu thức, toán tử và những chức năng khác được lấy trực tiếp từ ngôn ngữ C và C++, nhưng nó đã được cải tiến để làm cho ngôn ngữ đơn giản hơn. Một vài trong các sự cải tiến là loại bỏ các dư thừa, hay là thêm vào những cú pháp thay đổi. Ví dụ như, trong C++ có ba toán tử làm việc với các thành viên là ::, . , và ->. Để biết khi nào dùng ba toán tử này cũng phức tạp và 3
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm dễ nhầm lẫn. Trong C#, chúng được thay thế với một toán tử duy nhất gọi là . (dot). Đối với người mới học thì điều này và những việc cải tiến khác làm bớt nhầm lẫn và đơn giản hơn. Ghi chú: Nếu chúng ta đã sử dụng Java và tin rằng nó đơn giản, thì chúng ta cũng sẽ tìm thấy rằng C# cũng đơn giản. Hầu hết mọi người đều không tin rằng Java là ngôn ngữ đơn giản. Tuy nhiên, C# thì dễ hơn là Java và C++. C# là ngôn ngữ hiện đại Điều gì làm cho một ngôn ngữ hiện đại? Những đặc tính như là xử lý ngoại lệ, thu gom bộ nhớ tự động, những kiểu dữ liệu mở rộng, và bảo mật mã nguồn là những đặc tính được mong đợi trong một ngôn ngữ hiện đại. C# chứa tất cả những đặc tính trên. Nếu là người mới học lập trình có thể chúng ta sẽ cảm thấy những đặc tính trên phức tạp và khó hiểu. Tuy nhiên, cũng đừng lo lắng chúng ta sẽ dần dần được tìm hiểu những đặc tính qua các chương trong cuốn sách này. Ghi chú: Con trỏ được tích hợp vào ngôn ngữ C++. Chúng cũng là nguyên nhân gây ra những rắc rối của ngôn ngữ này. C# loại bỏ những phức tạp và rắc rối phát sinh bởi con trỏ. Trong C#, bộ thu gom bộ nhớ tự động và kiểu dữ liệu an toàn được tích hợp vào ngôn ngữ, sẽ loại bỏ những vấn đề rắc rối của C++. C# là ngôn ngữ hướng đối tượng Những đặc điểm chính của ngôn ngữ hướng đối tượng (Object-oriented language) là sự đóng gói (encapsulation), sự kế thừa (inheritance), và đa hình (polymorphism). C# hỗ trợ tất cả những đặc tính trên. Phần hướng đối tượng của C# sẽ được trình bày chi tiết trong một chương riêng ở phần sau. C# là ngôn ngữ mạnh mẽ và cũng mềm dẻo Như đã đề cập trước, với ngôn ngữ C# chúng ta chỉ bị giới hạn ở chính bởi bản thân hay là trí tưởng tượng của chúng ta. Ngôn ngữ này không đặt những ràng buộc lên những việc có thể làm. C# được sử dụng cho nhiều các dự án khác nhau như là tạo ra ứng dụng xử lý văn bản, ứng dụng đồ họa, bản tính, hay thậm chí những trình biên dịch cho các ngôn ngữ khác. C# là ngôn ngữ ít từ khóa C# là ngôn ngữ sử dụng giới hạn những từ khóa. Phần lớn các từ khóa được sử dụng để mô tả thông tin. Chúng ta có thể nghĩ rằng một ngôn ngữ có nhiều từ khóa thì sẽ mạnh hơn. Điều này không phải sự thật, ít nhất là trong trường hợp ngôn ngữ C#, chúng ta có thể tìm thấy rằng ngôn ngữ này có thể 4
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm được sử dụng để làm bất cứ nhiệm vụ nào. Bảng sau liệt kê các từ khóa của ngôn ngữ C#. C# là ngôn ngữ hướng module Mã nguồn C# có thể được viết trong những phần được gọi là những lớp, những lớp này chứa các phương thức thành viên của nó. Những lớp và những phương thức có thể được sử dụng lại trong ứng dụng hay các chương trình khác. Bằng cách truyền các mẫu thông tin đến những lớp hay phương thức chúng ta có thể tạo ra những mã nguồn dùng lại có hiệu quả. C# sẽ là một ngôn ngữ phổ biến C# là một trong những ngôn ngữ lập trình mới nhất. Vào thời điểm cuốn sách này được viết, nó không được biết như là một ngôn ngữ phổ biến. Nhưng ngôn ngữ này có một số lý do để trở thành một ngôn ngữ phổ biến. Một trong những lý do chính là Microsoft và sự cam kết của .NET Microsoft muốn ngôn ngữ C# trở nên phổ biến. Mặc dù một công ty không thể làm một sản phẩm trở nên phổ biến, nhưng nó có thể hỗ trợ. Cách đây không lâu, Microsoft đã gặp sự thất bại về hệ điều hành Microsoft Bob. Mặc dù Microsoft muốn Bob trở nên phổ biến nhưng thất bại. C# thay thế tốt hơn để đem đến thành công sơ với Bob. Thật sự là không biết khi nào mọi người trong công ty Microsoft sử dụng Bob trong công việc hằng ngày của họ. Tuy nhên, với C# thì khác, nó được sử dụng bởi Microsoft. Nhiều sản phẩm của công ty này đã chuyển đổi và viết lại bằng 5
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm C#. Bằng cách sử dụng ngôn ngữ này Microsoft đã xác nhận khả năng của C# cần thiết cho những người lập trình. Micorosoft .NET là một lý do khác để đem đến sự thành công của C#. .NET là một sự thay đổi trong cách tạo và thực thi những ứng dụng. Ngoài hai lý do trên ngôn ngữ C# cũng sẽ trở nên phổ biến do những đặc tính của ngôn ngữ này được đề cập trong mục trước như: đơn giản, hướng đối tượng, mạnh mẽ Ngôn ngữ C# và những ngôn ngữ khác Chúng ta đã từng nghe đến những ngôn ngữ khác như Visual Basic, C++ và Java. Có lẽ chúng ta cũng tự hỏi sự khác nhau giữa ngôn ngữ C# và nhưng ngôn ngữ đó. Và cũng tự hỏi tại sao lại chọn ngôn ngữ này để học mà không chọn một trong những ngôn ngữ kia. Có rất nhiều lý do và chúng ta hãy xem một số sự so sánh giữa ngôn ngữ C# với những ngôn ngữ khác giúp chúng ta phần nào trả lời được những thắc mắc. Microsoft nói rằng C# mang đến sức mạnh của ngôn ngữ C++ với sự dễ dàng của ngôn ngữ Visual Basic. Có thể nó không dễ như Visual Basic, nhưng với phiên bản Visual Basic.NET (Version 7) thì ngang nhau. Bởi vì chúng được viết lại từ một nền tảng. Chúng ta có thể viết nhiều chương trình với ít mã nguồn hơn nếu dùng C#. Mặc dù C# loại bỏ một vài các đặc tính của C++, nhưng bù lại nó tránh được những lỗi mà thường gặp trong ngôn ngữ C++. Điều này có thể tiết kiệm được hàng giờ hay thậm chí hàng ngày trong việc hoàn tất một chương trình. Chúng ta sẽ hiểu nhiều về điều này trong các chương của giáo trình. Một điều quan trọng khác với C++ là mã nguồn C# không đòi hỏi phải có tập tin header. Tất cả mã nguồn được viết trong khai báo một lớp. Như đã nói ở bên trên .NET runtime trong C# thực hiện việc thu gom bộ nhớ tự động. Do điều này nên việc sử dụng con trỏ trong C# ít quan trọng hơn trong C++. Những con trỏ cũng có thể được sử dụng trong C#, khi đó những đoạn mã nguồn này sẽ được đánh dấu là không an toàn (unsafe code). C# cũng từ bỏ ý tưởng đa kế thừa như trong C++. Và sự khác nhau khác là C# đưa thêm thuộc tính vào trong một lớp giống như trong Visual Basic. Và những thành viên của lớp được gọi duy nhất bằng toán tử “.” khác với C++ có nhiều cách gọi trong các tình huống khác nhau. Một ngôn ngữ khác rất mạnh và phổ biến là Java, giống như C++ và C# được phát triển dựa trên C. Nếu chúng ta quyết định sẽ học Java sau này, chúng ta sẽ tìm được nhiều cái mà học từ C# có thể được áp dụng. 6
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Điểm giống nhau C# và Java là cả hai cùng biên dịch ra mã trung gian: C# biên dịch ra MSIL còn Java biên dịch ra bytecode. Sau đó chúng được thực hiện bằng cách thông dịch hoặc biên dịch just-in-time trong từng máy ảo tương ứng. Tuy nhiên, trong ngôn ngữ C# nhiều hỗ trợ được đưa ra để biên dịch mã ngôn ngữ trung gian sang mã máy. C# chứa nhiều kiểu dữ liệu cơ bản hơn Java và cũng cho phép nhiều sự mở rộng với kiểu dữ liệu giá trị. Ví dụ, ngôn ngữ C# hỗ trợ kiểu liệt kệ (enumerator), kiểu này được giới hạn đến một tập hằng được định nghĩa trước, và kiểu dữ liệu cấu trúc đây là kiểu dữ liệu giá trị do người dùng định nghĩa. Chúng ta sẽ được tìm hiểu kỹ hơn về kiểu dữ liệu tham chiếu và kiểu dữ liệu giá trị sẽ được trình bày trong phần sau Tương tự như Java, C# cũng từ bỏ tính đa kế thừa trong một lớp, tuy nhiên mô hình kế thừa đơn này được mở rộng bởi tính đa kế thừa nhiều giao diện. Các bước chuẩn bị cho chương trình Thông thường, trong việc phát triển phần mềm, người phát triển phải tuân thủ theo quy trình phát triển phần mềm một cách nghiêm ngặt và quy trình này đã được chuẩn hóa. Tuy nhiên trong phạm vi của chúng ta là tìm hiểu một ngôn ngữ mới và viết những chương trình nhỏ thì không đòi hỏi khắt khe việc thực hiện theo quy trình. Nhưng để giải quyết được những vấn đề thì chúng ta cũng cần phải thực hiện đúng theo các bước sau. Đầu tiên là phải xác định vấn đề cần giải quyết. Nếu không biết rõ vấn đề thì ta không thể tìm được phương pháp giải quyết. Sau khi xác định được vấn đề, thì chúng ta có thể nghĩ ra các kế hoạch để thực hiện. Sau khi có một kế hoạch, thì có thể thực thi kế hoạch này. Sau khi kế hoạch được thực thi, chúng ta phải kiểm tra lại kết quả để xem vấn đề được giải quyết xong chưa. Logic này thường được áp dụng trong nhiều lĩnh vực khác nhau, trong đó có lập trình. Khi tạo một chương trình trong C# hay bất cứ ngôn ngữ nào, chúng ta nên theo những bước tuần tự sau: - Xác định mục tiêu của chương trình. - Xác định những phương pháp giải quyết vấn đề. - Tạo một chương trình để giải quyết vấn đề. - Thực thi chương trình để xem kết quả. Ví dụ mục tiêu để viết chương trình xử lý văn bản đơn giản, mục tiêu chính là xây dựng chương trình cho phép soạn thảo và lưu trữ những chuỗi ký tự hay văn bản. Nếu không có mục tiêu thì không thể viết được chương trình hiệu quả. 7
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Bước thứ hai là quyết định đến phương pháp để viết chương trình. Bước này xác địnhn những thông tin nào cần thiết được sử dụng trong chương trình, các hình thức nào được sử dụng. Từ những thông tin này chúng ta rút ra được phương pháp để giải quyết vấn đề. Bước thứ ba là bước cài đặt, ở bước này có thể dùng các ngôn ngữ khác nhau để cài đặt, tuy nhiên, ngôn ngữ phù hợp để giải quyết vấn đề một cách tốt nhất sẽ được chọn. Trong phạm vi của sách này chúng ta mặc định là dùng C#, đơn giản là chúng ta đang tìm hiểu nó! Và bước cuối cùng là phần thực thi chương trình để xem kết quả. 1.2 VÝ dô më ®Çu Để bắt đầu cho việc tìm hiểu ngôn ngữ C# và tạo tiền đề cho các chương sau, chương đầu tiên trình bày một chương trình C# đơn giản nhất. Ví dụ 1.2 : Chương trình C# đầu tiên. class ChaoMung { static void Main( ) { // Xuat ra man hinh System.Console.WriteLine(“Hello World”); } } Hello World Sau khi viết xong chúng ta lưu dưới dạng tập tin có phần mở rộng *.cs (C sharp). Sau đó biên dịch và chạy chương trình. Kết quả là một chuỗi “Hello World” sẽ xuất hiện trong màn hình Console. 1.3 CÊu tróc mét ch¬ng tr×nh C# ®¬n gi¶n Từ ví dụ trên ta nhận thấy rằng một chương trình C# đơn giản có ít nhất là một lớp. Mỗi lớp được bắt đầu bằng từ khoá class kế đó là tên lớp( tên lớp do chúng ta đặt và phải tuân thủ theo nguyên tắc đặt tên, bên trong một lớp ta có thể khai báo các biến(thành phần dữ liệu) và các hàm(phương thức)). Trong số các hàm bên trong lớp có một hàm tên là Main, hàm này có đặc điểm khi một chương trình C# được gọi ra thực hiện thì máy sẽ tiến hành thực hiện từ câu lệnh 8
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm đầu tiên của hàm Main và khi màn Main kết thúc thì chương trình C# cũng kết thúc. Điều đó chứng tỏ hàm Main là hàm chính của chương trình C#. Một chương trình C# muốn thực hiện được thì phải có một hàm Main và chỉ có duy nhất một hàm Main trong toàn bộ hệ thống chương trình và hàm này sẽ gọi các hàm khác ra để thực hiện yêu cầu bài toán. Hàm Main được khai báo như sau: static void Main( ) { // Các câu lệnh } Chú ý: - Hàm Main có thể có dạng thể hiện khác chúng ta sẽ tìm hiểu sau - Nếu trong một chương trình C# mà không có hàm Main đặt trong một lớp nào đó thì chương trình C# này không thể thực hiện được 1.4 Mét sè kh¸i niÖm c¬ b¶n Ghi chú: Một chương trình được viết tốt thì cần phải có chú thích các đoạn mã được viết. Các đoạn chú thích này sẽ không được biên dịch và cũng không tham gia vào chương trình. Mục đích chính là làm cho đoạn mã nguồn rõ ràng và dễ hiểu. Trong ví dụ 1.2 có một dòng chú thích : // Xuat ra man hinh. Một chuỗi chú thích trên một dòng thì bắt đầu bằng ký tự “//”. Khi trình biên dịch gặp hai ký tự này thì sẽ bỏ qua dòng đó. Ngoài ra C# còn cho phép kiểu chú thích cho một hay nhiều dòng, và ta phải khai báo “/*” ở phần đầu chú thích và kết thúc chú thích là ký tự “*/”. Ví dụ 1.4 : Minh họa dùng chú thích trên nhiều dòng. class ChaoMung { static void Main() { /* Xuat ra man hinh chuoi ‘chao mung’ Su dung ham WriteLine cua lop System.Console */ System.Console.WriteLine(“Hello World”); } } 9
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Hellp World Ngoài hai kiểu chú thích trên giống trong C/C++ thì C# còn hỗ trợ thêm kiểu thứ ba cũng là kiểu cuối cùng, kiểu này chứa các định dạng XML nhằm xuất ra tập tin XML khi biên dịch để tạo sưu liệu cho mã nguồn. ///XML Chú thích tài liệu 1 dòng định dạng theo XML / XML Chú thích nhiều dòng định dạng theo XML / Ứng dụng Console: Là cách xây dựng ứng dụng giao tiếp với người dùng thông quan bàn phím và không có giao diện người dùng (UI), giống như các ứng dụng thường thấy trong Windows. Trong các chương xây dựng các ứng dụng nâng cao trên Windows hay Web thì ta mới dùng các các giao diện đồ họa. Còn để tìm hiểu về ngôn ngữ C# thuần tuý thì cách tốt nhất là ta viết các ứng dụng Console. Trong hai ứng dụng đơn giản trên ta đã dùng phương thức WriteLine() của lớp Console. Phương thức này sẽ xuất ra màn hình dòng lệnh hay màn hình DOS chuỗi tham số đưa vào, cụ thể là chuỗi “Hello World”. Từ khóa using: Để làm cho chương trình gọn hơn, và không cần phải viết từng namespace(ta tìm hiểu khai niệm này sau) cho từng đối tượng, C# cung cấp từ khóa là using, sau từ khóa này là một namespace hay subnamespace với mô tả đầy đủ trong cấu trúc phân cấp của nó. Ta có thể dùng dòng lệnh : using System; Ở đầu chương trình và khi đó trong chương trình nếu chúng ta có dùng đối tượng Console thì không cần phải viết đầy đủ : System.Console. mà chỉ cần viết Console. thôi. Ví dụ 1.4 Dùng khóa using using System; class ChaoMung { static void Main() { //Xuat ra man hinh chuoi thong bao Console.WriteLine(“Hello World”); } 10
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm } Kết quả: Hello World Lưu ý rằng phải đặt câu using System trước định nghĩa lớp ChaoMung. Ví dụ 1.4: Không hợp lệ trong C#. using System.Console; class ChaoMung { static void Main() { //Xuat ra man hinh chuoi thong bao WriteLine(“Hello World”); } } Đoạn chương trình trên khi biên dịch sẽ được thông báo một lỗi như sau: error CS0138: A using namespace directive can only be applied to namespace; ‘System.Console’ is a class not a namespace. Cách biểu diễn namespace có thể làm giảm nhiều thao tác gõ bàn phím, nhưng nó có thể sẽ không đem lại lợi ích nào bởi vì nó có thể làm xáo trộn những namespace có tên không khác nhau. Giải pháp chung là chúng ta sử dụng từ khóa using với các namespace đã được xây dựng sẵn, các namespace do chúng ta tạo ra, những namespace này chúng ta đã nắm chắc sưu liệu về nó. Còn đối với namespace do các hãng thứ ba cung cấp thì chúng ta không nên dùng từ khóa using. Dấu chấm phẩy(;) : Trong C# quy ước kết thúc mỗi câu lệnh ta phải dùng dấu chấm phẩy( ;) 11
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm 1.5 Visual C# 2005 Express Edition 1.2.1 T¹o mét øng dông Console Để tạo chương trình chào mừng trong IDE, lựa chọn mục Visual Studio .NET trong menu Start hoặc icon của nó trên desktop, sau khi khởi động xong chương trình, chọn tiếp chức năng File New Project trong menu. Chức năng này sẽ gọi cửa sổ New Project (Hình 1.2.1 bên dưới). Nếu như chương trình Visual Studio .NET được chạy lần đầu tiên, khi đó cửa sổ New Project sẽ xuất hiện tự động mà không cần phải kích hoạt. Để tạo ứng dụng, ta lựa chọn mục Visual C# Projects trong cửa sổ Project Type bên trái. Lúc này chúng ta có thể nhập tên cho ứng dụng và lựa chọn thư mục nơi lưu trữ các tập tin này. Cuối cùng, kích vào OK khi mọi chuyện khởi tạo đã chấm dứt và một cửa sổ mới sẽ xuất hiện (Hình 1.2.1’ bên dưới), chúng ta có thể nhập mã nguồn vào đây. Lưu ý rằng Visual Studio .NET tạo ra một namespace dựa trên tên của project mà ta vừa cung cấp (ChaoMung), và thêm vào chỉ dẫn sử dụng namespace System bằng lệnh using, bởi hầu như mọi chương trình mà chúng ta viết đều cần sử dụng các kiểu dữ liệu chứa trong namespace System. Tríc tªn chon môc nµy Sau ®ã chon môc nµy Hình 1.2.1: Tạo ứng dụng C# console trong Visual Studio .NET. 12
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Hình 1.2.1’: Phần soạn thảo mã nguồn cho project. Visual Studio .NET tạo một lớp tên là Class1, lớp này chúng ta có thể tùy ý đổi tên của chúng. Khi đổi tên của lớp, tốt nhất là đổi tên luôn tập tin chứa lớp đó (Class1.cs). Giả sử trong ví dụ trên chúng ta đổi tên của lớp thành ChaoMung, và đổi tên tập tin Class1.cs (đổi tên tập tin trong cửa sổ Solution Explorer). Chúng ta sẽ xoá tất cả cac thư gi mà chương trình tạo cho chung ta và tiến hành viết bài theo ý muốn của chúng ta 1.2.2 Lu mét øng dông Trong một ứng dụng ta có thể tạo ra nhiều tệp tin *.cs. Đê lưu cất các tệp tin này ta có thể lưu toàn bộ sự thay đổi của cac tệp tin lên đĩa hay lưu sự thay đổi của tùng tập tin một, cụ thể như sau NhÊn vµo nót nµy th× lu th«ng tin cña toµn bé c¸c tÖp lªn ®Üa(hay cã NhÊn vµo nót nµy th× lu thÓ Ên th«ng tin cña tÖp hiÖn hµnh ( Ctrl+Shift+S) lªn ®Üa(hay cã thÓ Ên Ctrl +S) 13
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm 1.2.3 Thªm mét tËp tin .cs Để tạo một tệp tin .cs ta vào Project ( Add Class ) 1.2.4 DÞch vµ ch¹y mét øng dông §Ó dÞch vµ ch¹y mét øng dông ta cã thÓ NhÊn F5 (Chay chương trình có Debug) NhÊn Ctrl+F5 (Chạy chương trình không sử dụng Debug) Nhấn F6 (Dich chương trình) 14
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Ch¬ng 2: C¸c thµnh phÇn c¬ b¶n trong ng«n ng÷ C# 2.1 Bé ch÷ viÕt Mỗi ngôn ngữ lập trình đều được xây dựng từ một bộ ký tự nào đó. Các ký tự được nhóm lại theo nhiều cách khác nhau để lập lên các từ. Đến lượt mình các từ được liên kết theo một quy tắc nào đó để tạo thành các câu lệnh. Một chương trình bao gồm nhiều câu lệnh và diễn đạt một thuật toán để giải một bài toán nào đó. Ngôn ngữ C# được xây dựng trên bộ ký tự sau: Các chữ cái hoa: A B C Z Các chữ cái thường: a b c z Các chữ số: 0 1 2 9 Các kí hiệu toán học: + - * / = Các dấu ngoặc: [ ] { } ( ) Các ký hiệu đặc biệt khác: , . ; : / ? @ # $ % ^ & ‘ “ Các dấu ngăn cách không nhìn thấy như dấu cách, dấu nhảy cách tab, dấu xuống dòng Dấu gạch nối dưới: _ 2.2 Tõ kho¸ - Là những từ có một ý nghĩa hoàn toàn xác định trong chương trình: Ví dụ: void struct class while - Không được dùng từ khoá để đặt tên cho các hằng, biến, mảng, hàm - Từ khoá phải viết bằng chữ thường Ví dụ từ khoá viết đùng: struct Ví dụ từ khoá viết sai: Struct 2.3 Tªn(®Þnh danh) Tên gọi của các thành phần trong chương trình được gọi là định danh(Identifier). Định danh được sử dụng để xác định các thành phần như biến , kiểu, phương thức(method) hay còn được gọi là hàm, đối tượng, lớp Trong C# định danh là một dãy các ký tự gồm các chữ cái, chữ số và một số các ký hiệu như: ký tự gạch dưới nối câu ’_’ , ký hiệu tiền tệ $ và không được bắt đầu bằng chữ số. Chú ý: C# phân biệt chữ hoa và chữ thường, ví dụ HUE và hue là hai định danh khác nhau. Độ dài(số ký tự) của định danh trong C# về mặt lý thuyết là không giới hạn. 15
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Ví dụ: - Các định danh viết đúng: bai_1, hue, - Các định danh viết sai: 24gio. bai tap, Để trở thành một nhà lập trình chuyên nghiệp, chúng ta nên sử dụng cách đặt tên theo một chuẩn nhất định để dễ phân biệt được các loại khác nhau của các thành phần sử dụng. Chúng ta qui ước cách đặt tên thống nhất như sau: + Định danh cho các lớp: chữ cái đầu của mỗi từ trong định danh đều viết hoa, ví dụ MyClass, HocSinh + Định danh cho biến, phương thức, đối tượng: chữ cái đầu của mỗi định danh đều được viết hoa trừ từ đầu tiên, ví dụ bienTong, tinhTong, + Định danh cho hằng ta viết hoa, ví dụ: MAX, PI 2.4 C¸c kiÓu d÷ liÖu c¬ b¶n C# là ngôn ngữ lập trình mạnh về kiểu dữ liệu, một ngôn ngữ mạnh về kiểu dữ liệu là phải khai báo kiểu của mỗi đối tượng khi tạo (kiểu số nguyên, số thực, kiểu chuỗi, kiểu điều khiển ) và trình biên dịch sẽ giúp cho người lập trình không bị lỗi khi chỉ cho phép một loại kiểu dữ liệu có thể được gán cho các kiểu dữ liệu khác. Kiểu dữ liệu của một đối tượng là một tín hiệu để trình biên dịch nhận biết kích thước của một đối tượng (kiểu int có kích thước là 4 byte) và khả năng của nó (như một đối tượng button có thể vẽ, phản ứng khi nhấn, ). Tương tự như C++ hay Java, C# chia thành hai tập hợp kiểu dữ liệu chính: Kiểu xây dựng sẵn (built-in) mà ngôn ngữ cung cấp cho người lập trình và kiểu được người dùng định nghĩa (user-defined) do người lập trình tạo ra. C# phân tập hợp kiểu dữ liệu này thành hai loại: Kiểu dữ liệu giá trị (value) và kiểu dữ liệu tham chiếu (reference). Việc phân chi này do sự khác nhau khi lưu kiểu dữ liệu giá trị và kiểu dữ liệu tham chiếu trong bộ nhớ. Đối với một kiểu dữ liệu giá trị thì sẽ được lưu giữ kích thước thật trong bộ nhớ đã cấp phát là stack. Trong khi đó thì địa chỉ của kiểu dữ liệu tham chiếu thì được lưu trong stack nhưng đối tượng thật sự thì lưu trong bộ nhớ heap. Nếu chúng ta có một đối tượng có kích thước rất lớn thì việc lưu giữ chúng trên bộ nhớ heap rất có ích, trong chương 4 sẽ trình bày những lợi ích và bất lợi khi làm việc với kiểu dữ liệu tham chiếu, còn trong chương này chỉ tập trung kiểu dữ kiểu cơ bản hay kiểu xây dựng sẵn. Ghi chú: Tất cả các kiểu dữ liệu xây dựng sẵn là kiểu dữ liệu giá trị ngoại trừ các đối tượng và chuỗi. Và tất cả các kiểu do người dùng định nghĩa ngoại trừ kiểu cấu trúc đều là kiểu dữ liệu tham chiếu 16
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Ngoài ra C# cũng hỗ trợ một kiểu con trỏ C++, nhưng hiếm khi được sử dụng, và chỉ khi nào làm việc với những đoạn mã lệnh không được quản lý (unmanaged code). Mã lệnh không được quản lý là các lệnh được viết bên ngoài nền .MS.NET, như là các đối tượng COM. Kiểu dữ liệu xây dựng sẵn: Ngôn ngữ C# đưa ra các kiểu dữ liệu xây dựng sẵn rất hữu dụng, phù hợp với một ngôn ngữ lập trình hiện đại, mỗi kiểu dữ liệu được ánh xạ đến một kiểu dữ liệu được hỗ trợ bởi hệ thống xác nhận ngôn ngữ chung (Common Language Specification: CLS) trong MS.NET. Việc ánh xạ các kiểu dữ liệu nguyên thuỷ của C# đến các kiểu dữ liệu của .NET sẽ đảm bảo các đối tượng được tạo ra trong C# có thể được sử dụng đồng thời với các đối tượng được tạo bởi bất cứ ngôn ngữ khác được biên dịch bởi .NET, như VB.NET. Mỗi kiểu dữ liệu có một sự xác nhận và kích thước không thay đổi, không giống như C++, int trong C# luôn có kích thước là 4 byte bởi vì nó được ánh xạ từ kiểu Int32 trong .NET. Sau đây chúng ta đi tìm hiểu chi tiết một sô kiểu dữ liệu dựng sẵn có trong C#: 2.4.1 KiÓu nguyªn Dùng để lưu trữ các giá trị nguyên trong giới hạn cho phép tuỳ thuộc vào từng kiểu dữ liệu nguyên cụ thể. Trong ngôn ngữ C# có một số kiểu dữ liệu nguyên sau: Số Kiểu C# Kiểu .NET Mô tả byte byte 1 Byte Số nguyên dương không dấu từ 0-255 char 2 Char Ký tự Unicode sbyte 1 Sbyte Số nguyên có dấu ( từ -128 đến 127) short 2 Int16 Số nguyên có dấu giá trị từ -32768 đến 32767 ushort 2 Uint16 Số nguyên không dấu 0 – 65.535 int 4 Int32 Số nguyên có dấu –2.147.483.647 và 2.147.483.647 uint 4 Uint32 Số nguyên không dấu 0 – 4.294.967.295 long 8 Int64 Kiểu số nguyên có dấu có giá trị trong khoảng : -9.223.370.036.854.775.808 đến 9.223.372.036.854.775.807 ulong 8 Uint64 Số nguyên không dấu từ 0 đến 0xffffffffffffffff 17
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Thông thường để chọn một kiểu dữ liệu nguyên để sử dụng như short, int hay long thường dựa vào độ lớn của giá trị muốn sử dụng. Ví dụ, một biến ushort có thể lưu giữ giá trị từ 0 đến 65.535, trong khi biến ulong có thể lưu giữ giá trị từ 0 đến 4.294.967.295, do đó tùy vào miền giá trị của phạm vi sử dụng biến mà chọn các kiểu dữ liệu thích hợp nhất. Kiểu dữ liệu int thường được sử dụng nhiều nhất trong lập trình vì với kích thước 4 byte của nó cũng đủ để lưu các giá trị nguyên cần thiết. Kiểu số nguyên có dấu thường được lựa chọn sử dụng nhiều nhất trong kiểu số trừ khi có lý do chính đáng để sử dụng kiểu dữ liệu không dấu. Cách tốt nhất khi sử dụng biến không dấu là giá trị của biến luôn luôn dương, biến này thường thể hiện một thuộc tính nào đó có miền giá trị dương. Ví dụ khi cần khai báo một biến lưu giữ tuổi của một người thì ta dùng kiểu byte (số nguyên từ 0-255) vì tuổi của người không thể nào âm được. 2.4.2 KiÓu thùc Dùng để lưu trữ các giá trị thực trong giới hạn cho phép tuỳ thuộc vào từng kiểu dữ liệu thực cụ thể. Trong ngôn ngữ C# có một số kiểu dữ liệu thực sau: Số Kiểu C# Kiểu .NET Mô tả byte float 4 Single Kiểu dấu chấm động, giá trị xấp xỉ từ 3,4E-38 đến 3,4E+38, với 7 chữ số có nghĩa. double 8 Double Kiểu dấu chấm động có độ chính xác gấp đôi, giá trị xấp xỉ từ 1,7E-308 đến 1,7E+308, với 15,16 chữ số có nghĩa decimal 8 Decimal Có độ chính xác đến 28 con số và giá trị thập phân, được dùng trong tính toán tài chính, kiểu này đòi hỏi phải có hậu tố “m” hay “M” theo sau giá trị. Kiểu float, double, và decimal đưa ra nhiều mức độ khác nhau về kích thước cũng như độ chính xác.Với thao tác trên các phân số nhỏ thì kiểu float là thích hợp nhất. Tuy nhiên lưu ý rằng trình biên dịch luôn luôn hiểu bất cứ một số thực nào cũng là một số kiểu double trừ khi chúng ta khai báo rõ ràng. Để gán một số kiểu float thì số phải có ký tự f theo sau. 18
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm float soFloat = 24f; 2.4.3 KiÓu ký tù Kiểu ký tự được thể hiện bằng kiểu char, biểu diễn cho các ký tự mã Unicode gồm các chữ cái, chữ số và các ký tự đặc biệt. Kiểu char có 65536(216) ký tự trong tập mã Unicode 16 bit. Mã của 128 ký tụ đầu của tập Unicode hoàn toàn trùng với mã của 128 ký tự trong tập mã ASCII 7-bit và mã của 256 ký tự ban đầu hoàn toàn tương ứng với 256 ký tự của tập mã ISO Latin-1 8-bit. Số Kiểu C# Kiểu .NET Mô tả byte char 2 Char Ký tự Unicode 2.4.5 KiÓu logic Dùng để biểu diễn các giá trị logic và chỉ chứa một trong hai giá trị true và false Số Kiểu C# Kiểu .NET Mô tả byte bool 1 Boolean Giá trị logic true/ false 2.4.6 KiÓu x©u(chuçi) Kiểu dữ liệu chuỗi khá thân thiện với người lập trình trong bất cứ ngôn ngữ lập trình nào, kiểu dữ liệu chuỗi lưu giữ một dãy các ký tự. Để khai báo một chuỗi chúng ta sử dụng từ khoá string tương tự như cách tạo một thể hiện của bất cứ đối tượng nào: string tenchuoi; Một hằng chuỗi được tạo bằng cách đặt các chuỗi trong dấu nháy đôi: “Xin chao” Đây là cách chung để khởi tạo một chuỗi ký tự với giá trị hằng: string chuoi = “Xin chao”; Kiểu chuỗi sẽ được đề cập sâu trong phần sau 2.5 BiÕn vµ h»ng 2.5.1) BiÕn Biến là yếu tố cơ bản của bất kỳ ngôn ngữ máy tính nào. Biến là vùng trống trong bộ nhớ máy tính dành cho một kiểu dữ liệu nào đó và có đặt tên. Các biến trong bộ nhớ ở các thời điểm khác nhau có thể cất giữ các giá trị khác nhau. Trước khi sử dụng một biến nào đó phải khai báo nó. Quy tắc khai báo: 19
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Khai báo biến không có giá trị khởi đầu Kiểu_dữ_liệu Tên_biến ; Có thể khai báo nhiều biến cùng kiểu trên cùng một hàng, các tên biến được phân cách nhau bằng dấu phẩy Ví dụ: int a,b; /*biến có kiểu nguyên*/ float f; /*biến thực*/ char ch; /*biến ký tự*/ Khai báo biến có giá trị khởi đầu Kiểu_dữ_liệu Tên_biến=giá trị ; Ví dụ: int a =5; float b=6; char ch=’A’; hoặc char ch=’\u0041’ Chú ý: Trong C# trước khi chúng ta muốn sử dụng một biến ta phải khởi gán cho nó một giá trị cụ thể, nếu không chương trình dịch sẽ báo lỗi VÝ dô: using System; class VD { static void Main() { int a, b=1; float t; t = a + b; a = 2; Console.WriteLine("Tong {0}+{1}={2}", a, b,t); } } Chương trình trên sẽ bị báo lỗi vì biến a chưa được khởi tạo giá trị trước khi sử dụng Để chương trình trên có thể chạy được ta sửa lại như sau: 20
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm using System; class VD { static void Main() { int a=2, b=1; float t; t = a + b; Console.WriteLine("Tong {0}+{1}={2}", a, b,t); } } 2.5.2. H»ng sè Hằng cũng là một biến nhưng giá trị của hằng không thay đổi. Biến là công cụ rất mạnh, tuy nhiên khi làm việc với một giá trị được định nghĩa là không thay đổi, ta phải đảm bảo giá trị của nó không được thay đổi trong suốt chương trình. Ví dụ, khi lập một chương trình thí nghiệm hóa học liên quan đến nhiệt độ sôi, hay nhiệt độ đông của nước, chương trình cần khai báo hai biến là DoSoi và DoDong, nhưng không cho phép giá trị của hai biến này bị thay đổi hay bị gán. Để ngăn ngừa việc gán giá trị khác, ta phải sử dụng biến kiểu hằng. a/ H»ng sè nguyªn - Hệ thập phân bình thường VD: 545 - Hệ cơ số 16 (Hecxa) Bắt đầu bằng 0x, 0X Ví dụ: 0xAB = 16310 b/ H»ng sè thùc Được viết theo hai cách sau: - Dạng thập phân gồm: Phần nguyên, dấu chấm thập phân, phần thạp phân Ví dụ: 34.2 -344.122 - Dạng khoa học(dạng mũ) gồm: Phần định trị và phần mũ. Phần định trị là số nguyên hay số thực dạng thập phân, phần mũ bắt đầu bằng E hoặc e theo sau là số nguyên 21
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Ví dụ: 1234.54E-122 c/ H»ng ký tù Là một ký hiệu trong bảng mã Unicode được đặt trong hai dấu nháy đơn. Giá trị của hằng kí tự chính là mã Unicode của kí hiệu Ví dụ: Hằng ‘A’ có giá trị là 65 Chú ý: Hằng ký tự biểu thị mã của ký tự đó trong bảng mã Unicode. Do vậy một hằng ký tự cũng có thể tham gia vào các phép toán. Ví dụ: ‘A’+10 có giá trị (65+10=75) Hằng ký tự còn có thể được viết theo cách: ‘\uc1c2c3c4’ trong đó c1c2c3c4 là một số hệ 16 mà giá trị của nó chính là mã Unicode của ký tự cần biểu diễn. Ví dụ: ‘A’ hay ‘\u0041’ Một số ký tự đặc biệt: Viết Diễn giải \’ Dấu nháy đơn \” Dấu nháy kép \\ Dấu gạch chéo ngược \n Xuống dòng mới \0 Ký tự Null \t Nhảy cách ngang, ký tự tab \b Xoá trái \r Về đầu dòng \f Sang trang d/ H»ng x©u ký tù Là một dãy các ký tự đặt trong hay dấu nháy “ ” g) Khai b¸o h»ng const kieu_du_kieu ten_hang = gia_tri_hang; Một hằng phải được khởi tạo khi khai báo, và chỉ khởi tạo duy nhất một lần trong suốt chương trình và không được thay đổi. Ví dụ: const int DOSOI = 100; 22
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Trong khai báo trên, 100 là một hằng số và DOSOI là tên hằng có kiểu nguyên. Ví dụ: minh họa việc sử dụng những biểu tượng hằng. class MinhHoa { static void Main() { const int DOSOI = 100; // Độ C const int DODONG = 0; // Độ C System.Console.WriteLine( “Do dong cua nuoc {0}”, DODONG ); System.Console.WriteLine( “Do soi cua nuoc {0}”, DOSOI ); } } Kết quả: Do dong cua nuoc 0 Do soi cua nuoc 100 2.6 C¸c phÐp to¸n 2.6.1 PhÐp to¸n sè häc PhÐp to¸n ý nghÜa VÝ dô - §æi dÊu mét sè thùc hoÆc nguyªn -12, -a + PhÐp céng 2 sè thùc hoÆc nguyªn 2+4=6 - PhÐp trõ 2-3=-1 * PhÐp nh©n 4*2=8 / PhÐp chia 5/3=1 6/2=0 % PhÐp lÊy phÇn d 11.5%2.5=1.5 23
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Chó ý: - NÕu phÐp chia hai to¸n h¹ng ®Òu nguyªn th× phÐp chia cho kÕt qu¶ lµ phÇn nguyªn cña th¬ng hai to¸n h¹ng ®ã, vÝ dô: 5/3=1 - NÕu phÐp chia hai to¸n h¹ng ®Òu nguyªn víi sè chia b»ng 0 th× phÐp chia sinh lçi, vÝ dô 5/0 sÏ ph¸t sinh lçi - NÕu mét trong hai to¸n h¹ng lµ kiÓu thùc th× lóc nµy kÕt qu¶ cña phÐp chia cho ta gi¸ trÞ ®óng, vÝ dô 5/2.0=2.5 - NÕu mét trong hai to¸n h¹ng lµ kiÓu thùc víi sè chÜa b»nd 0 th× kÕt qu¶ phÐp chia lµ infinity(d¬ng v« cïng) hoÆc –infinity(©m v« cïng) - PhÐp to¸n % tr¶ vÒ phÇn d cña phÐp chia 2.6.2 PhÐp to¸n quan hÖ Những toán tử quan hệ được dùng để so sánh giữa hai giá trị, và sau đó trả về kết quả là một giá trị logic kiểu bool (true hay false). Ví dụ toán tử so sánh lớn hơn (>) trả về giá trị là true nếu giá trị bên trái của toán tử lớn hơn giá trị bên phải của toán tử. Do vậy 5 > 2 trả về một giá trị là true, trong khi 2 > 5 trả về giá trị false. Các toán tử quan hệ trong ngôn ngữ C# được trình bày ở bảng bên dưới. Phép toán ý nghĩa Ví dụ Kết quả > So sánh lớn hơn 1>2 false >= So sánh lớn hơn hoặc bằng 2>=2 true 1) false Liên kết hai biểu thức logic && Phép và (and). Giá trị bằng 1 (2>1)&&(5=2) false khi cả 2 toán hạng có giá trị 1 24
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Liên kết hai biểu thức logic. Phép hoặc (or). Giá trị biểu || (4>3)||(1>8) true thức bằng 1 khi một trong hai toán hạng bằng 1 Bảng giá trị của các phép toán logic X Y X && Y X || Y ! X true true true true false true false false true false false true false true true false false false false true 2.6.4 PhÐp to¸n t¨ng gi¶m Trong ngôn ngữ lập trình C# đưa ra hai phép toán một ngôi để tăng và giảm các biến (nguyên và thực). Toán tử tăng ++ sẽ thêm 1 vào toán hạng của nó, toán tử giảm – sẽ trừ đi 1. Dấu phép toán ++ và có thể đứng trước hoặc đứng sau toán hạng. Như vậy ta có thể viết: ++n, n++, n, n Sự khác nhau của ++n và n++ ở chỗ: Trong phép toán n++ thì n tăng sau khi giá trị của nó được sử dụng, còn trong ++n thì giá trị của n tăng trước khi giá trị của nó được sử dụng. Tương tự đối với –n và n Ví dụ: giả sử trước mỗi phép tính int i=3, j=15; Phép toán Tương đương Kết quả i=++j; tăng trước j=j+1; i=j; i=16 và j=16 i=j++; tăng sau i=j; j=j+1; i=15 và j=16 i++; i=i+1; i=4 j = ++i + 5; i=i+1; j=i+5; i=4 và j=9 j = i++ +5; j=i+5; i=i+1; i=4 và j=8 2.6.5 Thø tù u tiªn c¸c phÐp to¸n Trình biên dịch phải xác định thứ tự thực hiện các toán tử trong trường hợp một biểu thức có nhiều phép toán, giả sử, có biểu thức sau: var1 = 5+7*3; 25
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Biểu thức trên có ba phép toán để thực hiện bao gồm (=, +,*). Ta thử xét các phép toán theo thứ tự từ trái sang phải, đầu tiên là gán giá trị 5 cho biến var1, sau đó cộng 7 vào 5 là 12 cuối cùng là nhân với 3, kết quả trả về là 36, điều này thật sự có vấn đề, không đúng với mục đích yêu cầu của chúng ta. Do vậy việc xây dựng một trình tự xử lý các toán tử là hết sức cần thiết. Các luật về độ ưu tiên xử lý sẽ bảo trình biên dịch biết được toán tử nào được thực hiện trước trong biểu thức.Tương tự như trong phép toán đại số thì phép nhân có độ ưu tiên thực hiện trước phép toán cộng, do vậy 5+7*3 cho kết quả là 26 đúng hơn kết quả 36. Và cả hai phép toán cộng và phép toán nhân điều có độ ưu tiên cao hơn phép gán. Như vậy trình biên dịch sẽ thực hiện các phép toán rồi sau đó thực hiện phép gán ở bước cuối cùng. Kết quả đúng của câu lệnh trên là biến var1 sẽ nhận giá trị là 26. Trong ngôn ngữ C#, dấu ngoặc được sử dụng để thay đổi thứ tự xử lý, điều này cũng giống trong tính toán đại số. Khi đó muốn kết quả 36 cho biến var1 có thể viết: var1 = (5+7) * 3; Biểu thức trong ngoặc sẽ được xử lý trước và sau khi có kết quả là 12 thì phép nhân được thực hiện. Bảng sau liệt kê thứ tự độ ưu tiên các phép toán trong C#. 26
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Các phép toán được liệt kê cùng loại sẽ có thứ tự theo mục thứ thự của bảng: thứ tự trái tức là độ ưu tiên của các phép toán từ bên trái sang, thứ tự phải thì các phép toán có độ ưu tiên từ bên phải qua trái. Các toán tử khác loại thì có độ ưu tiên từ trên xuống dưới, do vậy các toán tử loại cơ bản sẽ có độ ưu tiên cao nhất và phép toán gán sẽ có độ ưu tiên thấp nhất trong các toán tử. 2.7 BiÓu thøc 2.7.1 BiÓu thøc sè häc Biểu thức số học đó là sự kết hợp giữa các toán hạng với các toán tử số học và cho ta kết quả là một số(số thực hoặc số nguyên) Ví dụ: (3+5)*6/4-2.4 2.7.8 BiÓu thøc logic Biểu thức logic đó là sự kết hợp giữa các toán hạng với các toán tử(toán tử số học, toán tử quan hệ, toán tử logic) v à kết quả cho ta là một giá trị logic. Biểu thức logic được dùng làm điều kiện trong các cấu trúc điều khiển(if, while, ) và một số trường hợp khác. Ví dụ: (1>=2)&&(5==4) 2.7.9 BiÓu thøc ®iÒu kiÖn Hầu hết các toán tử đòi hỏi có một toán hạng như toán tử (++, ) hay hai toán hạng như (+,-,*,/, ). Tuy nhiên, C# còn cung cấp thêm một toán tử có ba toán hạng (?:). Toán tử này có cú pháp sử dụng như sau: ? : Toán tử này sẽ xác định giá trị của một biểu thức điều kiện, và biểu thức điều kiện này phải trả về một giá trị kiểu bool. Khi điều kiện đúng thì sẽ được thực hiện, còn ngược lại điều kiện sai thì sẽ được thực hiện. Có thể diễn giải theo ngôn ngữ tự nhiên thì toán tử này có ý nghĩa : “Nếu điều kiện đúng thì làm công việc thứ nhất, còn ngược lại điều kiện sai thì làm công việc thứ hai”. Cách sử dụng toán tử ba ngôi này được minh họa trong ví dụ sau. Ví dụ: Sử dụng toán tử bao ngôi. using System; class Tester { public static void Main() { int value1; 27
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm int value2; int maxValue; value1 = 10; value2 = 20; maxValue = value1 > value2 ? value1 : value2; Console.WriteLine(“Gia tri thu nhat {0}, gia tri thu hai {1}, gia tri lon nhat {2}”, value1, value2, maxValue); } } Kết quả: Gia tri thu nhat 10, gia tri thu hai 20, gia tri lon nhat 20 Trong ví dụ minh họa trên toán tử ba ngôi được sử dụng để kiểm tra xem giá trị của value1 có lớn hơn giá trị của value2, nếu đúng thì trả về giá trị của value1, tức là gán giá trị value1 cho biến maxValue, còn ngược lại thì gán giá trị value2 cho biến maxValue. 2.7.10 BiÓu thøc g¸n Trong C# dùng dấu “=” làm dấu phép gán. Biểu thức gán có thể được thể hiện dưới các dạng như sau: Biến = Biểu_thức Biến op = Biểu_thức Cá ch viết dưới tương đương Biến = (biến) op (Biểu_thức) trong đó op là một toán tử nào đó. Giá trị của Biểu_thức sẽ được gán cho biến sau câu lệnh này Ví dụ: x + = y tương đương với x = x+y Nếu ta thêm dấu ; vào sau biểu thức gán sẽ thu được một câu lệnh gán Biểu thức gán có thể được sử dụng trong các phép toán và các câu lệnh như các biểu thức thông thường. Chẳng hạn khi viết: a=b=5; thì điều đó có nghĩa rằng gán giá trị của biểu thức b=5 cho biến a. Kết quả là b=5 và a=5. Tương tự sau câu lệnh: x = (a=5) * (b=10); sẽ gán 5 cho a, 10 cho b và sau đó gán tiếp 50 cho x 2.7.11 BiÓu thøc Ðp kiÓu Những đối tượng của một kiểu dữ liệu này có thể được chuyển sang những đối tượng của một kiểu dữ liệu khác thông qua cơ chế chuyển đổi tường minh hay ngầm định. Chuyển đổi nhầm định được thực hiện một cách tự động, 28
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm trình biên dịch sẽ thực hiện công việc này. Còn chuyển đổi tường minh diễn ra khi chúng ta gán ép một giá trị cho kiểu dữ liệu khác. Việc chuyển đổi giá trị ngầm định được thực hiện một cách tự động và đảm bảo là không mất thông tin và tuân theo quy tắc sau: byte short int long float double char Ví dụ, chúng ta có thể gán ngầm định một số kiểu short (2 byte) vào một số kiểu int (4 byte) một cách ngầm định. Sau khi gán hoàn toàn không mất dữ liệu vì bất cứ giá trị nào của short cũng thuộc về int: short x = 10; int y = x; // chuyển đổi ngầm định Tuy nhiên, nếu chúng ta thực hiện chuyển đổi ngược lại, chắc chắn chúng ta sẽ bị mất thông tin. Nếu giá trị của số nguyên đó lớn hơn 32.767 thì nó sẽ bị cắt khi chuyển đổi. Trình biên dịch sẽ không thực hiện việc chuyển đổi ngầm định từ số kiểu int sang số kiểu short: short x; int y = 100; x = y; // Không biên dịch, lỗi !!! không bị lỗi chúng ta phải dùng lệnh gán tường minh, đoạn mã trên được viết lại short x; int y = 100; x = (short) y; // Ép kiểu tường minh, trình biên dịch không báo lỗi Trong một biểu thức mà có nhiều kiểu dữ liệu khác nhau, trước khi tính toán máy sẽ chuyển từ kiểu thâp hơn lên kiểu cao hơn theo sơ đồ như ở trên. Ví dụ: short x=1,y; y=4-x; /*Khi biên dịch sẽ bị lỗi, vì 4-x sẽ cho kiểu int và không thể gán cho biến kiểu short*/ Nếu ta viết y=(short)(4-y);/* Khi biên dịch sẽ không bị lỗi, vì 4-x cho ta kiểu int và sau đó được chuyển thành kiểu short*/ Vì vậy biểu thức ép kiểu tường minh là: (kiểu dữ liệu)Biểu thức Chú ý: Khi ép kiểu thì bản thân biểu thức không bị thay đối kiểu mà chỉ có giá trị của biểu thức ép kiểu mới thay đổi 29
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm 2.8 Mét sè hµm thêng dïng Các hàm toán học: Để sử dụng được các hàm toán học dưới đây thì trước mỗi hàm ta phải đưa thêm Math. trước mỗi tên hàm cần sử dụng: Hàm Chức năng Abs(x) Trả về giá trị tuyệt đối của một số thực, nguyên, Cos(x) Trả về giá trị của Cos(x) Sin(x) Trả về giá trị của Sin(x) Exp(x) Trả về giá trị của ex Log(x) Trả về giá trị của Ln(x) Log10(x) Trả về giá trị của log10(x) Pow(x,y) Trả về giá trị của xy Round(x,n) Làm tròn số thực x với độ chính xác là n chữ số phập phân Sqrt(x) Trả về căn bậc hai của x Floor(x) Trả về gía trị lớn nhất hay bằng giá trị đưa ra Các hàm chuyển đổi: Để sử dụng được các hàm chuyển đổi dưới đây thì trước mỗi hàm ta phải đưa thêm Convert. trước mỗi tên hàm cần sử dụng: Hàm Chức năng ToDouble(x) Chuyển đối tượng x thành kiểu thực double ToInt32(x) Chuyển đối tươợng x thành kiểu int ToString(x) Chuyển đối tượng x thành xâu Trên đây là một số hàm thường dùng nhất, ngoài ra còn có rất nhiều hàm rất hữu dụng khác đòi hỏi các bạn phải tìm hiểu 2.9 NhËp/xuÊt d÷ liÖu 2.9.1 §a d÷ liÖu ra mµn h×nh Để đưa dữ liệu ra màn hình chúng ta dùng phương thức Write( ) hoặc WriteLine( ) trong lớp Console. - Ta có thể đưa ra màn hình một số nguyên, thực, ký tự, xâu ký tự, logic trực tiếp như sau: int x=5; 30
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Console.WriteLine(4); //Xuất một số nguyên ra màn hình Console.WriteLine(4.6); //Xuất một số thực ra màn hình Console.WriteLine(4.6+x);//Xuất một biểu thức có gia trị thực ra màn hình - Ta có thể đưa ra màn hình kết hợp của nhiều kiểu dữ liệu khác nhau ( nguyên, thực, ký tự, xâu ký, logic) theo các cách khác nhau thông qua ví dụ sau: int a=4,b=5,c=2; int max=(a>b?a:b)>c?(a>b?a:b):c; Console.WriteLine(“Max(“ + b + “,” + a + “,” + c + “)=” + max); hoặc ta có thể viết như sau: int a=4, b=5, c=2; int max=(a>b?a:b)>c?(a>b?a:b):c; Console.WriteLine(“Max({1}, {0},{2})={3}”,a,b,c, max); ở cách thứ hai này ta có thể phát biểu một cách tổng quát như sau: Console.Write(dòng diều khiển[, danh sách đối]); Console.WriteLine(dòng diều khiển[, danh sách đối]); Trong đó: dòng diều khiển: là một hằng xâu ký tự đặt trong hai dấu “ ”, nó bao gồm các loại đối tượng sau: Các ký tự thông thường, các ký tự đặc biệt và các đặc tả có dạng: {i,-j:dt } có thể đặt bất kỳ ở vị trí nào trong xâu với: i là thứ tự của một đối mà ta cần đưa giá trị của chúng ra tại vị trí đặt đặc tả(các đối có thứ tự bắt đầu từ không), j là độ rộng dành cho đối cần đưa ra(nếu j mà lớn hơn độ dài của dữ liệu cần đưa ra thì giá trị đó được căn phải, nếu muốn căn trái thì ta thêm dấu trừ phía trước, còn nếu j mà nhỏ hơn độ dài thực tế của dữ liệu cần đưa ra thì không có gì thay đổi), d là định dạng dữ liệu đưa ra, ví dụ định dạng là C định dạng theo kiểu tiền tệ, N định dạng kiểu số, G định dạng chuẩn , t là số chữ số thập phân(chỉ áp dụng cho số thực) danh sách các đối: các đối có thể là hằng, biến, biểu thức và đặt cách nhau một dấu phẩy Sự khác nhau giữa Write( ) và WriteLine( ) là: Write( ) sau khi viết dữ liệu ra màn hình thì con trỏ đặt ở cuối dòng còn WriteLine( ) sau khi viết dữ liệu ra màn hình thì con trỏ đặt ở đầu dòng tiếp theo 31
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Xét vị dụ: using System; class ViDu{ static void Main(){ double a, b, c,max; a = 4.5643; b = 3.234; c = 2.724; max=(a>b?a:b)>c?(a>b?a:b):c; Console.Write("\n\n\t\tCHUONG TRINH TIM MAX CUA BA SO\n"); Console.WriteLine("\tMax({0,-6:N2},{1,-6:N2},{2,-6:N2})={3,6:N2}", a, b, c, max); Console.ReadKey(); } } 2.9.2 NhËp d÷ liÖu vµo tõ bµn phÝm Để nhập dữ liệu vào từ bàn phím chúng ta dùng phương thức ReadLine có trong lớp Console như sau: Kiểudữliệu Tênbiến; Tênbiến=Kiểudữliệu.Parse(Console.ReadLine()); Thông thường trong qua trình nhập dữ liệu chúng ta thương kết hợp phương thức Write với ReadLine trong lớp Console. Xét ví dụ: using System; class ViDu{ static void Main(){ double a, b, c,max; Console.Write("\n\n\t\tCHUONG TRINH TIM MAX CUA BA SO\n"); Console.Write("Nhap a="); a = double.Parse(Console.ReadLine()); Console.Write("Nhap b="); b = double.Parse(Console.ReadLine()); Console.Write("Nhap c="); c = double.Parse(Console.ReadLine()); max=(a>b?a:b)>c?(a>b?a:b):c; Console.WriteLine("\n\tMax({0,-6:N2},{1,-6:N2},{2,-6:N2})={3,6:N2}", a, b, c, max); 32
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Console.ReadKey(); } } Chú ý: - Hàm Console.ReadLine() yêu cầu chúng ta nhập vào từ bàn phím một dãy ký tự và kết thúc quá trình nhập bằng cách nhấn phím Enter() - Để nhập một xâu ký tự chúng ta thực hiện các bước như sau: string s; . s=Console.ReadLine(); 33
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Ch¬ng 3: C¸c cÊu tróc ®iÒu khiÓn ch¬ng tr×nh 3.1 Khèi lÖnh Một dãy các câu lệnh được bao bởi các dấu { } gọi là một khối lệnh. Ví dụ: { a=2; b=3; Console.Write(“a={0}\nb={1}},a,b); } C# xem khối lệnh cũng như một câu lệnh riêng lẻ. Nói cách khác, chỗ nào viết được một câu lệnh thì ở đó cũng có quyền đặt một khối lệnh. Khai báo ở đầu khối lệnh : Trong C# việc khai báo các biến và mảng có thể ở bất cứ chỗ nào chương trình và phải bên trong một lớp miễn là trước khi sử dụng phải khai báo chúng. Nhưng một thói quen lập trình tốt là chúng ta nên khai báo chúng ở đầu các khôi lệnh, đầu các hàm { int a,b; float x,y,z; a=b=3; x=5.5; y=a*x; z=b*x; } Sự lồng nhau của các khối lệnh và phạm vi hoạt động của các biến và mảng: Bên trong một khối lệnh lại có thể viết lồng khối lệnh khác. Sự lồng nhau theo cách như vậy là không hạn chế. Khi máy bắt đầu làm việc với một khối lệnh thì các biến và mảng khai báo bên trong nó mới được hình thành và được cấp phát bộ nhớ. Các biến này chỉ tồn tại trong thời gian máy làm việc bên trong khối lệnh và chúng lập tức bị giải phóng ngay sau khi máy ra khỏi khối lệnh. Vậy : Giá trị của một biến hay một mảng khai báo bên trong một khối lệnh không thể đưa ra sử dụng ở bất kỳ chỗ nào bên ngoài khối lệnh đó. Ở bất kỳ chỗ nào bên ngoài một khối lệnh ta không thể can thiệp đến các biến và các mảng được khai báo bên trong khối lệnh 34
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Nếu có một biến đã được khai báo ở ngoài một khối lệnh và không trùng tên với các biến khai báo bên trong khối lệnh này thì biến đó cũng có thể sử dụng cả bên trong cũng như bên ngoài khối lệnh. Ví dụ : Xét đoạn chương trình sau : { int a=5,b=2; { int c=4; b=a+b; Console.Write(“\nc ={0,3 :N2}\t b={1,3:N2}”,c,b) ; } Console.Write("\na ={0,3:N2}\t b={1,3:N2}",a,b); } 3.2 C¸c cÊu tróc rÏ nh¸nh 3.2.1. CÊu tróc rÏ nh¸nh if a/ CÊu tróc rÏ nh¸nh if d¹ng khuyÕn - Cú pháp câu lệnh if (btđk) công_việc; Trong đó: * if là từ khoá * btđk là một biểu thức cho giá trị logic * Công_việc có thể là một lệnh đơn, một khối lệnh hay một cấu trúc điều khiển nào đó - Sơ đồ cú pháp False bt®k True C«ng_viÖc; 35
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm - Nguyên tắc hoạt động: Đầu tiên máy tính toán giá trị của btđk. Nếu btđk có giá trị True thì máy tiến hành thực hiện Công_việc sau đó tiến hành thực hiện các câu lệnh tiếp theo sau câu lệnh if. Nếu btđk có giá trị là False thì máy bỏ qua việc thực hiện Công_việc trong câu lệnh if mà tiến hành thực hiện ngay các câu lệnh sau câu lệnh if. c/ CÊu tróc rÏ nh¸nh if d¹ng ®Çy ®ñ - Cú pháp câu lệnh if (btđk) công_việc1; else công_việc2; Trong đó: * if, else là từ khoá * btđk là một biểu thức cho giá trị logic * Công_việc1,Công_việc2 có thể là một lệnh đơn, một cấu trúc điều khiển nào đó hay một khối lệnh - S¬ ®å thùc hiÖn False bt®k True C«ng_viÖc1 C«ng_viÖc2 - Nguyên tắc hoạt động: Đầu tiên máy tính toán giá trị của btđk. Nếu btđk có giá trị True thì máy tiến hành thực hiện Công_việc1 sau đó tiến hành thực hiện các câu lệnh tiếp theo sau câu lệnh if. Nếu btđk có giá trị là False thì máy tiến hành thực hiện công_việc2 sau đó tiến hành thực hiện các câu lệnh tiếp theo sau câu lệnh if. Ví dụ: Lập chương trình giải phương trình bậc hai ax2+bx+c=0 using System; class PTB2 36
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm { static void Main() { float a, b, c, delta; Console.WriteLine("Ban hay nhap vao ba so"); Console.Write("a="); a = float.Parse(Console.ReadLine()); Console.Write("b="); b = float.Parse(Console.ReadLine()); Console.Write("c="); c = float.Parse(Console.ReadLine()); if (a == 0) { Console.Write("Day la phuong trinh bac nhat Ax+C=0\n"); if (b != 0) Console.WriteLine("Phuong trinh co nghien duy nhat x={0}", -b / c); else if (c == 0) Console.WriteLine("Phuong trinh vo so nghiem"); else Console.WriteLine("phuong trinh vo nghiem"); } else { Console.WriteLine("Day la phuong trinh bac hai Ax^2+Bx+C=0"); delta = b * b - 4 * a * c; if (delta 0) { Console.WriteLine("Phuong trinh co hai nghiem phan biet:"); Console.WriteLine("x1={0,8:N2}\nx2={1,8:N2}", (-b - Math.Sqrt(delta)) / (2 * a), (-b + Math.Sqrt(delta)) / (2 * a)); } } Console.ReadKey(); 37
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm } } 3.2.2. CÊu tróc rÏ nh¸nh switch - Cú pháp câu lệnh switch ( bieu_thuc) { case e1:Khối_lệnh_1;[break;] case e2: Khối_lệnh_2;[break;] case en: Khối_lệnh_n;[break;] [default: Khối_lệnh_n+1; break;] } Trong đó: + switch, case, default là các từ khoá + bieu_thuc: là một biểu thức cho giá trị nguyên hoặc là xâu + ei:là giá trị nguyên mà biểu thức có thể nhận được. + Những phần đặt trong hai dấu [ và ] có thể có hoặc không - Sự hoạt động của cấu trúc điều khiển switch phụ thuộc vào giá trị của bieu_thuc * Khi giá trị này bằng ei máy sẽ nhảy tới khối lệnh có nhãn case ei và thực hiện Khối_lệnh_i. Nếu Khối_lệnh_i là rỗng thì ta có thể đặt break sau Khối_lệnh_i hoặc không, với trường hợp không có break thì máy sẽ tiến hành nhảy xuống thực hiện Khối_lệnh_(i+1). Nếu Khối_lệnh_i khác rỗng(tức là có công việc phải thực hiện) thì sau Khối_lệnh_i ta phải đặt câu lệnh break. Khi máy gặp câu lệnh break thì máy sẽ thoát khỏi cấu trúc switch và thực hiện các câu lệnh tiếp theo sau cấu trúc lệnh này. * Khi giá trị của bieu_thuc khác tất cả các giá trị ei thì cách làm việc của máy lại phụ thuộc vào sự có mặt hay không có mặt của default. Khi có default máy nhảy tới câu lệnh có nhãn default. Khi không có default máy tiến hành thực hiện các câu lệnh sau cấu trúc này. - Ví dụ áp dụng: Nhập vào một tháng của một năm bất ký sau đó cho biết tháng đó có bao nhiêu ngày: using System; class Songay 38
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm { static void Main() { int month, year,sumday; Console.Write("Nhap thang="); month = int.Parse(Console.ReadLine()); Console.Write("Nhap year="); year = int.Parse(Console.ReadLine()); switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: sumday=31;break; case 4: case 6: case 9: case 11: sumday = 30; break; case 2: if (year % 400==0 || (year % 4 == 0 && year % 100 != 0)) sumday = 29; else sumday = 28; break; default: sumday = 0; break; } if (sumday > 0) Console.Write("So ngay cua {0}/{1} la {2} ngay", month, year, sumday); Console.ReadKey(); } } 3.3. C¸c cÊu tróc lÆp 3.3.1 CÊu tróc lÆp while 39
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm - Cú pháp câu lệnh while(btđk) Công_việc; Trong đó: * while là từ khoá * btđk là một biểu thức cho giá trị logic * Công_việc có thể là một lệnh đơn, một cấu trúc điều khiển nào đó hay một khối lệnh. - Sơ đồ cú pháp False bt®k True C«ng_viÖc - Sự hoạt động của câu lệnh while được tiến hành lần lượt được tiến hành theo các bước sau: Bước 1: Tiến hành tính toán giá trị của btđk. Bước 2: Nếu biểu thức điều kiện có giá trị là False máy sẽ thoát khỏi chu trình và tiến hành thực hiện các câu lệnh sau câu lệnh while. Nếu biểu thức điều kiện có giá trị là True máy sẽ tiến hành thực hiện Công_việc và quay về bước 1. - Ví dụ áp dụng Ví dụ 1: Nhập vào hai số nguyên bất ký và cho biết ước số chung lớn nhất của hai số nguyên đó. using System; class VD { static void Main() { int a, b; Console.Write("Nhap a="); a = int.Parse(Console.ReadLine()); Console.Write("Nhap b="); b = int.Parse(Console.ReadLine()); 40
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm a=Math.Abs(a);b=Math.Abs(b); while (a != b) { if (a > b) a -= b; if (b > a) b -= a; } Console.Write("Uscln la:{0}", a);// hoac Console.Write("Uscln la:{0}", b); Console.ReadKey(); } } 3.3.2 CÊu tróc lÆp do while - Cú pháp câu lệnh do { Công việc cần thực hiện; }while(btđk); Trong đó: - while ,do là từ khoá - btđk là một biểu thức cho giá trị logic - Sơ đồ cú pháp C«ng_viÖc False bt True - Sự hoạt động của câu lệnh do while được tiến hành lần lượt theo các bước sau: Bước 1: Thực hiện Công_việc Bước 2: Sau khi thực hiện xong Công_việc máy tiến hành tính toán giá trị của btđk. 41
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Nếu btđk có giá trị True máy sẽ trở lại bước 1 để tiếp tục thực hiện vòng lặp mới của chu trình. Nếu btđk có giá trị bằng False máy sẽ ra khỏi chu trình và chuyển tới câu lệnh đứng sau cấu trúc do while. - Ví dụ áp dụng Ví dụ 1: Bài toán gửi tiền tiết kiệm, giả sử ta có số tiền là a gửi vào ngân hàng. Hỏi sau bao nhiêu tháng ta thu được số tiền là b(b>a) biết rằng lãi xuất hàng tháng là 5% using System; class VD1 { static void Main() { double a, b; int t=0; do { Console.Write("Nhap so tien ban co:"); a = double.Parse(Console.ReadLine()); if (a < 0) Console.Write("Ban nhap sai, hay nhap la"); } while (a < 0); do { Console.Write("Nhap so tien ban du dinh muon co:"); b = double.Parse(Console.ReadLine()); if (b < a) Console.Write("Ban nhap sai, hay nhap lai"); } while (b < a); // Di tim thoi gian can thiet do { a = a + a * 0.05; t = t + 1; } while (a < b); Console.Write("Ban phai mat {0} nam {1} thang", t / 12, t % 12); 42
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Console.ReadKey(); } } Ví dụ 2: Nhập vào một số nguyên dương sau đó phân tích số nguyên đó ra thừa số nguyên tố using System; class VD2 { static void Main() { int n, i; i = 2;// la so nguyen to dau tien Console.Write("Nhap n="); n = int.Parse(Console.ReadLine()); Console.Write("n="); do { while (n % i == 0) { Console.Write("{0}*", i); n = n / i; } if (i == 2) i = 3; else i = i + 2; } while (n != 1); Console.Write("\b "); Console.ReadKey(); } } 3.3.3 CÊu tróc lÆp for - Cú pháp câu lệnh for(bt1;btđk;bt2) Công_việc; Trong đó: * for là từ khoá 43
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm * bt1,bt2 là các biểu gán, btđk là một biểu thức cho giá trị logic * Công_việc có thể là một lệnh đơn , một cấu trúc điều khiển nào đó hay một khối lệnh - Sơ đồ cú pháp bt1 False bt®k True C«ng_viÖc bt2 - Sự hoạt động của câu lệnh for được tiến hành theo các bước sau: Bước 1: Xác định giá trị của bt1 Bước 2: Xác định giá trị của btđk Bước 3: Tuỳ thuộc vào tính đúng, sai của biểu thúc btđk máy sẽ tiến hành lựa chọn một trong hai nhánh sau: Nếu btđk có giá trị False, máy sẽ ra khỏi vòng lặp for và chuyển tới câu lệnh sau cấu trúc for Nếu btđk có giá trị True, máy sẽ tiến hành thực hiện các câu lệnh trong thân for . Khi thực hiện xong Công_việc hay gặp câu lệnh continue trong thân for máy sẽ chuyển sang buớc 4(khởi đầu lại). Bước 4: Tính bt2 sau đó quay lại bước 2 để bắt đầu lại vòng lặp mới của chu trình. Chú ý: +) Các bt1,bt2,btđk có thể vắng mặt nhưng phải để lại dấu chấm phẩy. +) Nếu btđk vắng mặt thì máy coi luôn đúng. Khi đó muốn thoát khỏi vòng lặp thì phải dùng câu lệnh return, break hay goto. +) Các bt1,bt2 có thể gồm nhiều biểu thức cách nhau bởi dấu phẩy +) Thông thường bt1 dùng để khởi gán giá trị cho các biến trong vòng lặp, bt2 dùng để thay đổi giá trị của các biến điều khiển khong vòng lặp sao cho lúc 44
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm đầu btđk cho giá trị True nhưng sau một số hữu hạn bước thực hiện thì btđk cho giá trị False Ví dụ 1: Nhập vào một số nguyên dương n sau đó tính n! using System; class VD6 { static void Main() { int n, i,s; Console.Write("Nhap vao so nguyen n="); n = int.Parse(Console.ReadLine()); for (s = 1, i = 1; i =1; i) s = Math.Sin(s); Console.Write("S={0:N3}",s); 45
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Console.ReadKey(); } } Ví dụ 3: Lập chương trình tìm số có ba chữ số sao cho số đó bằng tổng lập phương các chữ số của nó using System; class VD8 { static void Main() { int n, a, b, c; Console.WriteLine("Cac so thoa man yeu cau bai toan la:"); for (a = 1; a <= 9; ++a) for (b = 0; b <= 9; ++b) for (c = 0; c <= 9; ++c) if (a * 100 + b * 10 + c == a * a * a + b * b * b + c * c * c) Console.Write("{0}\t", a * 100 + b * 10 + c); Console.ReadKey(); } } 3.4 To¸n tö break vµ continue 3.4.1 C©u lªnh break Cho phép ra khỏi for, while, do while và switch. Nếu có nhiều vòng lặp lồng nhau, câu lệnh break sẽ ra khỏi chu trình(hoặc switch bên trong nhất chứa nó) Ví dụ: Nhập vào một số nguyên dương bất ký. Sau đó kiểm tra xem số nguyên đó có phải là số nguyên tố hay không using System; class VD8 { static void Main() { 46
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm int n,i; bool ok = true; Console.Write("Nhap vao so nguyen n="); n = int.Parse(Console.ReadLine()); for(i=2;i<=n-1;++i) if (n % i == 0) { ok = false; break; } if (ok && n != 1) Console.Write("day la so nguyen to"); else Console.Write("Day khong phai la so nguyen to"); Console.ReadKey(); } } 3.4.2 C©u lÖnh continue Dùng để quay về đầu vòng lặp mới của chu trình bên trong nhất chứa nó. Ví dụ: Nhập vào n số nguyên kiểu sau đó cho biết số nguyên dương lớn nhất trong số các số nguyên nhập vào using System; class VD8 { static void Main() { int n,x,i,max; max = int.MinValue; Console.Write("Nhap vao n="); n = int.Parse(Console.ReadLine()); for (i = 1; i <= n; ++i) { Console.Write("Nhap vao so nguyen thu {0}:", i); x = int.Parse(Console.ReadLine()); if (x < 0) continue; 47
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm if (x > max) max = x; } Console.Write("Max = {0}", max); Console.ReadKey(); } } 48
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Ch¬ng 4: Hµm 4.1 §Æt vÊn ®Ò Trong khi lập chương trình chúng ta thường gặp những đoạn chương trình được lặp đi lặp lại nhiều lần ở những chỗ khác nhau. Để tránh rườm rà, những đoạn chương trình này được thay thế bằng chương trình con tương ứng và khi cần, ta chỉ việc làm thủ tục gọi chương trình đó ra(với các tham số tương ứng cần thiết) mà không phải viết lại cả khúc chương trình đó. Thí dụ khi làm toán lượng giác, thường xuyên ta cần tính sin của một giá trị hay một biến x nào đó. Như vậy ta cần lập một chương trình con có tên Sin và tham số cần thiết là x. Những chương trình con thông dụng này đã được lập sẵn và để trong “thư viên” Trong C#, các chương trình con chuẩn này được phân loại và chứa trong các lớp như: Lớp chứa các hàm toán học Math, lớp chứa các hàm xử lý thời gian Timer, Lý do thứ hai để xây dựng chương trình con là: một vấn đề lớn phức tạp sẽ tương ứng với một chương trình có thể rất lớn , rất dài. Do đó việc nhìn tổng quan cả chương trình cũng như việc gỡ rối, hiệu chỉnh sẽ rất khó khăn. Ta có thể phân tác vấn đề phức tạp đó ra thành nhiều các vấn đề nhỏ hơn(tương ứng với các chương trình con) để dễ kiểm tra, gỡ rối từng khối một và sau đó ghép lại thành chương trình lớn. Trong ngôn ngữ lập trình C# chương trình con chỉ tồn tại dưới dạng các hàm 4.2 VÝ dô mét ch¬ng tr×nh cã sö dông hµm Xây dựng chương trình tính giá trị của biểu thức sau: x 2 x 3 x n S x 2! 3! n! using System; class VD { static double x; static int n; static void Nhap() { Console.Write("Nhap x=");x=double.Parse(Console.ReadLine()); Console.Write("Nhap n=");n=int.Parse(Console.ReadLine()); } 49
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm static double Mu(double x,int n) { double s; int i; for(s=1,i=1;i<=n;++i) s=s*x; return s; } static int GiaiThua(int n) { int s,i; for(i=1,s=1;i<=n;++i) s=s*i; return s; } static void Main() { double s=0; int i; Nhap(); for(i=1;i<=n;++i) s=s+Mu(x,i)/GiaiThua(i); Console.Write("S={0:N2}",s); Console.ReadKey(); } } 4.3 Ph¹m vi ho¹t ®éng cña biÕn Trong C# biến và hàm phải được khai báo bên trong một lớp. Những biến được khai báo bên trong một lớp và bên ngoài các hàm(trong lập trình hướng đối tượng(OOP) các biến đó gọi là dữ liệu của lớp, các hàm được gọi là các phương thức, tạm gọi các biến này là biến toàn cục trong một lớp) các biến này có phạm vi tác động trong toàn bộ lớp nghĩa là các hàm bên trong lớp có thể truy xuất được. Khi khai báo các biến này ta phải thêm từ khoá static(việc này sẽ được tìm hiểu kỹ trong lập trình hướng đối tượng) và nó được cấp phát bộ nhớ ngay từ khi ta thực hiện chương trình 50
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Ví dụ: Các biến static double x; và static int n; trong ví dụ trên có phạm vi tác động trong toàn bộ lớp, nghĩa là tất cả các phương thức trong cùng lớp đều có thể truy nhập. Chú ý: Thông thường những biến dùng chung cho các hàm trong cùng một lớp thì ta hay khai báo toàn cục Những biến được khai báo bên trong một hàm được gọi là biến cục bộ, phạm vi hoạt động của các biến này chỉ ở bên trong hàm mà nó được khai báo, các biến này chỉ được cấp phát bộ nhớ khi hàm mà có chứa các biến này được gọi ra thực hiện và khi thực hiên xong thì nó sẽ bị giải phóng khỏi bộ nhớ. Ví dụ: Các biến double s; và int i; trong ví dụ trên Chú ý: Những biến dùng để cài đặt thuật toán cho một hàm thì ta nên khai báo biến này là biến cục bộ, nếu biến cục bộ và biến toàn cục mà trùng tên nhau thì máy sẽ ưu tiến biến cục bộ trước Các biến được khai báo bên trong hai dấu “(“ và ” )” sau tên hàm được gọi là các đối của hàm. Trong C# có các kiểu đối sau: Đối kiểu tham trị Kiểudữliệu TênHàm(Kiểudữ liệu Tênđối1,Kiểudữ liệu Tênđối1, ) Ví dụ: static double Mu(double x,int n) Đối kiểu tham chiếu Kiểudữliệu TênHàm(ref Kiểudữ liệu Tênđối1, ref Kiểudữ liệu Tênđối1, ) Ví dụ: static void HoanVi(ref int x, ref int y) Đối kiểu tham chiếu chỉ nhận giá trị Kiểudữliệu TênHàm(out Kiểudữ liệu Tênđối1, out Kiểudữ liệu Tênđối1, ) Ví dụ: static void Ham(int x,int y, out int phannguyenm, out int phandu) Các đối của hàm có nguyên tắc hoạt động giống như biến cục bộ. Khi xây dựng một hàm chúng ta phải biết được hàm của chúng ta cần bao nhiêu đối, đối đó thuộc kiểu gì? 4.4 CÊu tróc mét hµm Hàm là một đơn vị độc lập của chương trình, do vậy khi định nghĩa một hàm chúng ta không được định nghĩa một hàm bên trong một hàm khác. Một hàm có dạng tổng quát như sau: KiểuDữLiệu TênHam(Danh sách các đối) { Các câu lệnh trong thâm hàm 51
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm [return [Biểu thức];] } Trong đó: Kiểu dữ liệu của hàm có thể là các kiểu dữ liệu cơ sở, các kiểu dữ liệu do người dùng định nghĩa. Nếu hàm không có giá trị trả về thì ta khai báo hàm trả về kiểu void. Nếu hàm trả về một giá trị thuộc kiểu dữ liệu cụ thể nào đó thì trước khi kết thúc hàm ta phải gán giá trị cho hàm bằng câu lệnh return BiểuThức. Khi máy gặp cấu lệnh này máy sẽ tính toán giá trị của biểu thức được bao nhiêu gán cho tên hàm và thoát khỏi hàm. Tên hàm là một tên do người dùng định nghĩa và phải tuân thủ theo nguyên tắc đặt tên và đặt làm sao nó mang ý nghĩa phù hợp với công việc của hàm. Danh sách các tham đối có thể là các kiểu đối như đã nêu ở trên, nếu có nhiều đối giữa các đối đặt cách nhau bởi dấu phẩy. Nếu như hàm không có đối thì chúng ta vấn phải viết hai dấu “(“ và”)” sau tên hàm Phần thân của hàm bắt đầu bằng dấu { và kết thúc bằng dấu } và ở giữa là các cấu lệnh thực hiện yêu cầu của hàm 4.6 Nguyªn t¾c ho¹t ®éng cña hµm vµ c¸ch truyÒn tham sè cho hµm Như ở trên chúng ta đã tìm hiểu mỗi hàm có thể có đối hoặc không. Nếu như hàm có đối thì sau khi xây dựng xong chúng ta có thể gọi chúng ra thực hiện. Mỗi một đối ta phải truyền cho nó một tham số tương ứng tuỳ thuộc đối đó là kiểu gì. Nếu đối là kiểu tham trị trước tiên máy sẽ cấp phát bộ nhớ cho đối kiểu tham trị đó, sau đó tiến hành sao chép giá trị của tham số thực thụ với nó đưa vào đối này. Từ đó ta thấy tham số thực thụ tương ứng với đối có kiểu tham trị có thể là một hằng, một biến, một biểu thức và có kiểu tương ứng. Bởi vì đối kiểu tham trị khi mà hàm chứa đối này được gọi ra thực hiện thì nó được cấp phát bộ nhớ riêng sau đó sao chép giá trị của tham số thực thụ tương ứng với nó đưa vào và sau đó máy tiến hành các thao tác trên đối này mà không làm ảnh hưởng tới tham số thực thụ tương ứng với nó và cũng bởi vì nó chỉ sao chép giá trị của tham số thực thụ tương ứng với nó do vậy tham số thực thụ tương ứng với nó cứ cho một giá trị là được. Vì vậy tham số thực thụ tương ứng với đối kiểu tham trị có thể là hàng, biến, biểu thức miễn là có cùng kiểu. Ghi chú: Theo những phân tích ở trên những đối chỉ nhằm mục đích cung cấp dữ liệu đầu vào cho hàm thì chúng ta khai báo đối đó là đối kiểu tham trị 52
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Nếu đối là kiểu tham chiếu đối này sẽ tham chiếu tới tham số tương ứng với nó. Nghĩa là trong thân hàm khi ta thao tác trên đối tham chiều này thực chất là thao tác trên tham số truyền vào tương ứng với đối này. Do vậy mọi thay đổi giá trị của đối tham chiếu cũng đồng nghĩa với việc thay đổi giá trị của tham số tương ứng truyền vào. Điều này chứng tỏ rằng tham số tương ứng với đối là kiểu tham chiểu phải là một biến hay phần tử của mảng có kiểu tương ứng và khi hàm chứa đối kiểu tham chiếu này kết thúc thì tham số tương ứng với nó lưu lại được sự thay đổi khi ra khỏi hàm Trong C# có hai loại đối kiểu tham chiếu đó là: đối kiểu tham chiếu ref và đối kiểu tham chiếu out. Nếu đối là kiểu tham chiếu ref thì tham số tương ứng với nó phải được khởi gán giá trị trươc khi truyền vào và nó có thể tham gia tính toán trong các biểu thức. Nếu đổi là kiểu tham chiếu out thì tham số tương ứng với nó không cần khởi tạo giá trị ban đầu, chính vì lẽ đó nó chỉ dùng để nhận giá trị và không được tham gia tính toán trong các biểu thức. Ghi chú: Theo những phân tích ở trên những đối dùng để cung cấp dữ liệu đầu vào cho hàm và đồng thời lưu lại được sự thay đổi khi chúng ta tác động trên những đối này, những đối như vậy thì chúng ta khai báo đối kiểu tham chiếu(ref). Những đối chỉ nhằm mục đích nhận giá trị khi ra khỏi hàm thì những đối này chúng ta khai báo đối theo kiểu tham chiếu out. Tham số truyền vào cho đối tham chiếu phải kèm theo hai từ khoá ref hoặc out tượng ứng với đối kiểu tham chiếu ref và đối kiểu tham chiếu out Ví dụ: Nhâp vào ba số nguyên dương sau đó tiến hành sắp xếp ba số nguyên đó theo thứ tự tăng dần. using System; class VD9 { static void HoanVi(ref int x, ref int y) { int tg = x; x = y; y = tg; } static void Main() { int a, b, c; Console.Write("Nhap a="); a = int.Parse(Console.ReadLine()); Console.Write("Nhap b="); b = int.Parse(Console.ReadLine()); 53
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Console.Write("Nhap c="); c = int.Parse(Console.ReadLine()); if (a > b) HoanVi(ref a, ref b); if (a > c) HoanVi(ref a, ref c); if (b > c) HoanVi(ref b, ref c); Console.Write("a={0}\tb={1}\tc={2}", a, b, c); Console.ReadKey(); } } Ví dụ: Xây dựng chương trình giải phương trình bậc hai ax2+bx+c=0(a<>0) using System; class VD { static double a,b,c; static void Nhap() { Console.Write("Nhap a=");a=double.Parse(Console.ReadLine()); Console.Write("Nhap b=");b=double.Parse(Console.ReadLine()); Console.Write("Nhap c=");c=double.Parse(Console.ReadLine()); } static double Delta() { return b*b-4*a*c; } static void Giai(out double x1,out double x2,out bool ok) { double d=Delta(); ok=true; if(d<0) {ok=false;x1=x2=0;} else if(d==0) x1=x2=-b/(2*a); else 54
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm { x1=(-b-Math.Sqrt(d))/(2*a); x2=(-b+Math.Sqrt(d))/(2*a); } } static void Main() { double x1,x2; bool ok; Nhap(); Giai(out x1,out x2,out ok); if(ok==true) if(x1==x2) Console.WriteLine("Phuong trinh co nghiem kep x1=x2={0}",x1); else { Console.WriteLine("Phuong trinh co hai nghiem phan biet"); Console.WriteLine("x1={0}\nx2={1}",x1,x2); } else Console.WriteLine("Phuong trinh vo nghiem"); Console.ReadKey(); } } 4.7 §Þnh nghÜa chång hµm Trong cùng một lớp chúng ta có thể định nghĩa các hàm trùng tên nhau, ta gọi đó là sự chồng hàm. Nếu như trong cùng một lớp các hàm trùng tên nhau thì để máy phân biệt giữa các hàm thì các hàm phải khác nhau về số đối, kiểu của các đối và giá trị trả về. Giả sử trong cùng một lớp hai hàm có cùng tên là max, có cùng số lượng đối là hai thì để máy phân biệt được giữa hai hàm này khi nó được gọi ra sử dụng thì hai hàm này phải khác nhau về kiểu của các đối. Ví dụ: Nhập vào ba số nguyên a,b,c từ bàn phím. Sau đó cho biết giá trị lớn nhất của hai số nguyên bất kỳ và của ba số nguyên đó. 55
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm using System; class VD { static int a,b,c; static void Nhap() { Console.Write("Nhap a=");a=int.Parse(Console.ReadLine()); Console.Write("Nhap b=");b=int.Parse(Console.ReadLine()); Console.Write("Nhap c=");c=int.Parse(Console.ReadLine()); } static int Max(int x,int y) { return x>y?x:y; } static int Max(int x,int y,int z) { int tg=Max(x,y);/*Đây không phải là gọi đệ quy mà chính là gọi một hàm tính Max của hai số ở phía trên ra sử dụng*/ tg=Max(tg,z); return tg; } static void Main() { Nhap(); Console.WriteLine("Max({0},{1})={2}",a,b,Max(b,c)); Console.WriteLine("Max({0},{1},{2})={3}",a,b,c,Max(a,b,c)); Console.ReadKey(); } } 4.8 C¸ch gäi hµm Trong cùng một lớp một hàm có quyền gọi các hàm khác ra sử dụng(thứ tự các hàm trong cùng một lớp là không quan trong), một hàm có thể gọi chính nó ra thực hiện. Ta gọi đó là gọi đệ quy, đệ quy và giải thuật đệ quy chúng ta sẽ 56
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm nghiên cứu trong các tài liệu khác, ở đậy tôi chỉ giới thiệu sơ bộ về cách gọi đệ quy trong việc xây dựng một hàm trong C# thông qua một ví dụ như sau: Ví dụ: Xây dựng chương trình tính giá trị của biểu thức sau: x 2 x 3 x n S x 2! 3! n! using System; class VD { static double x; static int n; static void Nhap() { Console.Write("Nhap x=");x=double.Parse(Console.ReadLine()); Console.Write("Nhap n=");n=int.Parse(Console.ReadLine()); } static double Mu(double x,int n) { if(n==0) return 1; else return x*Mu(x,n-1); } static int GiaiThua(int n) { if(n==0) return 1; else return n*GiaiThua(n-1); } static void Main() { double s=0; int i; Nhap(); for(i=1;i<=n;++i) s=s+Mu(x,i)/GiaiThua(i); Console.Write("S={0:N2}",s); Console.ReadKey(); } } 57
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Ch¬ng 5: Mét sè kiÓu d÷ liÖu më réng 5.1 KiÓu d÷ liÖu gi¸ trÞ vµ kiÓu d÷ liÖu tham chiÕu C# phân tập hợp kiểu dữ liệu thành hai loại: Kiểu dữ liệu giá trị (value) và kiểu dữ liệu tham chiếu (reference). Việc phân chia này do sự khác nhau khi lưu kiểu dữ liệu giá trị và kiểu dữ liệu tham chiếu trong bộ nhớ. Đối với một kiểu dữ liệu giá trị thì sẽ được lưu giữ kích thước thật trong bộ nhớ đã cấp phát là stack. Trong khi đó thì địa chỉ của kiểu dữ liệu tham chiếu thì được lưu trong stack nhưng đối tượng thật sự thì lưu trong bộ nhớ heap. Ghi chú: Tất cả các kiểu dữ liệu xây dựng sẵn là kiểu dữ liệu giá trị ngoại trừ các đối tượng và chuỗi. Và tất cả các kiểu do người dùng định nghĩa ngoại trừ kiểu cấu trúc đều là kiểu dữ liệu tham chiếu. Stack là một cấu trúc dữ liệu lưu trữ thông tin dạng xếp chồng tức là vào sau ra trước (Last In First Out : LIFO), điều này giống như chúng ta có một chồng các đĩa, ta cứ xếp các đĩa vào chồng và khi lấy ra thì đĩa nào nằm trên cùng sẽ được lập ra trước, tức là đĩa vào sau sẽ được lấy ra trước. Trong C#, kiểu giá trị như kiểu số nguyên được cấp phát trên stack, đây là vùng nhớ được thiết lập để lưu các giá trị, và vùng nhớ này được tham chiếu bởi tên của biến. Kiểu tham chiếu như các đối tượng thì được cấp phát trên heap. Khi một đối tượng được cấp phát trên heap thì địa chỉ của nó được trả về, và địa chỉ này được gán đến một tham chiếu. Thỉnh thoảng cơ chế thu gom sẽ hũy đối tượng trong stack sau khi một vùng trong stack được đánh dấu là kết thúc. Thông thường một vùng trong stack được định nghĩa bởi một hàm. Do đó, nếu chúng ta khai báo một biến cục bộ trong một hàm là một đối tượng thì đối tượng này sẽ đánh dấu để hũy khi kết thúc hàm. Những đối tượng trên heap sẽ được thu gom sau khi một tham chiếu cuối cùng đến đối tượng đó được gọi. 5.2 KiÓu d÷ liÖu m¶ng 5.2.1 Kh¸i niÖm Mảng là một tập hợp có thứ tự của những đối tượng, tất cả các đối tượng này cùng một kiểu hay nói cách khác mảng là một tập các biến cùng loại. Mảng trong ngôn ngữ C# có một vài sự khác biệt so với mảng trong ngôn ngữ C++ và một số ngôn ngữ khác, bởi vì chúng là những đối tượng. Điều này sẽ cung cấp cho mảng sử dụng các phương thức và những thuộc 58
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm tính. Trong C# ta có thể xây dựng mảng một chiều, mảng hai chiêu, mảng nhiều chiều 5.2.2 Mảng một chiều Chúng ta có thể khai báo mảng một chiều trong C# với cú pháp theo sau: [] ; Ví dụ ta có khai báo như sau: int [] a; float [] b; Cặp dấu ngoặc vuông ([]) báo cho trình biên dịch biết rằng chúng ta đang khai báo một mảng. Kiểu dữ liệu là kiểu của các thành phần chứa bên trong mảng. Trong ví dụ bên trên a được khai báo là mảng số nguyên, b là mảng các số thực. Chúng ta tạo thể hiện của mảng bằng cách sử dụng từ khóa new như sau: a = new int[6]; b=new double[20]; Khai báo này máy sẽ thiết lập bên trong bộ nhớ cho mảng a một dãy sáu vùng nhớ liên tiếp, mỗi vùng nhớ chứa được một số nguyên kiểu int tức là mỗi vùng nhớ gồm 4 byte liên tiếp, vậy tổng máy cấp phát cho mảng a là 24 byte liên tiếp . Mảng b máy sẽ cấp phát một dãy 20 vùng nhớ liên tiếp, mỗi vùng nhớ chứa được một số thực kiểu double tức là mỗi vùng nhớ gồm 8 byte liên tiếp, vậy tổng máy cấp phát cho mảng b là 120 byte liên tiếp . Ở đây chúng ta cần phân biệt giữa hai thành phần bản thân mảng và các thành phần trong mảng. Như chúng ta đã biết trong C# mảng là một kiểu dữ liệu tham chiếu nên bản thân mảng trong ví dụ trên là a và các thành phần trong mảng là sáu số nguyên, thế thì bản thân mảng a được cấp phát trong stack còn các thành phần của mảng được cấp phát trong heap, bản thân mảng a chỉ chứa địa chỉ của vùng nhớ cấp cho các thành phần của mảng trong heap mà thôi. Ta có thể minh hoạ như sau: 59
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Stack Heap a Chứa địa chỉ của vùng nhớ cấp phát cho các thành phần trong mảng Ta có thể vừa khai báo mảng và vừa cấp phát bộ nhớ cho các phần tử của mảng như sau: [] =new [kích thước]; Ví dụ: int []a=new int[40]; double []b=new double[10]; Ta có thể khai báo mảng và đồng thời khởi tạo cho các phần tử của mảng như sau: Chúng ta có thể khởi tạo nội dung của một mảng ngay lúc tạo thể hiện của mảng bằng cách đặt những giá trị bên trong dấu ngoặc ({}). C# cung cấp hai cú pháp để khởi tạo các thành phần của mảng, một cú pháp dài và một cú pháp ngắn: int[] myIntArray1 = new int[5] { 2, 4, 6, 8, 10}; int[] myIntArray2 = { 2, 4, 6, 8, 10}; Không có sự khác biệt giữa hai cú pháp trên, và hầu hết các chương trình đều sử dụng cú pháp ngắn hơn do sự tự nhiên và lười đánh nhiều lệnh của người lập trình. Khi khai báo mảng nếu ta không khởi đầu giá trị cho chúng thì các phần tử của mảng tự động được khởi tạo một giá trị ngầm định theo bảng thống kê sau: 60
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Ví dụ: Khi ta khia báo mảng int []a=new int[5]; Ta sẽ thu được mảng a gồm 5 phần tử và mỗi phần tử của mảng được khởi đầu giá trị là 0 Mảng trong ngôn ngữ C# có một vài sự khác biệt so với mảng trong ngôn ngữ C++ và một số ngôn ngữ khác, bởi vì chúng là những đối tượng. Điều này sẽ cung cấp cho mảng sử dụng các phương thức và những thuộc tính. Ngôn ngữ C# cung cấp cú pháp chuẩn cho việc khai báo những đối tượng Array. Tuy nhiên, cái thật sự được tạo ra là đối tượng của kiểu System.Array. Mảng trong ngôn ngữ C# kết hợp cú pháp khai báo mảng theo kiểu ngôn ngữ C và kết hợp với định nghĩa lớp do đó thể hiện của mảng có thể truy cập những phương thức và thuộc tính của System.Array. Một số các thuộc tính và phương thức của lớp System.Array 61
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Truy nhập vào các phần tử của mảng: Để truy nhập vào các phần tử của mảng chúng ta truy nhập thông qua tên mảng và chỉ số tương ứng. Đặc biệt trong C# phần tử đầu tien của mảng có chỉ số la 0. Phần tử cuối cùng của mảng có chỉ số là Tên_Mảng.Length-1. Nhớ rằng chỉ số của mảng phải ở trong giới hạn cho phép. Ta có thể truy nhập vào các phần tử của mảng thông qua công thức tổng quát sau: Tên_Mảng[chỉ số] Ví dụ: int []=new int[5]; a[0] // Truy nhập vào phần tử đầu tiên của mảng a[i] // Truy nhập vào phần tử thứ i của mảng i [0, Tên_Mảng.Length-1] Một số ví dụ áp dụng Ví dụ 1: Nhập vào một dãy số nguyên sau đó thực hiện các yêu cầu sau: a) Tính tổng các phần tử của mảng chia hết cho 3 b) Tìm giá trị lớn nhất trong số các phần tử của mảng c) Nhập vào từ bàn phím một số nguyên x và cho biết số nguyên này xuất hiên trong mảng bao nhiêu lần d) Rút gọn mảng(nghĩa là mỗi phần tử chỉ đcượ xuất hiện một lần) using System; class VD 62
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm { static int[] a; static int n; static void Nhap() { int i; Console.Write("Nhap so phan tu cua mang n="); n = int.Parse(Console.ReadLine()); a = new int[n]; Console.WriteLine("Hay nhap cac phan tu cho mang"); for (i = 0; i < n; ++i) { Console.Write("a[{0}]=", i); a[i] = int.Parse(Console.ReadLine()); } } static void Hien(int []x) { int i; for (i = 0; i < x.Length ; ++i) Console.Write("{0}\t", x[i]); Console.WriteLine(); } static int Tong3() { int i,t=0; for(i=0;i<n;++i) if(a[i]%3==0) t=t+a[i]; return t; } static int Max() { int i,mx; mx = a[0]; for (i = 1; i < n; ++i) if (mx < a[i]) mx = a[i]; 63
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm return mx; } static int Dem(int x) { int i,d=0; for (i = 0; i < n; ++i) if (a[i] == x) d++; return d; } static void RutGon(out int[] kq) { int i,d=0,j; int[] tmp = new int[n]; bool ok; for (i = 0; i < n; ++i) { ok = true; for(j=0;j<d;++j) if (tmp[j] == a[i]) { ok = false; break; } if (ok) tmp[d++] = a[i]; } kq = new int[d]; Array.Copy(tmp, kq, d); } static void Main() { ConsoleKeyInfo kt; int x; int []b=null; do{ Console.Clear(); Console.WriteLine("\t\t\tMain Menu"); Console.WriteLine("\t1. Nhap mang"); Console.WriteLine("\t2. Hien Mang"); 64
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Console.WriteLine("\t3. Cac phan tu cua mang chi het cho 3"); Console.WriteLine("\t4. Gia tri lon nhat cua mang"); Console.WriteLine("\t5. So lan xuat hien cua phan tu x trong mang"); Console.WriteLine("\t6. Rut gon mang"); Console.WriteLine("\t7. Thoat khoi chuong trinh"); Console.Write(" Ban hay chon mot cong viec tu 1->7:"); kt=Console.ReadKey(); Console.WriteLine(); switch(kt.KeyChar) { case '1': Nhap(); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '2': Console.WriteLine("Cac phan tu cua mang la:"); Hien(a); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '3': Console.WriteLine(" Ket qua la {0:8}",Tong3()); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '4': Console.WriteLine(" Ket qua la {0,8}",Max()); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '5': Console.Write("Nhap x=");x=int.Parse(Console.ReadLine()); Console.WriteLine(" Ket qua la {0:8}",Dem(x)); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); 65
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm break; case '6': Console.WriteLine("Cac phan tu cua mang sau khi rut gon:"); RutGon(out b); Hien(b); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '7': Environment.Exit(0); break; } } while (true); } } Ví dụ 2: Cho hai dãy số a1, a2, ,an và b1,b2, ,bm có các phân tử là các số nguyên được nhập vào từ bàn phím. Sau thực hiện các yêu cầu sau: a) Xây dựng mảng c bằng cach ghép hai mảng a và b với nhau b) Kiểm tra mảng c có lập thành một cấp số cộng không c) Đưa ra các phần tử của mảng c xuất hiện đúng một lần d) Tách mảng c thành hai mảng: một mảng chứa toán số chẵn, một mảng chứa toàn số lẻ e) Sắp xếp mảng a,b theo thứ tự tăng dần. Sau đó xây dựng mảng d bằng cách chèn các phần tử của mảng a,b sao cho ta thu được mảng d cũng có thứ tự tăng dần using System; class VD { static void Nhap(char ten, out int []x) { int i,n; Console.WriteLine("Nhap thong tin cho cac phan tu cua mang {0}",ten); Console.Write("Nhap so phan tu cua mang:"); n = int.Parse(Console.ReadLine()); x = new int[n]; 66
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Console.WriteLine("Hay nhap cac phan tu cho mang"); for (i = 0; i < n; ++i) { Console.Write("{0}[{1}]=",ten,i); x[i] = int.Parse(Console.ReadLine()); } } static void Hien(int[] x) { int i; for (i = 0; i < x.Length; ++i) Console.Write("{0}\t", x[i]); Console.WriteLine(); } static void GhepMang(int[] x, int[] y, out int[] kq) { kq = new int[x.Length + y.Length]; Array.Copy(x, kq, x.Length); Array.Copy(y, 0, kq, x.Length, y.Length); } static bool CapSoCong(int[] x) { bool ok = true; int i; for (i = 1; i < x.Length - 1; ++i) if (x[i] != (x[i - 1] + x[i + 1]) / 2) { ok = false; break; } return ok; } static void MotLan(int[] x, out int[] kq) { int i,j,d=0; int []tmp=new int[x.Length]; bool ok; for (i = 0; i < x.Length; ++i) { 67
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm ok = true; for(j=0;j<x.Length;++j) if (x[i] == x[j] && i != j) { ok = false; break; }; if(ok) tmp[d++]=x[i]; } kq = new int[d]; Array.Copy(tmp, kq, d); } static void Tach(int[] x, out int[] chan, out int[] le) { int i,d1=0,d2=0; int[] tmp1 = new int[x.Length]; int[] tmp2 = new int[x.Length]; for (i = 0; i < x.Length; ++i) if (x[i] % 2 == 0) tmp1[d1++] = x[i]; else tmp2[d2++] = x[i]; chan = new int[d1]; Array.Copy(tmp1, chan, d1); le = new int[d2]; Array.Copy(tmp2, le, d2); } static void Chen(int[] x, int[] y, out int[] kq) { int i,n,k,j; Array.Sort(x); Array.Sort(y); kq = new int[x.Length + y.Length]; Array.Copy(x, kq, x.Length); n=x.Length-1; for (i = 0; i < y.Length; ++i) { if (kq[n] < y[i]) kq[++n] = y[i]; else { j = 0; 68
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm while (y[i] >= kq[j]) j++; for (k = ++n; k > j; k) kq[k] = kq[k - 1]; kq[j] = y[i]; } } } static void Main() { int []a=null; int []b=null; int []c=null; int []d=null; int[] kq = null; int[] chan = null; int[] le = null; ConsoleKeyInfo kt; do { Console.Clear(); Console.WriteLine("\t\t\tMain Menu"); Console.WriteLine("\t1. Nhap thong tin cho hai mang"); Console.WriteLine("\t2. Mang ghep la"); Console.WriteLine("\t3. Kiem tra day co la cap so cong ko?"); Console.WriteLine("\t4. Cac phan tu cua mang xuat hien dung mot lan"); Console.WriteLine("\t5. Tach mang(chan, le)"); Console.WriteLine("\t6. Chen mang"); Console.WriteLine("\t7. Thoat khoi chuong trinh"); Console.Write(" Ban hay chon mot cong viec tu 1->7:"); kt = Console.ReadKey(); Console.WriteLine(); switch (kt.KeyChar) { case '1': Nhap('A', out a); Nhap('B', out b); 69
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '2': Console.WriteLine("Mang ghep la"); GhepMang(a, b, out c); Hien(c); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '3': if(CapSoCong(c)==true ) Console.WriteLine(" Day da cho la cap so cong"); else Console.WriteLine(" Day da cho kong phai la cap so cong"); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '4': Console.WriteLine("Cac phan tu cua mang xaut hien dung mot lan"); MotLan(c,out kq); Hien(kq); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '5': Tach(c, out chan, out le); Console.WriteLine("Cac phan tu chan"); Hien(chan); Console.WriteLine("Cac phan tu le"); Hien(le); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '6': Console.WriteLine("Mang chen la:"); 70
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Chen(a, b,out d); Hien(d); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '7': Environment.Exit(0); break; } } while (true); } } Câu lệnh lặp foreach Câu lệnh lặp foreach khá mới với những người đã học ngôn ngữ C, từ khóa này được sử dụng trong ngôn ngữ Visual Basic. Câu lệnh foreach cho phép chúng ta lặp qua tất cả các mục trong một mảng hay trong một tập hợp. Cú pháp sử dụng lệnh lặp foreach như sau: foreach ( in ) { // thực hiện thông qua tương ứng với // từng mục trong mảng hay tập hợp } Ví dụ: Nhập vào một dãy số nguyên sau đó đưa ra màn hình các số chẵn trên một dòng, các số lể trên một dòng using System; class ViDu { static int[] a; static void Nhap() { int i=0,x; ConsoleKeyInfo kt; Console.WriteLine("Ban hay nhap vao mot day so nguyen"); do { 71
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Console.Write("So nguyen thu {0}:", i); x = int.Parse(Console.ReadLine()); Array.Resize(ref a, ++i); a[i - 1] = x; Console.Write("Ban co nhap tiep C/K"); kt = Console.ReadKey(); Console.WriteLine(); } while (kt.KeyChar == 'c' || kt.KeyChar == 'C'); } static void Hien() { Console.WriteLine("Cac phan tu chan la:"); foreach (int i in a) if(i%2==0) Console.Write("{0}\t", i); Console.WriteLine("\nCac phan tu le la:"); foreach (int i in a) if(i%2!=0) Console.Write("{0}\t", i); } static void Main() { Nhap(); Hien(); Console.ReadKey(); } } 5.2.3 Mảng hai chiều Trong C# cho phép chúng ta xây dựng mảng nhiều chiều. Nhưng ở đây ta chỉ nghiên cứu mảng hai chiều Cách khai báo mảng hai chiều [ , ] tên mảng; Ví dụ: int [ , ]a; float [ , ]b; 72
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm Lý giải như trên ta thấy để cấp phát bộ nhớ cho các phần tử của mang ta dùng từ khoá new như sau: a=new int[4,3]; // Khai báo mảng hai chiều a gồm 4 hàng, 3 cột và mỗi phần tử của mảng là một số nguyên kiểu int b=new float[2,3];// Khai báo mảng hia chiều b gồm 2 hàng, 3 cột và mỗi phần tử của mảng là một số thực kiểu float Một số thông tin khác được lý giải như mảng một chiều Ta có thể vừa khai báo mảng và vừa cấp phát bộ nhớ cho các phần tử của mảng như sau: [,] =new [kích thước hàng, kích thước cột]; Ví dụ: int [,]a=new int[4,6]; double [,]b=new double[10,4]; Ta có thể khai báo mảng và đồng thời khởi tạo cho các phần tử của mảng như sau: Chúng ta có thể khởi tạo nội dung của một mảng ngay lúc tạo thể hiện của mảng bằng cách đặt những giá trị bên trong dấu ngoặc ({}). C# cung cấp hai cú pháp để khởi tạo các thành phần của mảng, một cú pháp dài và một cú pháp ngắn: int[,] myIntArray1 = new int[2,3] { {2, 4, 6},{ 8, 10,9}}; int[,] myIntArray2 = { {2, 4, 1},{ 6, 8, 10}}; Không có sự khác biệt giữa hai cú pháp trên, và hầu hết các chương trình đều sử dụng cú pháp ngắn hơn do sự tự nhiên và lười đánh nhiều lệnh của người lập trình. Để truy nhập vào các phần tử của mảng chúng ta cũng truy nhập thông qua tên và chỉ số tương ứng. Cụ thể Tên_Mảng[chỉ số hàng, chỉ số cột] chỉ số hàng nằm trong đoạn [0, Tên_Mảng.GetLength(0)-1] chỉ số cột nằm trong đoạn [0, Tên_Mảng.GetLength(1)-1] Một số ví dụ áp dụng Ví dụ 1: Cho ma tran a gồm m hàng, n cột có các phần tử là các số nguyên được nhập vào từ bàn phím. Sau đó thực hiện các yêu cầu sau: a) Tìm giá trị lớn nhất trong số các phần tử của mảng b) Tính tổng các phần tử là số nguyên tố của mảng 73
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm c) Nếu a là ma trận vuông hãy tính tích các phần trên đường chéo chính của mảng. using System; class VD { static int[,] a; static int m, n; static void Nhap() { int i, j; Console.Write("Nhap so hang m="); m = int.Parse(Console.ReadLine()); Console.Write("Nhap so cot n="); n = int.Parse(Console.ReadLine()); a = new int[m, n]; Console.WriteLine("Nhap gia tri cho cac phan tu cua ma tran"); for (i = 0; i < m; ++i) for (j = 0; j < n; ++j) { Console.Write("a[{0},{1}]=", i, j); a[i, j] = int.Parse(Console.ReadLine()); } } static void Hien() { int i, j; for (i = 0; i < m; ++i) { for (j = 0; j < n; ++j) Console.Write("{0}\t", a[i, j]); Console.WriteLine(); } } static int Max() { int i, j, mx = a[0, 0]; for (i = 0; i < m; ++i) 74
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm for (j = 0; j < n; ++j) if (mx < a[i, j]) mx = a[i, j]; return mx; } static bool Nt(int x) { bool ok = true; int i; for (i = 2; i < x - 1; ++i) if (x % i == 0) { ok = false; break; } return ok && x != 1; } static void NtMang() { int i, j; Console.WriteLine("cac phan tu la so nguyen to cua mang"); for (i = 0; i < m; ++i) for (j = 0; j < n; ++j) if (Nt(a[i, j])) Console.Write("{0}\t", a[i, j]); Console.WriteLine(); } static void Vet() { int i, j, t = 1; if (m == n) { for (i = 0; i < m; ++i) t = t * a[i, i]; Console.WriteLine("Tich cac phan tu tren duong cheo chinh la {0}", t); } else Console.WriteLine("Day khong phan la ma tran vuong"); } static void Main() { 75
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm ConsoleKeyInfo kt; do { Console.Clear(); Console.WriteLine("\t\t\tMain Menu"); Console.WriteLine("\t1. Nhap thong tin cho mang"); Console.WriteLine("\t2. Hien mang"); Console.WriteLine("\t3. Gia tri lon nhat cua mang"); Console.WriteLine("\t4. Cac phan tu la so nguyen to cua mang"); Console.WriteLine("\t5. Tich cac phan tu tren duong cheo chinh"); Console.WriteLine("\t6. Thoat khoi chuong trinh"); Console.Write(" Ban hay chon mot cong viec tu 1->6:"); kt = Console.ReadKey(); Console.WriteLine(); switch (kt.KeyChar) { case '1': Nhap(); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '2': Console.WriteLine("Cac phan tu cua mang la"); Hien(); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '3': Console.WriteLine(" Max cua mang {0}", Max()); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '4': NtMang(); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); 76
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm break; case '5': Vet(); Console.WriteLine("Ban hay nhan phim bat ky de tiep tuc "); Console.ReadKey(); break; case '6': Environment.Exit(0); break; } } while (true); } } Ví dụ 2: Cho hai ma trận vuông a,b cấp n có các phần tử là các số nguyên được nhập vào từ bàn phím. Sau đó thực hiện các yêu cầu sau: a) Tính tổng hai ma trận a,b kết qua cho vào ma trận c b) Tìm giá trị lớn nhất của các hàng trong ma trận c kết quả cho vào ma trận một chiều x c) Sắp xếp ma trận c tăng dần theo chiều xoáy trôn ốc using System; class VD { static int n; static void Nhap(char ten,out int [,]x) { int i, j; x = new int[n, n]; Console.WriteLine("Nhap gia tri cho cac phan tu cua ma tran {0}",ten); for (i = 0; i < n; ++i) for (j = 0; j < n; ++j) { Console.Write("{0}[{1},{2}]=",ten, i, j); x[i, j] =int.Parse(Console.ReadLine()); } } 77
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm static void Hien(int [,]x) { int i, j; for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) Console.Write("{0}\t", x[i, j]); Console.WriteLine(); } } static void Hien(int[] x) { int i; for (i = 0; i < n; ++i) Console.Write("{0}\t", x[i]); Console.WriteLine(); } static void Tong(int[,] x, int[,] y, out int[,] kq) { int i, j; kq = new int[n, n]; for (i = 0; i < n; ++i) for (j = 0; j < n; ++j) kq[i, j] = x[i, j] + y[i, j]; } static void MaxHang(int[,] x, out int[] kq) { int i, j,mx,d=0; kq = new int[n]; for (i = 0; i < n; ++i) { mx = x[i, 0]; for (j = 1; j < n; ++j) if (mx < x[i, j]) mx = x[i, j]; kq[d++] = mx; 78
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm } } static void TronOc(ref int[,] x) { int i, j, d=0,k=0; int[] tmp = new int[n * n]; for (i = 0; i k; j) x[i, j] = tmp[d++]; j = k; for (i = n - 1 - k; i > k; i) x[i, j] = tmp[d++]; k++; if (d == n * n - 1) x[n / 2 , n / 2 ] = tmp[d++]; } while (d < n * n); } static void Main() { int [,]a=null; int [,]b=null; int [,]c=null; int []x=null; Console.Write("Nhap cap cau ma tran n="); 79
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm n = int.Parse(Console.ReadLine()); Nhap('A', out a); Nhap('B', out b); Tong(a, b, out c); Console.WriteLine("Tong hai mang"); Hien(c); MaxHang(c, out x); Console.WriteLine("Gia tri lon nhat cua cac hang"); Hien(x); TronOc(ref c); Console.WriteLine("Mang c sau khi sap xep"); Hien(c); Console.ReadKey(); } } 5.2.4 Mảng đa chiều có kích khác nhau Như chúng ta đã biết hình dạng của mảng đa chiều cùng kích thước có dạng hình chữ nhật thì hình dạng của mảng đa chiều có khích thước khác nhau không phải hình chữ nhật vì các chiều của chúng không điều nhau. Khi chúng ta tạo một mảng đa chiều kích thước khác nhau thì chúng ta khai báo số dòng trong mảng trước. Sau đó với mỗi dòng sẽ giữ một mảng, có kích thước bất kỳ. Những mảng này được khai báo riêng. Sau đó chúng ta khởi tạo giá trị các thành phần trong những mảng bên trong. Cách khai báo [][] tên mảng=new [kích thước hàng][] ; hoặc [][] tên mảng; tên mảng=new [kích thước hàng][] ; Sau đó ta tiến hành cấp phát bộ nhớ cho các phần tử của mỗi hàng tênmảng[chỉ số]=new [số phần tử của mỗi hàng] Ví dụ: int a[][]a; a=new int[3][]; Tiếp đó ta đi cấp phát bộ nhớ cho các phần tử của mỗi hàng 80
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm a[0]=new int[6]; // Hàng thứ nhất có 6 phần tử a[1]=new int[2]; // Hàng thứ nhất có 2 phần tử a[2]=new int[5]; // Hàng thứ nhất có 5 phần tử Truy nhập vào các phần tử của mảng Để truy nhập vào các phần tử của mảng đa chiều có kích không bằng nhau ta có thể thực hiện theo nguyên tắc sau: Tênmảng[chí số 1][chỉ số 2] chí số 1 [0 Tênmảng.Length-1] chí số 2 [0 Tênmảng[chí số 1].Length-1] Ví dụ: Nhập vào n dãy số nguyên sau đó tìm giá trị lớn nhất của mỗi dãy lưu vào một mảng một chiều và sẵp xếp chúng theo thứ tự tăng dần. using System; class VisDu { static int[][] a; static void Nhap() { int i,j,n,m; Console.Write("Ban muon nhap vao bao nhieu day so nguyen n="); n = int.Parse(Console.ReadLine()); a = new int[n][]; Console.WriteLine("Ban hay nhap thong tin cho moi day so nguyen"); for (i = 0; i < a.Length; ++i) { Console.Write("Ban hay nhap so phan tu cho day thu {0} m=", i); m = int.Parse(Console.ReadLine()); a[i] = new int[m]; Console.WriteLine("Nhap gia tri cho moi phan tu o day thu {0}", i); for (j = 0; j < a[i].Length; j++) { Console.Write("a[{0}][{1}]=", i, j); a[i][j] = int.Parse(Console.ReadLine()); } } } 81
- NguyÔn H÷u §«ng – Bé m«n C«ng nghÖ phÇn mÒm static void Tim(out int[] kq) { int i, j, d=0,max; kq = null; for (i = 0; i < a.Length; ++i) { max = a[i][0]; for (j = 1; j < a[i].Length; ++j) if (max < a[i][j]) max = a[i][j]; Array.Resize(ref kq, ++d); kq[d - 1] = max; } } static void Hien(int []x) { int i; for (i = 0; i < x.Length; ++i) Console.Write("{0}\t", x[i]); Console.WriteLine(); } static void Main() { int[] kq; Nhap(); Tim(out kq); Array.Sort(kq); Console.WriteLine("Cac phan tu lon nhat cua moi day da duoc sap xep la"); Hien(kq); Console.ReadKey(); } } 5.3 KiÓu d÷ liÖu x©u(chuçi) Có một thời gian người ta luôn nghĩ rằng máy tính chỉ dành riêng cho việc thao tác các giá trị dạng số. Các máy tính đầu tiên là được thiết kế để sử 82