Перш ніж пояснити різні типи даних, доступні в 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);
...
}
}
Управління пам'яттю
Коли в операційній системі запущено декілька процесів і об'єм оперативної пам’яті недостатньо, щоб утримати її все, операційна система відображає частини жорсткого диска з оперативною пам’яттю і починає зберігати дані на жорсткому диску. Операційна система використовуватиме, ніж конкретні таблиці, де віртуальні адреси відображаються на їх кореспондентські фізичні адреси для виконання запиту. Ця можливість управління пам'яттю називається віртуальною пам'яттю.
У кожному процесі наявна віртуальна пам'ять організована в наступних 6 розділах, але для актуальності цієї теми ми зупинимося лише на стеці та купі.
Стек
Стек являє собою структуру даних LIFO (останній, перший), залежно від розміру операційної системи (за замовчуванням - резерв 1MB для систем ARM, x86 та x64, в той час як Linux резервує від 2 МБ до 8 МБ, залежно від цього версія).
Цим розділом пам'яті автоматично керує процесор. Кожен раз, коли функція оголошує нову змінну, компілятор виділяє новий блок пам'яті, такий же великий, як і її розмір на стеку, а коли функція закінчена, блок пам'яті для змінної переміщується.
Купа
Ця область пам'яті не управляється автоматично процесором, і її розмір більший, ніж стек. Після виклику нового ключового слова компілятор починає шукати перший вільний блок пам'яті, який відповідає розміру запиту. і коли він знаходить його, він позначається як зарезервований за допомогою вбудованої функції C malloc () та повертає вказівник на це місце. Можливо також розмістити блок пам'яті за допомогою вбудованої функції С (free). Цей механізм викликає фрагментацію пам’яті і має використовувати вказівники для доступу до правого блоку пам’яті, він повільніше, ніж стек для виконання операцій читання / запису.
Спеціальні та вбудовані типи
Хоча C # пропонує стандартний набір вбудованих типів, що представляють цілі числа, булеві, текстові символи тощо, ви можете використовувати такі структури, як структура, клас, інтерфейс та enum для створення власних типів.
Приклад користувальницького типу за допомогою структури constru:
struct Point
{
public int X;
public int Y;
};
Значення та довідкові типи
Ми можемо класифікувати тип C # у такі категорії:
- Типи значення
- Довідкові типи
Типи значення Типи
значень походять від класу System.ValueType і змінні цього типу містять свої значення в межах розподілу пам'яті в стеку. Дві категорії типів цінності - структура та перерахунок.
Наступний приклад показує член булевого типу. Як видно, явного посилання на клас 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);
...
}
}
Типи довідок
З іншого боку, типи посилань містять не фактичні дані, що зберігаються у змінній, а адресу пам'яті в купі, де зберігається значення. Категорії типів посилань - це класи, делегати, масиви та інтерфейси.
Під час виконання, коли оголошена змінна тип посилання, вона містить значення null до тих пір, поки об’єкт, створений за допомогою нових ключових слів, не буде присвоєний йому.
Наступний приклад показує членів списку загального типу.
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