MSIL cơ bản – Part 6/6: Sử dụng mảng

Mảng là một dãy các khối dữ liệu có cùng kiểu xếp liên tục nhau trong bộ nhớ. Mảng trong .Net được coi như là một đối tượng có kiểu tham chiếu, vì thế bạn có thể truy xuất các property và phương thức của nó. Trong phần này ta sẽ tìm hiểu cách tạo, truy xuất các property và lặp qua các phần tử của mảng.

Mảng một chiều

Để định nghĩa một mảng trong CIL, bạn có dùng các phương thức System.Array.CreateInstance() hoặc System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray() của thư viện mscorlib. Tuy nhiên CIL hỗ trợ cách tạo mảng mà không cần dùng đến các phương thức này

Để tạo một mảng, ta dùng lệnh newarr với tham số là kiểu dữ liệu của mảng. Lệnh này sẽ lấy một giá trị trong stack là sô phần tử của mảng và chỉ được dùng để tạo mảng một chiều.

Ví dụ tạo mảng 3 phần tử kiểu string và lưu vào biến:

.locals init (string[])

ldc.i4.3
newarr string
stloc.0

Ngoài ra ta có thể dùng lệnh newobj để tạo đối tượng thay cho newarr:

ldc.i4.3
newobj instance void string[]::.ctor(int32)
stloc.0

Để gán giá trị ta nạp mảng vào stack, nạp chỉ số của phần tử cần gán giá trị, nạp dữ liệu cần gán và cuối cùng dùng lệnh stelem.xx với xx là kiểu dữ liệu của mảng và là ref nếu là kiểu tham chiếu.

Ví dụ gán chuỗi “a string” cho phần tử đầu tiên của mảng:

ldloc.0
ldc.i4.0
ldstr "a string"
stelem.ref

Gán 10 vào phần tử đầu tiên của mảng:

ldloc.0
ldc.i4.0
ldc.i4.10
stelem.i4

Để lấy giá trị một phần tử của mảng, ta làm tương tự các bước trên nhưng dùng lệnh ldelem. Ví dụ in phần tử đầu tiên của mảng:

ldloc.0
ldc.i4.0
ldelem.ref
call void [mscorlib] System.Console::WriteLine(string)

Đây là một ví dụ hoàn chỉnh để tạo và in ra một mảng 2 phần tử kiểu string:

.assembly extern mscorlib {}
.assembly ArrayTest{}

.method public static void main()
{
	.entrypoint
	.locals init (string[])

	// init array
	ldc.i4.2
	newarr string
	stloc.0

	ldloc.0
	ldc.i4.0
	ldstr “string 1”
	stelem.ref

	ldloc.0
	ldc.i4.1
	ldstr “string 2”
	stelem.ref

	// print out
	ldloc.0
	ldc.i4.0
	ldelem.ref
	call void [mscorlib] System.Console::WriteLine(string)

	ldloc.0
	ldc.i4.1
	ldelem.ref
	call void [mscorlib] System.Console::WriteLine(string)

	ret
}

Output:

string 1
string 2

Lấy chiều dài của mảng:

Việc lấy chiều dài của mảng trong CIL rất dễ dàng, bạn chỉ cần nạp mảng vào stack và gọi lệnh ldlen. Lệnh này không có tham số, nó sẽ tính toán và trả về số phần tử của mảng vào stack. Ta sẽ dùng lệnh này để in ra số phần tử mà bạn truyền cho chương trình thông qua dòng lệnh:

.assembly extern mscorlib {}
.assembly ArrayDemo{}

.method public static void main(class System.String[] args)
{
.entrypoint
.maxstack 1

ldarg.0
ldlen

call void [mscorlib]System.Console::WriteLine(int32)

ret
}

Mảng nhiều chiều

Để tạo mảng nhiều chiều, bạn không thể dùng lệnh newarr mà phải dùng lệnh newobj. Cách tạo tương tự như với mảng một chiều: đầu tiên nạp độ lớn các chiều của mảng cần tạo vào stack, dùng newobj tạo mảng và lưu vào biến cục bộ.

Tạo mảng string 2 chiều với 1 dòng, 2 cột:

.locals (string[,])
ldc.i4.1
ldc.i4.2
newobj instance void string[,]::.ctor(int32,int32)
stloc.0

Tạo mảng string 3 chiều với chỉ số 1, 2, 3:

