1. Trang chủ
  2. » Công Nghệ Thông Tin

Addison Essential Csharp_9 docx

96 354 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 96
Dung lượng 3,23 MB

Nội dung

ptg Pointers and Addresses 835 If the data is an unmanaged variable type but is not fixed, then use the fixed statement to fix a moveable variable. Fixing Data To retrieve the address of a moveable data item, it is necessary to fix, or pin, the data, as demonstrated in Listing 20.14. Listing 20.14: Fixed Statement byte[] bytes = new byte[24]; fixed (byte* pData = &bytes[0]) // pData = bytes also allowed { // } Within the code block of a fixed statement, the assigned data will not move. In this example, bytes will remain at the same address, at least until the end of the fixed statement. The fixed statement requires the declaration of the pointer variable within its scope. This avoids accessing the variable outside the fixed state- ment, when the data is no longer fixed. However, it is the programmer’s responsibility to ensure that he doesn’t assign the pointer to another vari- able that survives beyond the scope of the fixed statement—possibly in an API call, for example. Similarly, using ref or out parameters will be prob- lematic for data that will not survive beyond the method call. Since a string is an invalid referent type, it would appear invalid to define pointers to strings. However, as in C++, internally a string is a pointer to the first character of an array of characters, and it is possible to declare pointers to characters using char*. Therefore, C# allows declar- ing a pointer of type char* and assigning it to a string within a fixed statement. The fixed statement prevents the movement of the string dur- ing the life of the pointer. Similarly, it allows any moveable type that sup- ports an implicit conversion to a pointer of another type, given a fixed statement. You can replace the verbose assignment of &bytes[0] with the abbrevi- ated bytes, as shown in Listing 20.15. From the Library of Wow! eBook ptg Chapter 20: Platform Interoperability and Unsafe Code836 Listing 20.15: Fixed Statement without Address or Array Indexer byte[] bytes = new byte[24]; fixed (byte* pData = bytes) { // } Depending on the frequency and time to execute, fixed statements have the potential to cause fragmentation in the heap because the garbage col- lector cannot compact fixed objects. To reduce this problem, the best practice is to pin blocks early in the execution and to pin fewer large blocks rather than many small blocks. Unfortunately, this has to be tempered with pinning as little as possible for as short a time as possible, to minimize the chance that a collection will happen during the time that the data is pinned. To some extent, .NET 2.0 reduces the problem, due to some addi- tional fragmentation-aware code. Allocating on the Stack You should use the fixed statement on an array to prevent the garbage col- lector from moving the data. However, an alternative is to allocate the array on the call stack. Stack allocated data is not subject to garbage collec- tion or to the finalizer patterns that accompany it. Like referent types, the requirement is that the stackalloc data is an array of unmanaged types. For example, instead of allocating an array of bytes on the heap, you can place it onto the call stack, as shown in Listing 20.16. Listing 20.16: Allocating Data on the Call Stack byte* bytes = stackalloc byte[42];} Because the data type is an array of unmanaged types, it is possible for the runtime to allocate a fixed buffer size for the array and then to restore that buffer once the pointer goes out of scope. Specifically, it allocates sizeof(T) * E, where E is the array size and T is the referent type. Given the requirement of using stackalloc only on an array of unmanaged types, the runtime restores the buffer back to the system simply by unwinding the stack, eliminating the complexities of iterating over the f-reachable queue (see Garbage Collection and Finalization in Chapter 9) and compacting reachable data. Therefore, there is no way to explicitly free stackalloc data. From the Library of Wow! eBook ptg Pointers and Addresses 837 Note that the stack is a precious resource and, although small, running out of stack space will result in a program crashing; every effort should be taken to avoid running out. If a program does run out of stack space, the best thing that can happen is for the program to shut down/crash immediately. Gener- ally, programs have less than 1MB of stack space (possibly a lot less). There- fore, take great care to avoid allocating arbitrarily sized buffers on the stack. Dereferencing a Pointer Accessing the data stored in a variable of a type referred to by a pointer requires that you dereference the pointer, placing the indirection operator prior to the expression. byte data = *pData;, for example, dereferences the location of the byte referred to by pData and returns the single byte at that location. Using this principle in unsafe code allows the unorthodox behavior of modifying the “immutable” string, as shown in Listing 20.17. In no way is this recommended, but it does expose the potential of low-level memory manipulation. Listing 20.17: Modifying an Immutable String string text = "S5280ft"; Console.Write("{0} = ", text); unsafe // Requires /unsafe switch. { fixed (char* pText = text) { char* p = pText; *++p = 'm'; *++p = 'i'; *++p = 'l'; *++p = 'e'; *++p = ' '; *++p = ' '; } } Console.WriteLine(text); The results of Listing 20.17 appear in Output 20.2. OUTPUT 20.2: S5280ft = Smile From the Library of Wow! eBook ptg Chapter 20: Platform Interoperability and Unsafe Code838 In this case, you take the original address and increment it by the size of the referent type (sizeof(char)), using the preincrement operator. Next, you dereference the address using the indirection operator and then assign the location with a different character. Similarly, using the + and – operators on a pointer changes the address by the * sizeof(T) operand, where T is the referent type. Similarly, the comparison operators (==, !=, <, >, <=, and =>) work to compare pointers translating effectively to the comparison of address location values. One restriction on the dereferencing operator is the inability to derefer- ence a void*. The void* data type represents a pointer to an unknown type. Since the data type is unknown, it can’t be dereferenced to another type. Instead, to access the data referenced by a void*, you must convert it to any other pointer type variable and then dereference the later type, for example. You can achieve the same behavior as Listing 20.17 by using the index operator rather than the indirection operator (see Listing 20.18). Listing 20.18: Modifying an Immutable with the Index Operator in Unsafe Code string text; text = "S5280ft"; Console.Write("{0} = ", text); Unsafe // Requires /unsafe switch. { fixed (char* pText = text) { pText[1] = 'm'; pText[2] = 'i'; pText[3] = 'l'; pText[4] = 'e'; pText[5] = ' '; pText[6] = ' '; } } Console.WriteLine(text); The results of Listing 20.18 appear in Output 20.3. OUTPUT 20.3: S5280ft = Smile From the Library of Wow! eBook ptg Summary 839 Modifications such as those in Listing 20.17 and Listing 20.18 lead to unexpected behavior. For example, if you reassigned text to "S5280ft" following the Console.WriteLine() statement and then redisplayed text, the output would still be Smile because the address of two equal string literals is optimized to one string literal referenced by both variables. In spite of the apparent assignment text = "S5280ft"; after the unsafe code in Listing 20.17, the internals of the string assignment are an address assignment of the modified "S5280ft" location, so text is never set to the intended value. Accessing the Member of a Referent Type Dereferencing a pointer makes it possible for code to access the members of the referent type. However, this is possible without the indirection oper- ator (&). As Listing 20.19 shows, it is possible to directly access a referent type’s members using the -> operator (that is, a->b is shorthand for (*a).b). Listing 20.19: Directly Accessing a Referent Type’s Members unsafe { Angle angle = new Angle(30, 18, 0); Angle* pAngle = &angle; System.Console.WriteLine("{0}° {1}' {2}\"", } The results of Listing 20.19 appear in Output 20.4. SUMMARY This chapter’s introduction outlined the low-level access to the underlying operating system that C# exposes. To summarize this, consider the Main() pAngle->Hours, pAngle->Minutes, pAngle->Seconds); OUTPUT 20.4: 30° 18' 0 From the Library of Wow! eBook ptg Chapter 20: Platform Interoperability and Unsafe Code840 function listing for determining whether execution is with a virtual com- puter (see Listing 20.20). Listing 20.20: Designating a Block for Unsafe Code using System.Runtime.InteropServices; class Program { unsafe static int Main(string[] args) { // Assign redpill byte[] redpill = { 0x0f, 0x01, 0x0d, // asm SIDT instruction 0x00, 0x00, 0x00, 0x00, // placeholder for an address 0xc3}; // asm return instruction fixed (byte* matrix = new byte[6], redpillPtr = redpill) { // Move the address of matrix immediately // following the SIDT instruction of memory. *(uint*)&redpillPtr[3] = (uint)&matrix[0]; using (VirtualMemoryPtr codeBytesPtr = new VirtualMemoryPtr(redpill.Length)) { Marshal.Copy( redpill, 0, codeBytesPtr, redpill.Length); MethodInvoker method = (MethodInvoker)Marshal.GetDelegateForFunctionPointer( codeBytesPtr, typeof(MethodInvoker)); method(); } if (matrix[5] > 0xd0) { Console.WriteLine("Inside Matrix!\n"); return 1; } else { Console.WriteLine("Not in Matrix.\n"); return 0; } unsafe { From the Library of Wow! eBook ptg Summary 841 } // fixed } } The results of Listing 20.20 appear in Output 20.5. In this case, you use a delegate to trigger execution of the assembler code. The delegate is declared as follows: delegate void MethodInvoker(); This book has demonstrated the power, flexibility, consistency, and fantastic structure of C#. This chapter demonstrated the ability, in spite of such high-level programming capabilities, to perform very low-level oper- ations as well. Before I end the book, the next chapter briefly describes the underlying execution platform and shifts the focus from the C# language to the broader platform in which C# programs execute. } // unsafe OUTPUT 20.5: Inside Matrix! From the Library of Wow! eBook ptg This page intentionally left blank From the Library of Wow! eBook ptg 843 21 The Common Language Infrastructure NE OF THE FIRST ITEMS that C# programmers encounter beyond the syntax is the context under which a C# program executes. This chap- ter discusses the underpinnings of how C# handles memory allocation and deallocation, type checking, interoperability with other languages, cross- platform execution, and support for programming metadata. In other words, this chapter investigates the Common Language Infrastructure (CLI) on which C# relies both at compile time and during execution. It cov- ers the execution engine that governs a C# program at runtime and how C# fits into a broader set of languages that are governed by the same execution O Common Language Infrastructure 1 What Is the CLI? Base Class Library Common Language Specification Common Type System Common Intermediate Language 2 CLI Implementations 3 C# Compilation Runtime 4 Garbage Collection Type Safety Code Access Security Platform Portability Performance 5 Components Metadata Application Domains Assemblies Manifests Modules From the Library of Wow! eBook ptg Chapter 21: The Common Language Infrastructure844 engine. Because of C#’s close ties with this infrastructure, most of the features that come with the infrastructure are made available to C#. Defining the Common Language Infrastructure (CLI) Instead of generating instructions that a processor can interpret directly, the C# compiler generates instructions in an intermediate language, the Common Intermediate Language (CIL). A second compilation step occurs, generally at execution time, converting the CIL to machine code that the processor can understand. Conversion to machine code is still not sufficient for code execution, however. It is also necessary for a C# pro- gram to execute under the context of an agent. The agent responsible for managing the execution of a C# program is the Virtual Execution System (VES), generally more casually referred to as the runtime. (Note that the runtime in this context does not refer to a time, such as execution time; rather, the runtime—the Virtual Execution System—is an agent responsi- ble for managing the execution of a C# program.) The runtime is responsi- ble for loading and running programs and providing additional services (security, garbage collection, and so on) to the program as it executes. The specification for the CIL and the runtime is contained within an international standard known as the Common Language Infrastructure (CLI). This is a key specification for understanding the context in which a C# program executes and how it can seamlessly interact with other programs and libraries, even when they are written in alternate languages. Note that the CLI does not prescribe the implementation for the standard, but rather identifies the requirements for how a CLI platform should behave once it conforms to the standard. This provides CLI implementers with the flexibility to innovate where necessary, while still providing enough structure that programs created by one platform can execute on a different CLI implementation, and even on a different operating system. NOTE Note the similarity between these two acronyms and the names they stand for. Take care to understand these upfront to avoid confusion later on. From the Library of Wow! eBook [...]... following: • The types that an assembly defines and imports • Version information about the assembly itself • Additional files the assembly depends on • Security permissions for the assembly The manifest is essentially a header to the assembly, providing all the information about what an assembly is composed of, along with the information that uniquely identifies it Assemblies can be class libraries or the... on From the Library of Wow! eBook Metadata 861 • Custom attributes embedded in the code, providing additional information about the constructs the attributes decorate The metadata is not a cursory, nonessential add-on to the CIL Instead, it forms a core part of the CLI implementation It provides the representation and the behavior information about a type and includes location information about which . (&). As Listing 20. 19 shows, it is possible to directly access a referent type’s members using the -> operator (that is, a->b is shorthand for (*a).b). Listing 20. 19: Directly Accessing. ptr [ebp-0Ch],0 00000014 cmp dword ptr ds:[001833E0h],0 0000001b je 00000022 0000001d call 75F9C9E0 00000022 mov ecx,dword ptr ds:[01C31418h] 00000028 call dword ptr ds: [03C8E854h] 0000002e. complexities of iterating over the f-reachable queue (see Garbage Collection and Finalization in Chapter 9) and compacting reachable data. Therefore, there is no way to explicitly free stackalloc data. From

Ngày đăng: 18/06/2014, 16:20

TỪ KHÓA LIÊN QUAN