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: |

Each account can create up to 10 public channels In 2018, Telegram’s audience reached 200 million people, with 500,000 new users joining the messenger every day. It was launched for iOS on 14 August 2013 and Android on 20 October 2013. In handing down the sentence yesterday, deputy judge Peter Hui Shiu-keung of the district court said that even if Ng did not post the messages, he cannot shirk responsibility as the owner and administrator of such a big group for allowing these messages that incite illegal behaviors to exist. Hui said the messages, which included urging the disruption of airport operations, were attempts to incite followers to make use of poisonous, corrosive or flammable substances to vandalize police vehicles, and also called on others to make weapons to harm police. Select “New Channel”
from us


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