Con trỏ trong C # – GraphGuide.org

C # hỗ trợ con trỏ ở một mức độ hạn chế. Con trỏ AC # không là gì ngoài một biến chứa địa chỉ bộ nhớ của một kiểu khác. Nhưng trong C # con trỏ chỉ có thể được khai báo để giữ địa chỉ bộ nhớ của kiểu giá trị và mảng. Không giống như các kiểu tham chiếu, các kiểu con trỏ không được theo dõi bởi cơ chế thu gom rác mặc định. Vì lý do tương tự, con trỏ không được phép trỏ đến kiểu tham chiếu hoặc thậm chí đến kiểu cấu trúc có chứa kiểu tham chiếu. Chúng ta có thể nói rằng con trỏ chỉ có thể trỏ đến các kiểu không được quản lý bao gồm tất cả các kiểu dữ liệu cơ bản, kiểu enum, các kiểu và cấu trúc con trỏ khác chỉ chứa các kiểu không được quản lý.

Khai báo kiểu con trỏ

Hình thức chung của việc khai báo một kiểu con trỏ như được hiển thị bên dưới,

kiểu * tên_biến;

Trong đó * được gọi là toán tử khử tham chiếu. Ví dụ câu lệnh sau

int * x;

Khai báo một biến con trỏ x, có thể chứa địa chỉ của kiểu int. Toán tử tham chiếu (&) có thể được sử dụng để lấy địa chỉ bộ nhớ của một biến.

& X cung cấp địa chỉ bộ nhớ của biến x, mà chúng ta có thể gán cho một biến con trỏ

  1. int * ptr = & x ;.
  2. Console.WriteLine ((int) ptr) // Hiển thị địa chỉ bộ nhớ
  3. Console.WriteLine (* ptr) // Hiển thị giá trị tại địa chỉ bộ nhớ.

Mã không an toàn

Các câu lệnh C # có thể được thực thi như trong một an toàn hoặc trong một ngữ cảnh không an toàn. Các câu lệnh được đánh dấu là không an toàn bằng cách sử dụng từ khóa không an toàn sẽ chạy ngoài sự kiểm soát của Garbage Collector. Hãy nhớ rằng trong C # bất kỳ mã nào liên quan đến con trỏ đều yêu cầu một ngữ cảnh không an toàn.

Chúng ta có thể sử dụng từ khóa không an toàn theo hai cách khác nhau. Nó có thể được sử dụng như một bổ ngữ cho một phương thức, thuộc tính và hàm tạo, v.v. Ví dụ

  1. // Tác giả: [email protected]
  2. sử dụng Hệ thống;
  3. lớp MyClass
  4. {
  5. công khai không an toàn Phương pháp void ()
  6. {
  7. int x = 10;
  8. int y = 20;
  9. int * ptr1 = & x;
  10. int * ptr2 = & y;
  11. Console.WriteLine ((int) ptr1);
  12. Console.WriteLine ((int) ptr2);
  13. Console.WriteLine (* ptr1);
  14. Console.WriteLine (* ptr2);
  15. }
  16. }
  17. lớp MyClient
  18. {
  19. public static void Main ()
  20. {
  21. MyClass mc = new MyClass ();
  22. mc.Method ();
  23. }
  24. }

Từ khóa không an toàn cũng có thể được sử dụng để đánh dấu một nhóm các câu lệnh là không an toàn như được hiển thị bên dưới.

  1. // Tác giả: [email protected]
  2. // khối không an toàn
  3. sử dụng Hệ thống;
  4. lớp MyClass
  5. {
  6. public void Method ()
  7. {
  8. không an toàn
  9. {
  10. int x = 10;
  11. int y = 20;
  12. int * ptr1 = & x;
  13. int * ptr2 = & y;
  14. Console.WriteLine ((int) ptr1);
  15. Console.WriteLine ((int) ptr2);
  16. Console.WriteLine (* ptr1);
  17. Console.WriteLine (* ptr2);
  18. }
  19. }
  20. }
  21. lớp MyClient
  22. {
  23. public static void Main ()
  24. {
  25. MyClass mc = new MyClass ();
  26. mc.Method ();
  27. }
  28. }

