UNSAFECSHARP Telegram 231
StructLayout

На самом деле довольно интересный аттрибут.
Давайте рассмотрим пример:


struct MyStruct {
public int a;
public byte b;
public int c;
public byte d;
}


Размер такой структуры определяется следующим образом:

public int a; // 4 байта
public byte b; // 1 байт
public int c; // 4 байта
public byte d; // 1 байт


Складываем, получаем 4 + 1 + 4 + 1 = 10 байт
Казалось бы, что тут сложного. Не все так просто 😉
Существует такое понятие как Pack size, то есть каким образом будет выровнена в памяти, если простым языком - каждая переменная будет минимум занимать размер pack size, максимум - кратное значение этому размеру:


public byte b; // 4 байта при Pack = 4
public byte b; // 1 байт при Pack = 1


Таким образом размер структуры будет вычисляться так:


public int a; // 4 байта
public byte b; // 4 байта
public int c; // 4 байта
public byte d; // 4 байта


Итого: 16 байт вместо 10 байт

Но мы умные и давайте переставим поля таким образом:


public int a; // 4 байта
public int c; // 4 байта
public byte b; // 1 байт
public byte d; // 1 байт


Получается, что теперь будет 10? А вот и снова нет 🙂
Теперь будет 12 байт. Почему так произошло?
Потому что последние два байта будут выровнены до 4х.

Что вообще такое Pack size и где он задается, о котором шла речь?
Это параметр аттрибута StructLayout:

