C # sharp experience
Nanjing University of Posts and Telecommunications
Li Jianzhong (
Lijianzhong@263.net.cn
)
Eighteen
Non-security code
The .NET universal language is currently C # introduces a managed security programming. Pointer access, variable address calculation, object destruction, etc. Operation is not allowed by C #, which greatly improves traditional C / C security issues in the hosted programming environment. But things are often porous, while we have direct access to memory, etc., we have also lost its convenience on certain issues, such as some and operating system's underlying interactions, memory map devices Access, etc. On some special tasks, we don't even want to introduce automatic garbage collection of this "uncertain system consumption". C # cater to these special needs by introducing a non-secure (unsafe) code.
Non-security code
??????? In the non-secure code, C # allows us to operate the pointer directly, obtain the variable address, perform the conversion between the pointer type and integer type, the operation, often occurs in C / C . Non-secure code flags via the keyword "unsafe". Compile the option "/ unsafe" when compiling non-secure codes, otherwise the compiler error. "Unsafe" keywords can be added to the type (class, structure, interface, delegation) declaration, member (constructor, destructor, domain, method, attribute, event, indexer, operator) declaration, and brace " {} "Enclosed in the statement block. The code block marks the unsafe is also called the Unsafe context. Look at the routine below:
Using system; unsafe struct point // unsafe type {????? public int * x, y; ????? public point (int * a, int * b) ????? {????? ???? x = a; ????????? y = b; ?????}} class test {????? public static void swap (Ref int x, ref int y)? ???? {????????? Int a; ????????? a = x; x = y; y = a; ?????} ????? public Static unsafe void swap (ref int * x, ref int * y) // unsafe member ????? {????????? INT * a; ????????? a = x ; x = y; y = a; ?????} ????? public static void main () ????? {????????? INT A = 1, b = 2; ????????? Unsafe // unsafe statement block ??????????????? Point Pt = New Point (& A, & B); // Initialization ?????????????? console.writeline ("initial value:"); ?????????????? console.writeline ("VALUES:? ? * pt.x = {0}, * pt.y = {1} ", * pt.x, * pt.y); ?????????????? console.writeLine (" Address: pt.x = {0}, pt.y = {1} ", (int) pt.x, (int) pt.y); ???????????????? ???????????? SWAP (Ref * pt.x, ref * pt.y); // exchange value ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? After the value: "); ?????????????? console.writeline (" VALUES: ?? * pt.x = {0}, * pt.y = {1} ", * Pt.x, * pt.y); ?????????????? console.writeline ("Address: pt.x = {0}, pt.y = {1}", (int ) pt.x, (int) pt.y); ???????????????????? ????????????????????? SWAP (Ref pt.x, ref pt.y); // exchange address ????????????????????????? ?? console.writeline ("Dedicated:"); ?????????????? console.writeline ("VALUES: ?? * pt.x = {0}, * pt.y = {1} ", * pt.x, * pt.y); ?????????????? console.writeline (" Address: pt.x = {0}, pt.y = {1} ", (int) pt.x, (int) pt.y); ?????????} ?????}} program output:
Initial value:
VALUES: ?? * pt.x = 1, * pt.y = 2
Address: pt.x = 1243332, pt.y = 1243328
After the value is changed:
VALUES: ?? * pt.x = 2, * pt.y = 1
Address: pt.x = 1243332, pt.y = 1243328
Reed:
VALUES: ?? * pt.x = 1, * pt.y = 2
Address: pt.x = 1243328, pt.y = 1243332
In the above program, we demonstrate the typical three unsafe contexts. We use unsafe to modify the POINT structure type, so that the pointer can be operated in this structure. Note that the members declare statement "public int * x, y;" declaration X and Y are pointers pointing to integers, which need to be in the traditional C / C in front of X, Y front plus star number (*), such as "public INT * X, * Y; ".
After the SWAP method declaration in the Test class is added to the unsafe modification, the parameters of the pointer type can be passed, and the pointer operation is performed in the method.
In the main function, we use the processing method of the UNSAFE statement block, which makes a pointer access operation in the statement block. Two swap functions, a swap pointer pointing to data, a swap pointer address, we can see this from the output of the program.
What needs to be pointed out is that "non-secure code is not unsafe"! It is just to indicate that the memory is not managed by automatic garbage collector, and we need to assign and release itself as before C / C .
Pointer type
??????? The type of pointer is a bit similar to the reference type in our hosting environment, the type itself does not contain data, and the data is included in the memory block pointing. But the type of pointer type and cantory environment has the difference in nature - the data block pointed to by the pointer is not subject to automatic garbage collector, and the data object that reference handle points is subject to automatic garbage collector tracking and management. In fact, the automatic garbage collector does not know the presence of pointers and their data!
??????? The pointer type contains the data type of the memory block pointing, which is limited to "Non-Manage Type" and "Void Type" in C #. Where "Non-Manage Type" is defined as one of the following types:
1. SBYTE, BYTE, SHORT, USHORT, INT, UINT, Long, Ulong, Char, Float, Double, Decimal, and BOOL;
2. ?? Enumeration type;
3. ?? Customize the structure type, its member variable can only be "unmanage type";
4. ?? Pointer type.
It can be seen that "non-host type" cannot be a reference type. In fact, the memory block pointing by the pointer type cannot be a reference type, nor can it be a custom structure containing a reference type. But the reference type can include the pointer type, why? It is not difficult to understand this, because the reference type will be managed by automatic garbage collector, and the "non-host type" pointed to the pointer is not managed by automatic garbage collector!
??????? Although the pointer can express the address (pass reference) in OUT and REF, so that we can change the pointer variable itself in the function (that is, the address of the variable), this article The SWAP function in the first example is done. But like the pass pointer parameters in C / C , if we change the value of the pointer to the address of the local variable in such a function, when we exit the function, due to the system often reclaims these address space, the program will An undefined behavior occurs. Look at the example below:
Using system; class test {????? Unsafe static void f (out INT * PI1, REF INT * PI2) ????? {????????? int i = 100; ???? ????? int J = 200; ????????? pi1 = & i; ????????? pi2 = & j; ????????? console.writeline (( INT) PI1 "," (int) pi2); ?????} ????? Unsafe static void main () ????? {????????? INT i = 10; ?????????????????? INT * PX1 = & i; ????????? INT * PX2 = & i; ????????? console.writeline ((int) PX1 "," (int) PX2); ???????????????????? f (OUT PX1, REF PX2); ???????? ? Console.writeLine ((int) PX1 "," (int) PX2); ????????? console.writeline ("* px1 = {0}, * px2 = {1}", * PX1 , * px2); ?????}} program output:
1243332, 1243332
1243304, 1243308
1243304, 1243308
* px1 = 13249636, * px2 = 13253284
??????? Note The result of the first three lines depends on a specific execution environment, and the last line is an undefined program behavior. It can be seen that the variable I, the storage space of the function f is reclaimed after the exit function stack, the value of the pointer variable PX1, and PX2 will be uncertain. In view of this, we generally do not use the OUT modified pointer type parameters, and only the REF is used to modify the pointer type parameters only after the changed address space is not recycled after the exit function.
??????? The pointer type can be converted between the integer type of 8 C #, which must be clarified in the form of parentheses "()". The empty type "null" is also available as a pointer type, indicating that the address is 0, and does not point to valid data. There is also a clear transformation between pointers pointing to different data types (excluding VOID types). VOID * Types Translate to other managed types of hosted types. Conversely, other pointer types are converted to the VOID * type, which can be implicitly converted. C # does not guarantee that the conversion between the pointer type is secure, and this possible situation can be handled by the C # exception capture mechanism.
??????? and C / C , the pointer type can participate in considerable expressions. In addition to the first priorcted example, a pointer, the address operation (P = & value), and structural members obtain operation (P-> Value), pointer element acquisition operation (P [0]) , Pointer increment and reduction operation (P , P -, P, - P), Sizeof operation (unmanaged-type), comparison between pointers, and pointers and integers (int, uint , line, ulong, between the decimalization and subtraction between the pointers. These expressions are required to be placed in the code of the UNSAFE flag, otherwise the compile time is incorrect.
Fixed
Statement
??????? From the perspective of memory management, the variables in the C # can be divided into two categories. One type is a fixed variable, and the other is the movable variable. The fixed variable is typically boarded in the stack of the method, and their memory is not managed by Automatic garbage collector.
Examples of fixed variables include locally variables of the non-hosting type and its real-purpose domain members, transmitted value parameters of the unmanaged type, and the variables created by the protocol. A method of discriminating a fixed variable is to see if it can obtain its address without any restrictions. Look at the routines below: use system; struct point {????? public int x; ????? public int x; ????? public int z;} unsafe class test {????? static Void main () ????? {????????? Int integer = 100; ????????????printaddress (Integer); ????????? INT * PTINTEGER = & integer; ????????? console.writeline ((int) & (* ptinteger)); / / Profit the variable created by the pointer ????????? Point Point = New point (); ????????? console.writeLine ((int)); // unmanage type local variable ????????? console.writeLine ((int)) & point.x ); // instance member of the non-host local variable ????????? console.writeLine (int) & point); // unscrupulous instance member of the unmanaged type part of the local variable ??????? ?? console.writeline ((int) & point.z); // instance member of the unmanaged type part of the local variable ?????} ????? static void printaddress (INT i) ????? {?? ??????? console.writeline ((int) & i); // The transmitted value parameter of the unmanaged type ?????}}
??????? Mobility variables are boarded in the hosted stack, and their memory is fully controlled by automatic garbage collector. An example of a movable variable has an instance domain member of the reference type object, any type of static domain member, reference, or output parameters, array elements, and the like. You cannot directly use the address of the movable variable directly, C # supports the address of the movable variable with the Fixed statement to temporarily obtain the movable variable. This address is only valid only in the statement block followed behind Fixed. Look at the routine below:
Using system; class myclass {????????? public static int staticfield; ????? public int instancefield;} Unsafe class test {????? static void printaddress (int * point)??? ?? {????????? console.writeline ((int) point); ?????} ????? public static void main () ????? {????? ???? myclass myobject = new myclass (); ?????????? INT [] arr = new int [10]; ????????? fixed (int * p = & myclass.staticfield ) PRINTADDRESS (P); ????????? Fixed (int * p = & myObject.instancefield) PrintAddress (p); ????????? fixed (int * p = & arr [0]) PrintAddress (P); ????????? Fixed (int * p = arr) PrintDress (P); ?????}}
Sizeof
Operator
??????? The SizeOf operator can be used to calculate the storage size of the non-hosting type in the stack assignment, and its result is an integer in bytes. The operating object of the SIZEOF operator is a managed type, and the variable cannot be a parameter of the SIZEOF operator.
??????? For the simple type in C #, since their storage space has been determined by the system, SizeOf returns to their accounting number of bytes. For custom enumeration types, SIZEOF returns the storage size of the base type specified by the enumeration type. If no enumeration class is specified, the C # defaults to 32-bit integer types. For pointer types, its storage size and 32-bit integer type are the same, natural SIZEOF will return 4 bytes. Look at the following routines: use system; enum myenum: Byte {????? Beijing, ????? Shanghai, ????? Nanjing} Class test {????? public unsafe static void main () ????? {????????? console.write ("SIZEOF (Sbyte): {0} ?? / t", sizeof (sbyte)); ????????? console .Writeline ("SIZEOF (Byte) sizeof (byte)); ????????? console.write (" SIZEOF (Short): {0} ?? / t ", sizeof (short)); ????????? console.writeline ("SIZEOF (Ushort): sizeof (ushort)); ????????? console.write (" sizeof (int): {0}? ?? / t ", sizeof (int)); ????????? console.writeline (" SIZEOF (UINT): " sizeof (uint)); ????????? console. Write ("SIZEOF (long): {0} ??? / t", sizeof (long)); ????????? console.writeline ("SIZEOF (ULONG):" Sizeof (Ulong)) ; ????????? console.write ("sizeof (char): {0} ??? / t", sizeof (char)); ????????? console.writeline (" SizeOf: " sizeof (bool)); ????????? console.write (" sizeof (float): {0} ??? / t ", sizeof (float)); ?? ??????? console.writeline ("sizeof (double): sizeof (double)); ????????? console.write (" sizeof (myenum): {0} ??? /T" ,sizeof (MYENUM) ");??????????console.writeline ("sizeof(int*): {0} ??? / t", sizeof (int *));??? ??}}
Program output:
Sizeof (sbyte): 1 ????????? Sizeof (Byte) 1
Sizeof (Short): 2 ????????? Sizeof (Ushort): 2
Sizeof (int): 4 ?????????? Sizeof (UINT): 4
Sizeof (long): 8 ?????????? sizeof (ulong): 8
SIZEOF (Char): 2 ?????????? SizeOf (BOOL): 1
Sizeof (float): 4 ?????????? sizeof (double): 8
SIZEOF (MyEnum): 1 ?????????? sizeof (int *): 4 ?????????
For hosted custom structural types, SizeOf is calculated when the storage size is calculated, considering the storage space of each domain member variable in the arrangement. If there is no StructLayout feature to explicitly specify, the storage arrangement of the domain member variable of managed custom structural type will be responsible by the CLR runtime, and it is often a certain amount of storage to the structure of the storage space, that is to say. It is worth noting that the order of member variables declares is different, and it can also cause different arrangements for the CLR runtime, thereby there will be different fill behaviors, which may also cause the SIZEOF return value. For managed custom structures specified with StructLayout characteristics, SizeOf is to consider the problem of alignment while considering the specified requirements. Look at the routines below: use system; use system.Runtime.Interopservices; struct mystruct1 {????? public byte mybyte; // 1 Byte ????? public short myshrt; // 2 Bytes ????? Public int myint; // 4 Bytes ????? public long mylong; // 8 bytes} Struct MyStruct2 {????? public short myshort; // 2 BYTES ????? public int myint; // 4 BYTES ????? public long mylong; // 8 Bytes ????????? public byte mybyte; // 1 byte} [structLayout (layoutkind.explicit)] struct myunion {????? [Fieldoffset (0)] ????? public byte mybyte; // start from zero byte ????? [fieldoffset (0)] ????? public short myshrth; // Starting from zero? ???? [fieldoffset (0)] ????? public int myint; // started from zero bits [Fieldoffset (0)] ????? public long mylong; // LONG} class test {???????? {???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????. SIZEOF (MyStruct1)); ????????? console.writeline ("SIZEOF (mYStruct2): {0}, sizeof (mystruct2)); ????????? console.writeline "SIZEOF (Myunion): {0}", sizeof (myunion)); ?????}}
Program output:
Sizeof (MyStruct1): 16
Sizeof (MyStruct2): 24
SizeOf (Myunion): 8
??????? We see the same non-hosting custom structure for all domain variables MYSTRUCT1 and MYSTRUCT2, only because we put the NBYTE domain's declaration positions in different places, their SizeOf's return value is different. . MyUnion Structure Since we specify the storage layout of its domain member with the StructLayout feature, its SIZEOF return value and the return value of the longest domain variable Mylong in which it is the same, which is consistent as "United".
Memory allocation
??????? C # There is no memory dynamic allocation syntax like C / C (allocated on the heap), only the stack allocation statement applied to local unmanaged type variables: StackAlloc unmanaged-type [expression], where Expression is an expression or constant of an integer. The stack allocation space does not require our own clear, and the function is automatically reclaimed after exiting. Look out below: use system; class test {????? Unsafe static string intentring (int value) ????? {????????? char * buffer = stackalloc char [16];? ????????????????????????????? INT n = value> = 0? value: -view; ????????? do ?? ??????? {?????????????? * (- p) = (char) (N% 10 '0'); ???????? ?????? n / = 10; ??????????} while (n! = 0); ????????? IF (value <0) * - p = ' - '; ????????? Return New String (p, 0, (int)); ?????} ????? public static void main ()? ???? {????????? console.writeline (INTSTOSTRING (12345)); ????????? console.writeline (inteltString (-999)); ?????} }
Function INTSTRING implements integers to string conversion, where we assign the buffer buffer character array to temporarily store the transformed character variables. If the system is not in full, the stack assignment statement will throw the System.StackoverflowException exception. This can be captured with a TRY statement. It is worth pointing out that the C # specified stack assignment statement cannot be placed in a catch or finally.
??????? C # does not provide dynamic allocation syntax, but if we need to do? The answer is to call the dynamic allocation service of a particular platform by introducing an external approach. Look at the example below:
Using system.runtime.interopservices; use system; public unsafe class memory {????? static int pH = getProcessheap (); // get the handle of the process stack ?????prate memory () {} ???? ? public static void * alloc (int size) // memory allocation ????? {???????? void * result = Heapalloc (pH, heap_zero_memory, size); ???????? ? if (result == null) throw new outofMemoryException (); ????????? Return Result; ?????} ????? public static void free (void * block) // memory release ????? {????????? IF (! HeapFree (pH, 0, block) throw new invalidopertyleException (); ?????} ????? const INT Heap_Zero_Memory = 0x00000008; // Memory start address ????? ["kernel32")] ????? static extern int getProcessHeap (); ????? [DLLIMPORT ("kernel32")] ????? static EXTERN VOID * Heapalloc (int hHEAP, INT FLAGS, INT Size); ????? [DLLIMPORT ("kernel32")] ????? static extern bool heapfree (int hHeap, int flags, void * block); Class test {????? Unsafe static void main () ????? {????????? Byte * buffer = (byte *) Memory.alloc (256); ?????? ??? for (int i = 0; i <256; i ) ?????????????? buffer [i] = (byte) i; ????????? (INT i = 0; i <256; i ) ?????????????? console.writeline (buffer [i]); ????????? Memory.Free (buffer); ????? }} We realize the dynamic memory allocation and release function of the Memory class by calling the GetProcessHeap, HeapAlloc and HeapFree in the kernel32.dll library on the Win32 platform. Note that this is the mortality allocation on the heap, we must be responsible for the release!