Ghim một đối tượng

Trình thu gom rác C # có thể di chuyển các đối tượng trong bộ nhớ theo ý muốn trong quá trình thu gom rác. C # cung cấp một từ khóa đặc biệt cố định để yêu cầu Bộ thu gom rác không di chuyển một đối tượng. Điều đó có nghĩa là điều này sẽ sửa chữa vị trí của các loại giá trị được trỏ đến trong bộ nhớ. Đây là những gì được gọi là ghim trong C #.

Câu lệnh cố định thường được thực hiện bằng cách tạo các bảng mô tả cho Bộ thu gom rác, các đối tượng nào sẽ được giữ cố định trong vùng nào của mã thực thi. Do đó, miễn là quá trình Garbage Collector không thực sự xảy ra trong quá trình thực thi các câu lệnh cố định, thì sẽ có rất ít chi phí liên quan đến điều này. Tuy nhiên, khi quá trình Garbage Collector xảy ra, các đối tượng cố định có thể gây ra sự phân mảnh của đống. Do đó các đối tượng chỉ nên được cố định khi thực sự cần thiết và chỉ trong khoảng thời gian ngắn nhất.

Con trỏ & Phương thức

Các điểm có thể được chuyển như một đối số cho một phương thức như hiển thị bên dưới. Các phương thức cũng có thể trả về một con trỏ.

  1. // Tác giả: [email protected]
  2. sử dụng Hệ thống;
  3. lớp MyClass
  4. {
  5. công khai không an toàn void Method ()
  6. {
  7. int x = 10;
  8. int y = 20;
  9. int * sum = swap (& x, & y);
  10. Console.WriteLine (* sum);
  11. }
  12. công khai không an toàn int * swap (int * x, int * y)
  13. {
  14. int sum;
  15. tổng = * x + * y;
  16. trở lại ∑
  17. }
  18. }
  19. lớp MyClient
  20. {
  21. public static void Main ()
  22. {
  23. MyClass mc = new MyClass ();
  24. mc.Method ();
  25. }
  26. }

Con trỏ & Chuyển đổi

Trong C #, kiểu con trỏ không kế thừa từ đối tượng và không tồn tại chuyển đổi giữa kiểu con trỏ và đối tượng. Điều đó có nghĩa là quyền anh và không quyền anh không được con trỏ hỗ trợ. Nhưng C # hỗ trợ chuyển đổi giữa các loại con trỏ khác nhau và các loại con trỏ và các loại tích phân.

C # hỗ trợ cả chuyển đổi con trỏ ẩn và rõ ràng trong ngữ cảnh không an toàn. Các chuyển đổi ngầm định là

  1. Từ bất kỳ kiểu con trỏ kiểu nào thành kiểu void *.
  2. Từ kiểu null sang kiểu con trỏ bất kỳ.

Toán tử ép kiểu (()) là cần thiết cho bất kỳ chuyển đổi kiểu rõ ràng nào. Các chuyển đổi loại rõ ràng là

  1. Từ bất kỳ loại con trỏ nào đến bất kỳ loại con trỏ nào khác.
  2. Từ sbyte, byte, short, ushort, int, uint, long, ulong đến bất kỳ loại con trỏ nào.
  3. Từ bất kỳ loại con trỏ nào đến các loại sbyte, byte, short, ushort, int, uint, long, ulong.

Ví dụ

  1. char c = ‘R’;
  2. char * pc = & c;
  3. void * pv = pc; // Chuyển đổi ngầm định
  4. int * pi = (int *) pv; // Chuyển đổi rõ ràng bằng cách sử dụng toán tử truyền

Số học con trỏ

Trong ngữ cảnh không an toàn, các toán tử ++ và – có thể được áp dụng cho biến con trỏ thuộc mọi kiểu ngoại trừ kiểu void *. Vì vậy, đối với mọi kiểu con trỏ T *, các toán tử sau đây được nạp chồng ngầm.

  1. Toán tử T * ++ (T * x);
  2. Toán tử T * – (T * x);