.locals (string[,,])
ldc.i4.1
ldc.i4.2
ldc.i4.3
newobj instance void string[,,]::.ctor(int32,int32,int32)
stloc.0

Gán giá trị cho một phần tử của mảng: nạp mảng vào stack, nạp các chỉ số tọa độ của phần tử, nạp giá trị cần gán và gọi phương thức Set() của mảng. Phương thức này có các tham số đầu là tọa độ của phần tử và tham số cuối cùng là giá trị cần gán.

Ví dụ gán chuỗi “hello” cho phần tử ở dòng 0 cột 1:

ldloc.0
ldc.i4.0
ldc.i4.1
ldstr "hello"
call instance void string[,]::Set(int32,int32,string)

Để lấy giá trị của một phần tử trong mảng, bạn chỉ cần nạp các chỉ số tọa độ của phần tử đó vào stack và gọi phương thức Get().

Sau đây là ví dụ hoàn chỉnh về mảng 2 chiều:

.assembly extern mscorlib {}
.assembly 2DArray{}

.method static void main()
{
	.entrypoint
	.maxstack 4
	.locals (string[,])

	ldc.i4.1
	ldc.i4.2
	newobj instance void string[,]::.ctor(int32,int32)
	stloc.0

	ldloc.0
	ldc.i4.0
	ldc.i4.1
	ldstr "hello"
	call instance void string[,]::Set(int32,int32,string)

	ldloc.0
	ldc.i4.0
	ldc.i4.1
	call instance string string[,]::Get(int32,int32)
	call void [mscorlib]System.Console::WriteLine(string)

	ret
}

Output:

hello

Để lấy chiều dài của mảng nhiều chiều, bạn gọi phương thức GetLength() của đối tượng mảng với tham số là chiều cần lấy chiều dài.

Ví dụ bạn có thể in ra số cột của mảng 2 chiều bằng cách sau:

ldloc.0
ldc.i4.1
call instance int32 [mscorlib]System.Array::GetLength(int32)
call void [mscorlib]System.Console::WriteLine(int32)

Mảng Jagged

Mảng răng cưa (jagged) là loại mảng được tạo bởi các mảng một chiều. Mỗi phần tử của mảng là một mảng có số phần tử không giống nhau. Trong CIL, bạn tạo mảng loại này tương tự như mảng một chiều.

Ví dụ tạo mảng jagged kiểu string có hai phần tử:

ldc.i4.2
newarr string[]
stloc.0

Tạo một mảng string 4 phần tử cho phần tử thứ nhất của mảng jagged:

ldloc.0
ldc.i4.0
ldc.i4.4
newarr [mscorlib]System.String
stelem.ref

Gán chuỗi “Hello” vào phần tử thứ 2 của phần tử đầu tiên của mảng jagged:

ldloc.0
ldc.i4.0
ldelem.ref
ldc.i4.1
ldstr "Hello"
stelem.ref

Lấy chiều dài của mảng jagged:

ldloc.0
ldlen
call void [mscorlib]System.Console::WriteLine(int32)

Ta có ví dụ hoàn chỉnh sau:

.assembly extern mscorlib {}
.assembly JaggedArray{}

.method static void main()
{
	.entrypoint
	.locals (string[][])

	ldc.i4.2
	newarr string[]
	stloc.0

	ldloc.0
	ldc.i4.0
	ldc.i4.4
	newarr [mscorlib]System.String
	stelem.ref

	ldloc.0
	ldc.i4.1
	ldc.i4.6
	newarr [mscorlib]System.String
	stelem.ref

	ldloc.0
	ldc.i4.0
	ldelem.ref
	ldc.i4.1
	ldstr "Hello"
	stelem.ref

	ldloc.0
	ldc.i4.0
	ldelem.ref
	ldc.i4.1
	ldelem.ref
	call void [mscorlib]System.Console::WriteLine(string)

	ret
}

Output:

Hello

Bài viết liên quan:

MSIL – Part 1: Giới thiệu về MSIL
MSIL – Part 2: Biến cục bộ
MSIL – Part 3: Rẽ nhánh và vòng lặp
MSIL – Part 4: Lập trình hướng đối tượng
MSIL – Part 5: Conversion, casting và boxing
MSIL – Part 6: Sử dụng mảng

https://yinyangit.wordpress.com

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s