التعليمات البرمجية غير الآمنة والمؤشرات والمخازن المؤقتة الثابتة
استخدم الكلمة المفتاحية unsafe، وتعامَل مع المؤشرات، وثبّت الذاكرة المُدارة باستخدام fixed، وادخل إلى المخازن المؤقتة ثابتة الحجم في البُنى
التعليمات البرمجية غير الآمنة والمؤشرات والمخازن المؤقتة الثابتة درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 4 دروس في المجموع.
ما المقصود بالشيفرة unsafe؟
تتيح الكلمة المفتاحية unsafe في C# التعامل المباشر مع الذاكرة، بما في ذلك المؤشرات الخام وحسابات المؤشرات والمخازن المؤقتة ثابتة الحجم. وهي تتجاوز ضمانات أمان GC. فعّلها بإضافة <AllowUnsafeBlocks>true</AllowUnsafeBlocks> إلى ملف المشروع.
// Must add to .csproj:
// <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
// Unsafe blocks can appear inside methods:
unsafe
{
int x = 42;
int* ptr = &x; // get address of x
Console.WriteLine(*ptr); // dereference — prints 42
*ptr = 100;
Console.WriteLine(x); // 100 — x was mutated via pointer
}أنواع المؤشرات في C#
تشبه صياغة المؤشرات صياغتها في C/C++: الرمز T* هو مؤشر إلى T. يمكنك تعريف مؤشرات إلى أنواع القيم غير المُدارة، مثل int وdouble والبنى التي لا تتضمن حقولًا مرجعية. ولا يُسمح بالمؤشرات إلى الأنواع المُدارة.
unsafe
{
int i = 10;
double d = 3.14;
int* ip = &i;
double* dp = &d;
// Dereference with *
Console.WriteLine(*ip); // 10
// Pointer arithmetic — move to next int in memory
int[] arr = { 1, 2, 3 };
fixed (int* p = arr)
{
Console.WriteLine(*(p + 0)); // 1
Console.WriteLine(*(p + 1)); // 2
Console.WriteLine(*(p + 2)); // 3
}
}تعليمة fixed
يمكن لـ GC نقل الكائنات المُدارة من مكان إلى آخر. وللحصول على عنوان كائن مُدار، يجب تثبيته باستخدام fixed. لن ينقل GC الكائن طوال مدة كتلة fixed.
byte[] buffer = new byte[256];
unsafe
{
fixed (byte* pBuf = buffer)
{
// pBuf is valid only inside this block
// GC won't relocate 'buffer' here
for (int i = 0; i < buffer.Length; i++)
pBuf[i] = (byte)i;
}
// After 'fixed', GC can move 'buffer' again
}
Console.WriteLine(buffer[5]); // 5حسابات المؤشرات
يمكنك جمع أعداد صحيحة إلى المؤشرات أو طرحها منها. تؤدي إضافة 1 إلى مؤشر int* إلى تقدمه بمقدار 4 بايتات، أي sizeof int. ويشكّل ذلك أساس عمليات الذاكرة المجمّعة السريعة.
unsafe
{
int[] data = { 10, 20, 30, 40, 50 };
fixed (int* start = data)
{
int* p = start;
long sum = 0;
for (int i = 0; i < data.Length; i++)
{
sum += *p;
p++; // advance by sizeof(int) = 4 bytes
}
Console.WriteLine(sum); // 150
// Or with index syntax:
Console.WriteLine(start[2]); // 30
}
}stackalloc: التخصيص على المكدس
تخصص stackalloc كتلة من الذاكرة على المكدس، وليس على الكومة. ولا يسبب ذلك ضغطًا على GC، كما لا يتطلب تثبيتًا. ويقتصر نطاقها على إطار مكدس الدالة الحالية، وتُحرَّر الذاكرة تلقائيًا عند عودة الدالة.
// Stack-allocated buffer — zero heap allocation
Span<int> buffer = stackalloc int[128]; // safe Span wrapper
buffer.Fill(0);
buffer[0] = 42;
Console.WriteLine(buffer[0]); // 42
// Or raw pointer form (requires unsafe):
unsafe
{
int* raw = stackalloc int[128];
raw[0] = 99;
Console.WriteLine(raw[0]); // 99
}
// Stack frame popped — memory goneالمخازن المؤقتة ثابتة الحجم في البنى
يضمّن المخزن المؤقت ثابت الحجم مصفوفة ثابتة الطول داخل البنية مباشرةً، دون تخصيص على الكومة أو مؤشر إضافي. استخدم المُعدِّل fixed داخل بنية مُعرَّفة في سياق unsafe.
unsafe struct NetworkPacket
{
public int Length;
public byte Command;
public fixed byte Payload[256]; // 256 bytes inline in the struct
}
unsafe
{
NetworkPacket pkt;
pkt.Length = 10;
pkt.Command = 0x01;
// Fill payload inline — no heap allocation
for (int i = 0; i < 10; i++)
pkt.Payload[i] = (byte)i;
Console.WriteLine(pkt.Payload[5]); // 5
}
// sizeof(NetworkPacket) = 4 + 1 + 256 = ~261 bytes on stackvoid* والتحويل النوعي
إن void* مؤشر بلا نوع، وهو مفيد لعمليات الذاكرة العامة. يجب تحويله إلى مؤشر ذي نوع قبل إلغاء الإشارة إليه. ويعمل العامل sizeof(T) مع الأنواع غير المُدارة في سياق unsafe.
unsafe
{
int value = 42;
void* vp = &value; // typeless pointer
int* ip = (int*)vp; // cast back to int*
Console.WriteLine(*ip); // 42
// sizeof works on unmanaged types
Console.WriteLine(sizeof(int)); // 4
Console.WriteLine(sizeof(double)); // 8
Console.WriteLine(sizeof(long)); // 8
}فئتا MemoryMarshal وUnsafe
توفر System.Runtime.CompilerServices.Unsafe وSystem.Runtime.InteropServices.MemoryMarshal بدائل آمنة، لا تعتمد على الكلمة المفتاحية، للعديد من العمليات منخفضة المستوى. وهي متوافقة مع Span ويمكن استخدامها دون الكلمة المفتاحية unsafe.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Reinterpret a Span<byte> as Span<int> — no copy, no unsafe keyword
byte[] raw = new byte[16];
Span<int> ints = MemoryMarshal.Cast<byte, int>(raw);
ints[0] = 12345678;
Console.WriteLine(ints[0]); // 12345678
// Unsafe.As<,> — reinterpret reference type (advanced)
ref int first = ref MemoryMarshal.GetReference(ints);
Unsafe.Add(ref first, 1) = 99999;
Console.WriteLine(ints[1]); // 99999متى تستخدم unsafe ومتى لا تستخدمه
استخدم الشيفرة unsafe فقط عند وجود حاجة واضحة إلى الأداء وعدم توفر بديل آمن. واجعل نطاق الشيفرة unsafe محدودًا، واعزله في دوال مساعدة خاصة أو أنواع مخصصة. اكتب اختبارات شاملة ووثّق الافتراضات.
// GOOD: narrow unsafe scope
public static unsafe int SumBytes(ReadOnlySpan<byte> data)
{
int total = 0;
fixed (byte* p = data)
{
byte* end = p + data.Length;
for (byte* cur = p; cur < end; cur++)
total += *cur;
}
return total;
}
// BETTER for modern code: use SIMD via Vector<T> or hardware intrinsics
// which stay in managed code but still achieve native-level perfمثال عملي: تحليل بروتوكول ثنائي
تتألق المؤشرات unsafe والمخازن المؤقتة ثابتة الحجم عند تحليل البروتوكولات الثنائية التي يجب أن يتطابق فيها تخطيط البنية بدقة مع تنسيق البيانات على السلك. ويمكن تحويل البنية مباشرةً من مخزن بايتات دون أي نسخ.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct MessageHeader
{
public uint Magic; // 4 bytes
public ushort Version; // 2 bytes
public ushort PayloadLen; // 2 bytes
public fixed byte Id[16]; // 16 bytes GUID inline
}
unsafe ReadOnlySpan<byte> SerializeHeader(MessageHeader hdr)
{
// Cast struct directly to bytes — zero copy
return new ReadOnlySpan<byte>(&hdr, sizeof(MessageHeader));
}
unsafe MessageHeader ParseHeader(ReadOnlySpan<byte> buf)
{
fixed (byte* p = buf)
return *(MessageHeader*)p; // reinterpret cast
}اختبار سريع
لماذا نحتاج إلى تعليمة fixed عند الحصول على عنوان كائن مُدار؟
مراجعة: الشيفرة unsafe والمؤشرات والمخازن المؤقتة الثابتة
أهم النقاط:
- تتيح الكلمة المفتاحية
unsafeاستخدام المؤشرات الخام، وتتطلبAllowUnsafeBlocksفي المشروع - تثبّت تعليمة
fixedالكائنات المُدارة كي لا ينقلها GC - تتقدم حسابات المؤشرات بمقدار حجم العنصر، وليس بعدد البايتات
- تخصص
stackallocالذاكرة على المكدس، فلا تسبب ضغطًا على GC وتُحرَّر عند خروج الدالة - تضمّن المخازن المؤقتة ثابتة الحجم، مثل
fixed byte Payload[N]، المصفوفات داخل البنى مباشرةً - فضّل
Span<T>وMemoryMarshalوفئةUnsafeفي معظم السيناريوهات
الأسئلة الشائعة
هل درس «التعليمات البرمجية غير الآمنة والمؤشرات والمخازن المؤقتة الثابتة» مجاني؟
نعم — نص درس «التعليمات البرمجية غير الآمنة والمؤشرات والمخازن المؤقتة الثابتة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 4 دروس في المجموع.
ماذا ستتعلم في «التعليمات البرمجية غير الآمنة والمؤشرات والمخازن المؤقتة الثابتة»؟
استخدم الكلمة المفتاحية unsafe، وتعامَل مع المؤشرات، وثبّت الذاكرة المُدارة باستخدام fixed، وادخل إلى المخازن المؤقتة ثابتة الحجم في البُنى تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟
لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «التعليمات البرمجية غير الآمنة والمؤشرات والمخازن المؤقتة الثابتة»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟
نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- أساسيات P/Invoke
- LibraryImport وP/Invoke المولّد من المصدر
- التعليمات البرمجية غير الآمنة والمؤشرات والمخازن المؤقتة الثابتة
- التعامل البيني مع COM والتغليفات القابلة للاستدعاء من وقت التشغيل