Khi chúng ta khai báo biến, phần mềm lập trình sẽ tự động tìm một vùng nhớ còn trống trong bộ nhớ để lưu trữ giá trị của biến, kích thước của vùng nhớ tùy thuộc vào kiểu dữ liệu của biến
Trang 1TÓM TẮT CHƯƠNG 2 : C++/CLI CĂN BẢN
1) Tạo và biên dịch một chương trình C++/CLI :
a) Dùng phần mềm Visual Studio :
Mở chương trình VS 2008 Trong màn hình khởi động, chọn File - > New -> Project
Trong cửa sổ New Project : Chọn kiểu project là CLR, trong cửa sổ Templates chọn
CLR Console Application
Nhập tên của project vào ô Name và chọn thư mục chứa project trong ô Location Nhấp OK
Trang 2Xuất hiện cửa sổ Project, chúng ta có thể bắt đầu viết chương trình trong vùng soạn thảo
Sau khi hoàn tất chương trình, nhấn vào biểu tượng Debug để chạy chương trình (hoặc nhấn F5)
Khi Debug, nếu chương trình co lỗi về mặt cú pháp, VS sẽ thông báo các lỗi này trong cửa
sổ Error, nhấp kép chuột vào dòng thông báo lỗi để biết vị trí lỗi
Nếu không thấy cửa sổ Error, trên thanh công cụ chọn View -> Other Windows -> Error
List (Hoặc nhấn Ctrl+F5)
b) Dùng Notepad :
Mở Notepad và bắt đầu viết chương trình trong cửa sổ soạn thảo của Notepad
Trang 3Sau khi hoàn tất, lưu lại với đuôi cpp (Ví dụ muốn lưu lại với tên là Vidu thì trong ô File name nhập Vidu.cpp, trong ô Save as type chọn All Files
Để biên dịch chương trình, mở VS Command Prompt
Trong màn hình Command Prompt, chuyển đến thư mục đang chứa tập tin muốn biên dịch
bằng lệnh cd Ví dụ nếu tập tin Vidu.cpp được lưu trong thư mục CLI trong ổ E thì nhập cd
E:\CLI
Trang 4Sau khi chuyển đến thư mục, tiến hành biên dịch bằng lệnh : cl name /clr:safe Trong
đó, name là tên tập tin muốn biên dịch bao gồm cả đuôi mở rộng (Ví dụ : Vidu.cpp)
Sau khi biên dịch thành công, hai tập tin Vidu.exe và Vidu.obj sẽ được tạo ra
Ta có thể chạy chương trình bằng cách nhấp kép vào Vidu.exe trong thư mục chứa tập tin
lập trình hoặc chạy trực tiếp trên Command Prompt bằng cách nhập vào tên của tập tin tại dấu nhắc (không bao gồm đuôi mở rộng)
Lưu ý : Nếu chương trình có lỗi về mặt cú pháp, quá trình biên dịch sẽ thất bại
2) Class CONSOLE :
Trang 5Class quản lí việc tương tác với màn hình Console Các phương thức (hàm) cơ bản của lớp:
Write(a) : Hàm một đối số Xuất ra màn hình Console giá trị của a (a có thể là biến hoặc
ReadLine() : Hàm không có đối số Kiểu dữ liệu trả về là String Nhận một chuỗi kí tự
được nhập từ bàn phím
VD : String^ b = Console::ReadLine ();
Hello : b = “Hello”
ReadKey() : Hàm không có đối số Kiểu dữ liệu trả về là ConsoleKeyInfo Nhận phím
được nhấn từ bàn phím (trừ các phím đặc biệt Ctrl, Shift, Alt)
VD : ConsoleKeyInfo b = Console::ReadKey ();
Console::Write(b.Key);
(Nhấn phím) Esc -> Escape
Kiểm tra xem có phải phím x đã được nhấn : b.Key == ConsoleKey::x ;
VD : if (b.Key ==ConsoleKey::Escape) Console::Write(L “Phím ESC”);
else Console::Write(L “Không phải phím ESC”);
Clear() : Hàm không có đối số.Xóa toàn bộ màn hình Console
VD : Console::Clear ();
Trang 6
3) Biến và Con trỏ :
a) Biến:
Các biến chúng ta đã biết và sử dụng trước đây đều là biến có kích thước và kiểu dữ liệu xác định Người ta gọi các biến kiểu này là biến tĩnh Khi chúng ta khai báo biến, phần mềm lập trình sẽ tự động tìm một vùng nhớ còn trống trong bộ nhớ để lưu trữ giá trị của biến, kích thước của vùng nhớ tùy thuộc vào kiểu dữ liệu của biến Bất cứ một biến nào cũng có một địa chỉ trong
bộ nhớ của máy tính (địa chỉ này được lưu trữ trong các thanh ghi (register) đặc biệt của CPU)
Khi khai báo biến tĩnh, một lượng ô nhớ cho các biến này sẽ được cấp phát mà không cần biết trong quá trình thực thi chương trình có sử dụng hết lượng ô nhớ này hay không Mặt khác, các biến tĩnh dạng này sẽ tồn tại trong suốt thời gian thực thi chương trình dù có những biến mà chương trình chỉ sử dụng 1 lần rồi bỏ
Các kiểu biến tĩnh : các biến kiểu giá trị như interger, float, decimal, boolean và character
Một số hạn chế có thể gặp phải khi sử dụng các biến tĩnh:
Cấp phát ô nhớ dư, gây ra lãng phí ô nhớ
Cấp phát ô nhớ thiếu, chương trình thực thi bị lỗi
Để tránh những hạn chế trên, ngôn ngữ C++ cung cấp cho ta một loại biến đặc biệt gọi là biến động với các đặc điểm sau:
Chỉ phát sinh trong quá trình thực hiện chương trình chứ không phát sinh lúc bắt đầu chương trình
Khi chạy chương trình, kích thước của biến, vùng nhớ và địa chỉ vùng nhớ được cấp phát cho biến có thể thay đổi
Sau khi sử dụng xong có thể giải phóng để tiết kiệm chỗ trong bộ nhớ
Các kiểu biến động : String, Object, array, interface, delegate …
Tuy nhiên các biến động không có địa chỉ nhất định nên ta không thể truy cập đến chúng được Vì thế, ngôn ngữ C lại cung cấp cho ta một loại biến đặc biệt nữa để khắc phục tình trạng này, đó là biến con trỏ (pointer) với các đặc điểm:
Biến con trỏ không chứa dữ liệu mà chỉ chứa địa chỉ của dữ liệu hay chứa địa chỉ của ô nhớ chứa dữ liệu
Kích thước của biến con trỏ không phụ thuộc vào kiểu dữ liệu, luôn có kích thước cố định
Cú pháp khai báo : type* name;
Trang 7 Trong đó type là kiểu dữ liệu của biến mà con trỏ chứa địa chỉ và name là tên của
int* a = &k; //Con trỏ a đang chứa địa chỉ của biến k
Để truy cập nội dung của ô nhớ mà con trỏ đang chỉ tới, ta sử dụng toán tử gián tiếp *
VD : int y =*a; //Biến y sẽ được gán giá trị của ô nhớ mà con trỏ a đang chỉ tới
Ta có thể cộng (+), trừ (-) 1 con trỏ với 1 số nguyên N nào đó; kết quả trả về là 1 con trỏ Con trỏ này chỉ đến vùng nhớ cách vùng nhớ của con trỏ hiện tại N phần tử (vùng nhớ) Kích thước của vùng nhớ phụ thuộc vào kiểu dữ liệu của biến mà con trỏ chỉ tới (mỗi ô nhớ có kích thước 1 byte)
VD : int k = 10;
int* a = &k;
int* b = a + 3; // Nếu a đang chỉ tới ô nhớ 1000 thì b sẽ chỉ tới ô nhớ có địa
chỉ 1012 (kiểu int có kích thước 32-bit (4 byte), do đó mỗi vùng nhớ của biến kiểu int là
4 ô nhớ; địa chỉ của con trỏ b cách con trỏ a 4 phần tử tương ứng với 12 ô nhớ )
Ta không thể công hai biến con trỏ với nhau nhưng có thể trừ hai biến con trỏ (kết quả là một số nguyên cho biết khoảng cách giữa hai ô nhớ mà hai con trỏ đang trỏ tới)
VD : int k1=10, k2=20;
int *a =&k1, *b =&k2;
int y = a – b; // Nếu a đang chỉ tới ô nhớ 1000 và b chỉ tới ô nhớ 1012 thì y =
Trang 8lý Do đó handle còn có thể được xem là một con trỏ ảo cho phép tương tác với bộ nhớ thông qua CLR
Do không phản ánh địa chỉ thật của đối tượng trên bộ nhớ nên ta không thể thực hiện các phép toán công trừ với số nguyên như pointer
Cơ chế quản lý bộ nhớ của một số ngôn ngữ :
4) String (Kiểu chuỗi kí tự) :
String là kiểu dữ liệu cho phép lưu trữ giá trị dưới dạng một dãy các kí tự Để dễ hình dung, ta có xem String như là một vectơ gồm nhiều phần tử liên tiếp nhau, mỗi phần tử là một kí
tự
Cú pháp khai báo : String^ name; //name là tên biến
Để gán giá trị cho biến kiểu String, ta đặt chuỗi kí tự trong cặp dấu “”
VD : String^ a = “Xinchao”;
Do kích thước của kiểu String là không xác định (vì không biết chính xác chiều dài của chuỗi kí tự) Nên String thuộc kiểu dữ liệu tham chiếu (reference type) và khi khai báo cần có ^ (handle)
Ta có thể truy xuất đến bất kì một phần tử nào trong chuỗi nếu biết vị trí của phần tử đó
VD : String^ a = “Xinchao”;
Char b = a [3]; // b = „c‟ (a[3] có nghĩa là phần tử thứ 4, phần tử đầu tiên là a[0])
Trong C++/CLI, có riêng một Class quản lí kiểu chuỗi là Class String Các biến (variable)
và phương thức (method) cơ bản của Class String :
Trang 9Length : biến có kiểu interger Trả về chiều dài của chuỗi (số lượng các kí tự của chuỗi)
VD : String^ a = “Xinchao” ;
int x = a->Length; // x sẽ có giá trị = 7
Concat() : Hàm nhiều đối số Kiểu dữ liệu trả về là String Thực hiện chức năng nối các
chuỗi lại với nhau
VD : String ^ a = “Welcome”, ^b = “ To ”, ^c = “Viet Nam”;
String ^ d = String::Concat(a,b,c); // d = “Welcome to Viet Nam”
Substring(int n) : Hàm một đối số Kiểu dữ liệu trả về là String Tạo một chuỗi mới từ
chuỗi ban đầu bằng cách loại đi n phần tử đầu tiên
VD : String^ a = “Hello”;
String^ b = a->Substring(2); // b = “llo”
ToUpper() : Hàm không đối số Kiểu dữ liệu trả về là String Thực hiện chức năng chuyển
tất cả các kí tự của chuỗi thành chữ HOA
VD : String ^ a = “Welcome”;
String ^ b = a->ToUpper(); // b= “WELCOME”
ToLower() : Hàm không đối số Kiểu dữ liệu trả về là String Thực hiện chức năng chuyển
tất cả các kí tự của chuỗi thành chữ thường
VD : String ^ a = “Welcome”;
String ^ b = a->ToLower(); // b= “welcome”
Remove(int n, int m) : Hàm hai đối số Kiểu dữ liệu trả về là String Tạo một chuỗi mới từ
chuỗi ban đầu bằng cách loại đi m các phần tử từ vị trí n
VD : String^ a = “Hello Viet Nam”;
String^ b = a->Remove(2,5); // b = “Heiet Nam”
Replace(String^ n, String^ m) : Hàm hai đối số Kiểu dữ liệu trả về là String Tạo một
chuỗi mới từ chuỗi ban đầu bằng cách thay thế chuỗi n bằng chuỗi m
VD : String^ a = “Hello Hell”;
String^ b = a->Replace(“ell”, “i”); // b = “Hio Hi”
Insert(int n, String^ m) : Hàm hai đối số Kiểu dữ liệu trả về là String Tạo một chuỗi mới
từ chuỗi ban đầu bằng cách chèn thêm vào chuỗi m tại vị trí thứ n
VD : String^ a = “Hello”;
Trang 10String^ b = a->Insert(2, “hi”); // b = “Hehillo”
Trim() : Hàm không đối số Kiểu dữ liệu trả về là String Loại bỏ các kí tự khoảng trắng
(spacebar) ở đầu và cuối chuỗi
VD : String^ a = “ Hello VN ”;
String^ b = a->Trim() ; // b = “Hello VN”
5) Array (Kiểu mảng) :
array là kiểu dữ liệu dạng tập hợp (collection) cho phép lưu trữ giá trị dưới dạng một
mảng các phần tử có cùng kiểu dữ liệu Để dễ hình dung, ta có xem array như là một ma trận
nhiều chiều gồm nhiều phần tử, kiểu dữ liệu của mỗi phần tử là bất kì
Với phiên bản VS 2005 (.Net Framework 1.1), muốn sử dụng kiểu mảng cần khai báo
namespace stdcli::language Tuy nhiên từ phiên bản VS 2008 (.Net Framework 2.0) thì không
cần khai báo
Cú pháp khai báo :
Mảng 1 chiều : array <type>^ name; //name là tên biến, type là kiểu dữ liệu của biến
Mảng n chiều : array <type, n>^ name; //n là số chiều của mảng
VD : array <int>^ a; //mảng 1 chiều, phần tử có kiểu interger
array <String^, 2>^ b; //mảng 2 chiều, kiểu dữ liệu là String
array <array<int,2>^,2>^ b; //mảng 2 chiều, mỗi phần tử lại là 1 array 2
chiều
Số chiều tối đa của mảng là 32
Trước khi sử dụng mảng, ta phải thực hiện việc cấp phát bộ nhớ cho mảng Việc cấp phát
bộ nhớ được thực hiện bằng lệnh gcnew()
Cú pháp : name = gcnew array<type,n>(k1,k2,…,kn); // k1,k2,…,kn là số phần tử ứng với chiều thứ 1,2,…,n của mảng
VD : array<int,2>^ M;
M = gcnew array<int,2>(5,3); //mảng 2 chiều 5x3 (5 hàng, 3 cột)
Muốn gán giá trị cho mảng, ta phải gán giá trị cho từng phần tử một Để truy xuất đến từng phần tử của mảng, ta dùng cú pháp :
name[i1,i2,…,in] //name là tên mảng, i1,i2,…,in là vị trí của phần tử trong mảng
VD : array<int,2>^ M = gcnew array<int,2>(5,3);
Trang 11Trong C++/CLI, có riêng một Class quản lí kiểu mảng là Class Array Các biến (variable)
và phương thức (method) cơ bản của Class Array :
Rank : biến có kiểu interger Trả về số chiều của mảng
VD : array<int,2>^ M = gcnew array<int,3>(5,3,2);
int x = M->Rank; // x sẽ có giá trị = 3
GetLength (int i) : hàm 1 đối số Kiểu dữ liệu trả về là interger Trả về số phần tử (kích
thước) chiều thứ i của mảng
VD : array<int,2>^ M = gcnew array<int,2>(5,3);
Sort(array^ x, array^ y) : Hàm hai đối số Không trả về dữ liệu Thực hiện chức năng sắp
xếp thứ tự của 2 mảng 1 chiều dựa vào mảng thứ nhất
VD : array<int>^ M = { 5,1,3,3,2 };
array<Char>^ C = {„A‟, „D‟, „K‟, „E‟, „F”};
Trang 12Array::Sort(M,C); // M = {1,2,3,3,5} C = {„D‟, „F‟, „K‟, „E‟, „A‟}
Phép chia lấy dư (%) chỉ thực hiện trên số nguyên (interger)
++/ đặt trước toán hạng : thực hiện tăng giảm trên toán hạng rồi sau đó mới thực hiện
phép tính
++/ đặt sau toán hạng : thực hiện phép tính trước rồi sau đó mới thực hiện tăng giảm trên
toán hạng
VD : int x = 5, y = 3;
int z = x%y; // z = 2 (5 chia 3 dư 2)
int k = x++%y; // k = 2 , x = 6 ( thực hiện x% y trước rồi mới tăng x)
int t = ++z%y; // t = 0 , z = 3 (tăng z rồi mới thực hiện z%y)
Trang 13! : Phép NOT
&& : Phép AND
|| : Phép OR
VD : int x = 5, y = 3;
int z = x%y; // z = 2 (5 chia 3 dư 2)
int k = x++%y; // k = 2 , x = 6 ( thực hiện x% y trước rồi mới tăng x)
int t = ++z%y; // t = 0 , z = 3 (tăng z rồi mới thực hiện z%y)
c) Toán tử thao tác bit (bitwise) :
Thực hiện các phép tính trên từng bit của một số nguyên (chuyển số nguyên sang số nhị phân rồi thực hiện phép toán, kết quả sau đó được chuyển ngược lại số nguyên)
VD : Byte x = 5, y = 3; // Giá trị nhị phân x = 0000 0101 y = 0000 0011
Byte z = x&y; // z = 0000 0101 & 0000 0011 = 0000 0001 = 1
Byte k = x>>2; // k = 0000 0001 = 1
Byte t = y<<3; // t = 0000 1100 = 12
d) Toán tử điều kiện :
Toán tử điều kiện là toán tử ba ngôi duy nhất trong C++/CLI
Cú pháp : condition ? result 1 : result 2;
Trong đó condition là điều kiện : nếu điều kiện là đúng (true) thì biểu thức sẽ trả về
result1 , nếu điều kiện là sai (false) thì biểu thức sẽ trả về result2
VD : int x = 5, y = 3;
int z = (x > y) ? (x+y) : (x-y); // z = 8 : x > y đúng nên z = x + y
int k = (x < y) ? (x+y) : (x-y); // z = 2 : x < y sai nên z = x - y
e) Toán tử gán :
Thực hiện việc gán giá trị cho các biến Bao gồm 11 toán tử gán
Trang 14= : Gán trực tiếp
+= : Thực hiện công rồi mới gán ( x += a x = x + a)
-= : Thực hiện trừ rồi mới gán ( x -= a x = x - a)
*= : Thực hiện nhân rồi mới gán ( x *= a x = x * a)
/= : Thực hiện chia rồi mới gán ( x /= a x = x / a)
%= : Thực hiện chia lấy dư rồi mới gán ( x %= a x = x % a)
&= : Thực hiện AND rồi mới gán ( x &= a x = x & a)
^= : Thực hiện XOR rồi mới gán ( x ^= a x = x ^ a)
|= : Thực hiện OR rồi mới gán ( x |= a x = x|a)
>>=: Thực hiện dịch phải rồi mới gán ( x >>= a x = x >> a)
<<=: Thực hiện dịch trái rồi mới gán ( x <<= a x = x << a)
f) Toán tử ngoặc đơn (comma) :
Gồm nhiều biểu thức liên tiếp được thực hiện liên tiếp, kết quả là kết quả của biểu thức cuối cùng
VD : int x = 5, y = 3;
int z = (y++, x = y++ * x++, x%y); // z = 4
g) Toán tử địa chỉ, tham chiếu và gián tiếp :
Toán tử địa chỉ (&) : trả về địa chỉ (trong bộ nhớ) của một biến
VD : int x = 5, *y ;
y = &x; // đặt địa chỉ của biến x vào biến con trỏ y
Toán tử gián tiếp (*) : toán tử một ngôi trên biến con trỏ, trả về giá trị đang lưu trữ trong ô
nhớ mà biến con trỏ đang chỉ tới
Trang 15x = 10; // x = 10, y =10 : vì y tham chiếu đến x nên khi x thay
đổi giá trị thì y cũng thay đổi giá tri
int z = y; // z = 10
y = 20; // x = 20, y =20 : khi y thay đổi giá trị thì x cũng thay
đổi giá trị
h) Thứ tự ƣu tiên của các toán tử : (Tham khảo trong tài liệu)
7) Cấu trúc điều khiển (Control Construct) :
a) Cấu trúc điều kiện (Condition Construct) :
Các cấu trúc điều kiện là một đoạn mã lệnh, chương trình chỉ thực hiện đoạn mã lệnh nếu điều kiện của cấu trúc là đúng (true) C++/CLI cung cấp 2 loại cấu trúc điều kiện : if và switch