ก่อนที่จะอธิบายประเภทข้อมูลต่างๆที่มีอยู่ใน C # สิ่งสำคัญคือต้องระบุว่า C # เป็นภาษาที่พิมพ์ผิด ซึ่งหมายความว่าแต่ละตัวแปรค่าคงที่พารามิเตอร์อินพุตประเภทการส่งคืนและโดยทั่วไปทุกนิพจน์ที่ประเมินเป็นค่าจะมีชนิด
แต่ละประเภทมีข้อมูลที่คอมไพลเลอร์จะฝังลงในไฟล์ปฏิบัติการเป็นข้อมูลเมตาซึ่งจะใช้โดยรันไทม์ภาษาทั่วไป (CLR) เพื่อรับประกันความปลอดภัยของประเภทเมื่อจัดสรรและเรียกคืนหน่วยความจำ
หากคุณต้องการทราบว่าหน่วยความจำประเภทใดประเภทหนึ่งจัดสรรได้เท่าใดคุณสามารถใช้ตัวดำเนินการ sizeof ดังต่อไปนี้:
static void Main()
{
var size = sizeof(int);
Console.WriteLine($"int size:{size}");
size = sizeof(bool);
Console.WriteLine($"bool size:{size}");
size = sizeof(double);
Console.WriteLine($"double size:{size}");
size = sizeof(char);
Console.WriteLine($"char size:{size}");
}
ผลลัพธ์จะแสดงจำนวนไบต์ที่จัดสรรโดยแต่ละตัวแปร
int size:4
bool size:1
double size:8
char size:2
ข้อมูลที่เกี่ยวข้องกับแต่ละประเภท ได้แก่ :
- พื้นที่จัดเก็บที่ต้องการ
- ค่าสูงสุดและต่ำสุด ตัวอย่างเช่นประเภท Int32 ยอมรับค่าระหว่าง 2147483648 ถึง 2147483647
- ประเภทฐานที่สืบทอดมา
- ตำแหน่งที่จะจัดสรรหน่วยความจำสำหรับตัวแปรในขณะทำงาน
- ประเภทของการดำเนินการที่ได้รับอนุญาต
สมาชิก (วิธีการฟิลด์เหตุการณ์ ฯลฯ ) ที่มีอยู่ตามประเภท ตัวอย่างเช่นหากเราตรวจสอบคำจำกัดความของประเภท int เราจะพบโครงสร้างและสมาชิกต่อไปนี้:
namespace System
{
[ComVisible(true)]
public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<Int32>, IEquatable<Int32>
{
public const Int32 MaxValue = 2147483647;
public const Int32 MinValue = -2147483648;
public static Int32 Parse(string s, NumberStyles style, IFormatProvider provider);
...
}
}
การจัดการหน่วยความจำ
เมื่อหลายกระบวนการทำงานบนระบบปฏิบัติการและจำนวน RAM ไม่เพียงพอที่จะเก็บไว้ทั้งหมดระบบปฏิบัติการจะแมปส่วนต่างๆของฮาร์ดดิสก์กับ RAM และเริ่มจัดเก็บข้อมูลในฮาร์ดดิสก์ ระบบปฏิบัติการจะใช้มากกว่าตารางเฉพาะที่ที่อยู่เสมือนถูกจับคู่กับที่อยู่ทางกายภาพของผู้ติดต่อเพื่อดำเนินการตามคำขอ ความสามารถในการจัดการหน่วยความจำนี้เรียกว่าหน่วยความจำเสมือน
ในแต่ละกระบวนการหน่วยความจำเสมือนที่พร้อมใช้งานจะถูกจัดระเบียบใน 6 ส่วนต่อไปนี้ แต่สำหรับความเกี่ยวข้องของหัวข้อนี้เราจะเน้นเฉพาะสแตกและฮีป
Stack
สแตกเป็นโครงสร้างข้อมูล LIFO (เข้าก่อนออกก่อน) โดยมีขนาดขึ้นอยู่กับระบบปฏิบัติการ (โดยค่าเริ่มต้นสำหรับเครื่อง ARM, x86 และ x64 สำรองของ Windows 1MB ในขณะที่สำรอง Linux จาก 2MB ถึง 8MB ขึ้นอยู่กับ รุ่น).
ส่วนนี้ของหน่วยความจำได้รับการจัดการโดยอัตโนมัติโดย CPU ทุกครั้งที่ฟังก์ชันประกาศตัวแปรใหม่คอมไพลเลอร์จะจัดสรรบล็อกหน่วยความจำใหม่ให้ใหญ่เท่ากับขนาดบนสแต็กและเมื่อฟังก์ชันสิ้นสุดลงบล็อกหน่วยความจำสำหรับตัวแปรจะถูกยกเลิกการจัดสรร
ฮีป
พื้นที่หน่วยความจำนี้ไม่ได้ถูกจัดการโดยอัตโนมัติโดย CPU และขนาดของมันจะใหญ่กว่าสแต็ก เมื่อมีการเรียกคีย์เวิร์ดใหม่คอมไพลเลอร์จะเริ่มค้นหาบล็อกหน่วยความจำว่างแรกที่เหมาะกับขนาดของคำขอ และเมื่อพบมันจะถูกทำเครื่องหมายว่าสงวนไว้โดยใช้ฟังก์ชัน C ในตัว malloc () และส่งกลับตัวชี้ไปยังตำแหน่งนั้น นอกจากนี้ยังสามารถยกเลิกการจัดสรรบล็อกหน่วยความจำโดยใช้ฟังก์ชัน C ในตัวฟรี () กลไกนี้ทำให้หน่วยความจำแตกตัวและต้องใช้พอยน์เตอร์เพื่อเข้าถึงบล็อกหน่วยความจำด้านขวาซึ่งช้ากว่าสแต็กในการดำเนินการอ่าน / เขียน
ชนิดที่กำหนดเองและในตัวใน
ขณะที่ C # จัดเตรียมชุดมาตรฐานของประเภทในตัวที่แสดงถึงจำนวนเต็มบูลีนอักขระข้อความและอื่น ๆ คุณสามารถใช้โครงสร้างเช่นโครงสร้างคลาสอินเทอร์เฟซและ enum เพื่อสร้างประเภทของคุณเอง
ตัวอย่างของประเภทที่กำหนดเองโดยใช้โครงสร้างของโครงสร้างคือ:
struct Point
{
public int X;
public int Y;
};
ประเภทค่าและข้อมูลอ้างอิง
เราสามารถแบ่งประเภท C # ออกเป็นหมวดหมู่ต่อไปนี้:
- ประเภทมูลค่า
- ประเภทการอ้างอิง
ชนิดค่าประเภท
ค่าที่ได้มาจากคลาส System.ValueType และตัวแปรประเภทนี้มีค่าภายในการจัดสรรหน่วยความจำในสแตก ประเภทของค่าสองประเภทคือโครงสร้างและ enum
ตัวอย่างต่อไปนี้แสดงสมาชิกของชนิดบูลีน อย่างที่คุณเห็นไม่มีการอ้างถึงคลาส System.ValueType อย่างชัดเจนสิ่งนี้เกิดขึ้นเนื่องจากคลาสนี้สืบทอดโดยโครงสร้าง
namespace System
{
[ComVisible(true)]
public struct Boolean : IComparable, IConvertible, IComparable<Boolean>, IEquatable<Boolean>
{
public static readonly string TrueString;
public static readonly string FalseString;
public static Boolean Parse(string value);
...
}
}
ประเภทการอ้างอิง
ในทางกลับกันประเภทการอ้างอิงไม่มีข้อมูลจริงที่เก็บไว้ในตัวแปร แต่เป็นที่อยู่หน่วยความจำของฮีปที่เก็บค่า ประเภทของประเภทการอ้างอิง ได้แก่ คลาสตัวแทนอาร์เรย์และอินเทอร์เฟซ
ในขณะรันเมื่อมีการประกาศตัวแปรชนิดการอ้างอิงตัวแปรจะมีค่าว่างจนกว่าอ็อบเจ็กต์ที่สร้างขึ้นโดยใช้คีย์เวิร์ดใหม่จะถูกกำหนดให้
ตัวอย่างต่อไปนี้แสดงสมาชิกของประเภทรายการทั่วไป
namespace System.Collections.Generic
{
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(Generic.Mscorlib_CollectionDebugView<>))]
[DefaultMember("Item")]
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
{
...
public T this[int index] { get; set; }
public int Count { get; }
public int Capacity { get; set; }
public void Add(T item);
public void AddRange(IEnumerable<T> collection);
...
}
}
ในกรณีที่คุณต้องการค้นหาที่อยู่หน่วยความจำของอ็อบเจ็กต์เฉพาะคลาส System.Runtime.InteropServices มีวิธีการเข้าถึงอ็อบเจ็กต์ที่มีการจัดการจากหน่วยความจำที่ไม่มีการจัดการ ในตัวอย่างต่อไปนี้เราจะใช้เมธอดแบบคงที่ GCHandle Alloc () เพื่อจัดสรรแฮนเดิลให้กับสตริงจากนั้นเมธอด AddrOfPinnedObject เพื่อดึงแอดเดรส
string s1 = "Hello World";
GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
IntPtr pObj = gch.AddrOfPinnedObject();
Console.WriteLine($"Memory address:{pObj.ToString()}");
ผลลัพธ์จะเป็น
Memory address:39723832
อ้างอิงเอกสาร
อย่างเป็นทางการ: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019