Toán tử ++ thêm sizeof (T) vào địa chỉ chứa trong biến và – toán tử trừ sizeof (-) khỏi địa chỉ chứa trong biến cho một biến con trỏ kiểu T *.

Trong ngữ cảnh không an toàn, một hằng số có thể được thêm vào hoặc trừ khỏi một biến con trỏ. Tương tự, một biến con trỏ có thể bị trừ khỏi một biến con trỏ khác. Nhưng không thể thêm hai biến con trỏ trong C #.

Trong bối cảnh không an toàn = =,! Các toán tử =, <,>, <=,> = có thể được áp dụng cho các kiểu giá trị của tất cả các kiểu con trỏ. Không thể nhân và chia một biến con trỏ với một hằng số hoặc với một biến con trỏ khác trong C #.

Phân bổ ngăn xếp

Trong bối cảnh không an toàn, một khai báo cục bộ có thể bao gồm một bộ khởi tạo cấp phát ngăn xếp, cấp phát bộ nhớ từ ngăn xếp cuộc gọi.

T stackalloc T[E] yêu cầu T là một kiểu không được quản lý và E là một biểu thức của kiểu int. Cấu trúc trên phân bổ các byte E * sizeof (T) từ ngăn xếp cuộc gọi và tạo ra một con trỏ kiểu T * cho khối mới được cấp phát. E là âm, một System.OverFlowException được ném ra. Nếu không có đủ bộ nhớ để cấp phát, một System.StackOverflowException sẽ được ném ra.

Nội dung của bộ nhớ mới được cấp phát là không xác định. Không có cách nào để giải phóng bộ nhớ được cấp phát một cách hoàn toàn bằng cách sử dụng stackalloc. Thay vào đó, tất cả khối bộ nhớ được cấp phát ngăn xếp sẽ tự động bị loại bỏ khi hàm trả về.

  1. kiểm tra lớp
  2. {
  3. char * đệm =
  4. }

Con trỏ & Mảng

Trong C #, các phần tử của mảng có thể được truy cập bằng cách sử dụng ký hiệu con trỏ.

  1. // Tác giả: [email protected]
  2. sử dụng Hệ thống;
  3. lớp MyClass
  4. {
  5. công khai không an toàn void Method ()
  6. {
  7. int[] iArray = int mới[10];
  8. for (int count = 0; count <10; count ++)
  9. {
  10. iArray[count] = count * đếm;
  11. }
  12. cố định (int * ptr = iArray)
  13. Hiển thị (ptr);
  14. //Console.WriteLine(*(ptr+2));
  15. //Console.WriteLine((int)ptr);
  16. }
  17. công khai không an toàn void Display (int * pt)
  18. {
  19. for (int i = 0; i <14; i ++)
  20. {
  21. Console.WriteLine (* (pt + i));
  22. }
  23. }
  24. }
  25. lớp MyClient
  26. {
  27. public static void Main ()
  28. {
  29. MyClass mc = new MyClass ();
  30. mc.Method ();
  31. }
  32. }

Con trỏ & cấu trúc

Các cấu trúc trong C # là các kiểu giá trị. Các con trỏ có thể được sử dụng với các cấu trúc nếu nó chỉ chứa các kiểu giá trị làm thành viên của nó. Ví dụ

  1. // Tác giả: [email protected]
  2. sử dụng Hệ thống;
  3. struct MyStruct
  4. {
  5. công khai int x;
  6. công khai int y;
  7. public void SetXY (int i, int j)
  8. {
  9. x = i;
  10. y = j;
  11. }
  12. public void ShowXY ()
  13. {
  14. Console.WriteLine (x);
  15. Console.WriteLine (y);
  16. }
  17. }
  18. lớp MyClient
  19. {
  20. public không an toàn static void Main ()
  21. {
  22. MyStruct ms = new MyStruct ();
  23. MyStruct * ms1 = & ms;
  24. ms1-> SetXY (10, 20);
  25. ms1-> ShowXY ();
  26. }
  27. }