[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct MyStruct {
public int a;
public byte b;
public int c;
public byte d;
}


Мы можем задать Pack = 1, чтобы запаковать структуру по одному байту, таким образом мы получим 10 байт.

Что не так с паковкой по одному байту и почему не паковать все структуры таким образом по-умолчанию?
Ну, во-первых, это нарушает выравнивание в памяти. Например, если вы захотите после такого сделать Interlocked.Add(ref s.c), то получите краш, т.к. аддрес в памяти у поля c будет не кратным 4, а это приведет к крашу.
Во-вторых, я не знаю аллокатора, который не применяет общее выравнивание аллоцируемых объектов, т.е. в памяти он вероятнее всего будет занимать 12 байт, а не 10.

Что еще есть у StructLayout?

Еще есть Size, которым мы можем ограничить размер структуры до минимально необходимого:

[StructLayout(LayoutKind.Sequential, Size = 10)]
struct MyStruct {
public int a;
public int c;
public byte b;
public byte d;
}


Заметьте, что я специально переставил поля, т.к. если этого не сделать, то будет интересный эффект:
Размер sizeof(MyStruct) вернет нам 13 (т.к. Pack = 4, последний байт будет обрезан), а вот Marshal.SizeOf(s) вернет нам 10, т.к. он берет тип объекта и возвращает сколько нам необходимо было байт, чтобы создать этот инстанс, ведь Marshal.SizeOf принимает именно фактический инстанс объекта.
В любом случае, такого нужно не допускать.

Что про LayoutKind?

Для структур это значение может принимать 2 варианта:
LayoutKind.Sequential - как поля объявлены, так и раскладываем в памяти.
LayoutKind.Explicit - ручное распределение, необходимо указать FieldOffset аттрибут для каждого поля.

С Explicit можно "наслаивать" поля друг на друга, как самый простой вариант:

[StructLayout(LayoutKind.Explicit)]
struct MyStruct {
[FieldOffset(0)]
public int a;
[FieldOffset(4)]
public int c;
[FieldOffset(0)]
public long b;
}


Т.е. положили значения a и c, забрали одно значение b, которое будет содержать 2 int.

#unsafe #structlayout #sizeof
🔥43👍23🤡2🍓21🥰1



tgoop.com/unsafecsharp/231
Create:
Last Update:

StructLayout

На самом деле довольно интересный аттрибут.
Давайте рассмотрим пример:


struct MyStruct {
public int a;
public byte b;
public int c;
public byte d;
}


Размер такой структуры определяется следующим образом:

public int a; // 4 байта
public byte b; // 1 байт
public int c; // 4 байта
public byte d; // 1 байт


Складываем, получаем 4 + 1 + 4 + 1 = 10 байт
Казалось бы, что тут сложного. Не все так просто 😉
Существует такое понятие как Pack size, то есть каким образом будет выровнена в памяти, если простым языком - каждая переменная будет минимум занимать размер pack size, максимум - кратное значение этому размеру:


public byte b; // 4 байта при Pack = 4
public byte b; // 1 байт при Pack = 1


Таким образом размер структуры будет вычисляться так:


public int a; // 4 байта
public byte b; // 4 байта
public int c; // 4 байта
public byte d; // 4 байта


Итого: 16 байт вместо 10 байт

Но мы умные и давайте переставим поля таким образом:


public int a; // 4 байта
public int c; // 4 байта
public byte b; // 1 байт
public byte d; // 1 байт


Получается, что теперь будет 10? А вот и снова нет 🙂
Теперь будет 12 байт. Почему так произошло?
Потому что последние два байта будут выровнены до 4х.

Что вообще такое Pack size и где он задается, о котором шла речь?
Это параметр аттрибута StructLayout:

[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct MyStruct {
public int a;
public byte b;
public int c;
public byte d;
}


Мы можем задать Pack = 1, чтобы запаковать структуру по одному байту, таким образом мы получим 10 байт.

Что не так с паковкой по одному байту и почему не паковать все структуры таким образом по-умолчанию?
Ну, во-первых, это нарушает выравнивание в памяти. Например, если вы захотите после такого сделать Interlocked.Add(ref s.c), то получите краш, т.к. аддрес в памяти у поля c будет не кратным 4, а это приведет к крашу.
Во-вторых, я не знаю аллокатора, который не применяет общее выравнивание аллоцируемых объектов, т.е. в памяти он вероятнее всего будет занимать 12 байт, а не 10.

Что еще есть у StructLayout?

Еще есть Size, которым мы можем ограничить размер структуры до минимально необходимого:

[StructLayout(LayoutKind.Sequential, Size = 10)]
struct MyStruct {
public int a;
public int c;
public byte b;
public byte d;
}


Заметьте, что я специально переставил поля, т.к. если этого не сделать, то будет интересный эффект:
Размер sizeof(MyStruct) вернет нам 13 (т.к. Pack = 4, последний байт будет обрезан), а вот Marshal.SizeOf(s) вернет нам 10, т.к. он берет тип объекта и возвращает сколько нам необходимо было байт, чтобы создать этот инстанс, ведь Marshal.SizeOf принимает именно фактический инстанс объекта.
В любом случае, такого нужно не допускать.

Что про LayoutKind?

Для структур это значение может принимать 2 варианта:
LayoutKind.Sequential - как поля объявлены, так и раскладываем в памяти.
LayoutKind.Explicit - ручное распределение, необходимо указать FieldOffset аттрибут для каждого поля.

С Explicit можно "наслаивать" поля друг на друга, как самый простой вариант:

[StructLayout(LayoutKind.Explicit)]
struct MyStruct {
[FieldOffset(0)]
public int a;
[FieldOffset(4)]
public int c;
[FieldOffset(0)]
public long b;
}


Т.е. положили значения a и c, забрали одно значение b, которое будет содержать 2 int.

#unsafe #structlayout #sizeof

BY Unity: Всё, что вы не знали о разработке


Share with your friend now:
tgoop.com/unsafecsharp/231

View MORE
Open in Telegram


Telegram News

Date: |

The public channel had more than 109,000 subscribers, Judge Hui said. Ng had the power to remove or amend the messages in the channel, but he “allowed them to exist.” While the character limit is 255, try to fit into 200 characters. This way, users will be able to take in your text fast and efficiently. Reveal the essence of your channel and provide contact information. For example, you can add a bot name, link to your pricing plans, etc. A new window will come up. Enter your channel name and bio. (See the character limits above.) Click “Create.” Just at this time, Bitcoin and the broader crypto market have dropped to new 2022 lows. The Bitcoin price has tanked 10 percent dropping to $20,000. On the other hand, the altcoin space is witnessing even more brutal correction. Bitcoin has dropped nearly 60 percent year-to-date and more than 70 percent since its all-time high in November 2021. Done! Now you’re the proud owner of a Telegram channel. The next step is to set up and customize your channel.
from us


Telegram Unity: Всё, что вы не знали о разработке
FROM American