程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C# 之不安全代碼(2)

C# 之不安全代碼(2)

編輯:關於C#

固定的和活動的變量

&和fixed將變量分為兩類:固定的和活動的。

固定的變量永久的存在並不會被GC的操作影響。(例如局部變量,值參數和間接指針)活動的變量會被GC重新分配位置或是釋放(例如對象中的字段,基礎數據數組)。

&取固定變量的地址是沒有限制的。但是活動變量容易受GC的影響,其地址只能通過fixed來獲得,並且地址僅會在整個fixed語句的周期持續有效。

靜態字段也是活動變量。還有標記為ref或out的也是活動變量,即使這個參數是傳遞給固定參數的。最後,通過間接指針引用的變量總是固定變量。

指針轉換

在不安全上下文中,下面的轉換是隱式的:

l  從任何指針類型到void*

l  從null到任何指針類型

除此之外,下面的轉換都是顯式的:

l  從任何指針類型到另外的指針類型

l  從sbyte, byte, short, ushort, int, uint, long, or ulong到其他類型

l  從任何類型到sbyte, byte, short, ushort, int, uint, long, or ulong

指針轉換並不改變指針的值。也就是說指針轉換前後不影響地址的值。

當轉換發生後,當結果指針沒有按照正確的指針類型排列的話,當訪問結果時結果是無法預知的。

下面的例子:

1 char c = 'A';
2 char* pc = &c;
3 void* pv = pc;
4 int* pi = (int*)pv;
5 int i = *pi;   // undefined
6 *pi = 123456;  // undefined
7

下面的例子會打印出double的8字節的值:

1 using System;
2 class Test
3 {
4 unsafe static void Main() {
5  double d = 123.456e23;
6   unsafe {
7    byte* pb = (byte*)&d;
8    for (int i = 0; i < sizeof(double); ++i)
9      Console.Write("{0:X2} ", *pb++);
10    Console.WriteLine();
11  }
12 }
13 }
14 

打印結果取決於endian。

指針數組

在不安全上下文中,是允許指針數組的,只有幾種用於其轉換是允許的:

l  從任何數組類型到System.Array或是實現了其接口的隱式引用類型轉換同樣適用於指針數組。但是,任何試圖通過System.Array或是實現了其接口訪問數組元素都會引發一個 運行時錯誤,因為指針類型不能轉化為object。

l  從一個一維數組類型S[]到System.Collections.Generic.IList<T>或是到其基接口,任何顯示或是隱式的轉換都是不行的。因為指針類型不能被用作類型參數,還有沒有從指針類型到非指針類型的轉換。

l  從System.Array或是實現了其接口到任何數組類型顯示引用轉換適用於指針數組。

l  從System.Collections.Generic.IList<T>或是到其基接口到一個一維數組類型T[],任何顯示的轉換都是不行的。原因同上面第2條。

還有就是對於foreach語句,不適用於指針數組。相反,下面的語句

foreach (V v in x) embedded-statement

中的x是一個數組類型T[,,…,],n是數組的維度減1,T和V是指針類型,被改寫為:

{
 T[,,…,] a = x;
 V v;
 for (int i0 = a.GetLowerBound(0); i0 <= a.GetUpperBound(0); i0++)
 for (int i1 = a.GetLowerBound(1); i1 <= a.GetUpperBound(1); i1++)
 …
 for (int in = a.GetLowerBound(n); in <= a.GetUpperBound(n); in++) {
  v  = (V)a.GetValue(i0,i1,…,in);
  embedded-statement
 }
}

變量a,i0,i1,…對於x或者embedded-statement或者其余部分的代碼是不可見的或是不可訪問的。變量v在embedded-statement中是只讀的。如果沒有顯示轉換從T到V,那麼就會有錯誤。如果x是個null,就會有空引用異常。

表達式中的指針

在不安全上下文中,一個表達式的值可以來自於一個指針類型;但是在上下文之外,會造成編譯時期錯誤。

間接訪問

一元的*表示一個指針,被用來獲得指針指向的值。*用在void*類型表達式或是非指針類型表達式時,會造成編譯期錯誤。

*被用在null指針時是由實現來決定的。不能保證在使用時會拋出System.NullReferenceException。

如果一個非法的值賦給指針,那麼*的行為是不可預知的。

指針成員訪問

在指針成員訪問P->I中,P必須是除了void*之外的類型,I同時必須是一個可訪問的成員。

P->I效果上相同於(*P).I。例如:

using System;
struct Point
{
 public int x;
 public int y;
 public override string ToString() {
  return "(" + x + "," + y + ")";
 }
}
class Test
{
 static void Main() {
  Point point;
  unsafe {
   Point* p = &point;
   p->x = 10;
   p->y = 20;
   Console.WriteLine(p->ToString());
  }
 }
}

或是:

class Test
{
 static void Main() {
  Point point;
  unsafe {
   Point* p = &point;
   (*p).x = 10;
   (*p).y = 20;
   Console.WriteLine((*p).ToString());
  }
 }
}

指針元素訪問

在指針元素訪問P[E]中,P必須是除了void*之外的類型,同時E必須能隱式的轉換為int, uint, long, or ulong的表達式。

P[E]效果上同*(P + E).例如:

1 class Test
2 {
3  static void Main() {
4   unsafe {
5     char* p = stackalloc char[256];
6     for (int i = 0; i < 256; i++) p[i] = (char)i;
7   }
8  }
9 }
10 又如:
11 class Test
12 {
13 static void Main() {
14  unsafe {
15   char* p = stackalloc char[256];
16   for (int i = 0; i < 256; i++) *(p + i) = (char)i;
17  }
18 }
19 }
20

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved