mirror of
https://github.com/Nekrolm/ubbook.git
synced 2026-06-09 13:14:18 +03:00
prettify zero_size.md
This commit is contained in:
+15
-13
@@ -10,6 +10,7 @@ struct MyTag {};
|
||||
|
||||
```C++
|
||||
struct Tag {};
|
||||
|
||||
Tag func(Tag t1) {
|
||||
Tag t2;
|
||||
return Tag{};
|
||||
@@ -64,20 +65,22 @@ struct Vector3 : StdAllocator {
|
||||
Ну ладно, мы выяснили, что, неаккуратно использовав пустые структуры, мы можем получить увеличение потребления памяти. Давайте играть дальше.
|
||||
|
||||
```C++
|
||||
struct StdAllocator {};
|
||||
struct StdComparator {};
|
||||
|
||||
struct Map1 {
|
||||
StdAllocator alloc;
|
||||
StdComparator comp;
|
||||
};
|
||||
|
||||
|
||||
struct Map2 {
|
||||
StdAllocator alloc;
|
||||
[[no_unique_address]] StdComparator comp;
|
||||
[[no_unique_address]] StdComparator comp;
|
||||
};
|
||||
|
||||
struct Map3 {
|
||||
[[no_unique_address]] StdAllocator alloc;
|
||||
[[no_unique_address]] StdComparator comp;
|
||||
[[no_unique_address]] StdAllocator alloc;
|
||||
[[no_unique_address]] StdComparator comp;
|
||||
};
|
||||
|
||||
struct MapImpl1 : Map1 {
|
||||
@@ -97,13 +100,13 @@ struct MapImpl3 : Map3 {
|
||||
|
||||
Ну, тут все просто!:
|
||||
- Очевидно, что `sizeof(Map1) == 2`, ведь она состоит из двух пустых структур, каждая из которых имеет размер 1.
|
||||
- Благодаря аттрибуту `[[no_unique_address]]` из C++20, `Map2` и `Map3` должны иметь размеры 1. В `Map3` оба поля разделяют общий адрес. В `Map2` то же самое. Да и меньще чем 1 не бывает.
|
||||
- Благодаря атрибуту `[[no_unique_address]]` из стандарта C++20 (Clang поддерживает с C++11), `Map2` и `Map3` должны иметь размер 1. В `Map3` оба поля разделяют общий адрес. В `Map2` то же самое. Да и меньще чем 1 не бывает.
|
||||
|
||||
Хорошо. А что же теперь с наследующими структурами?
|
||||
|
||||
Все по `2*sizeof(int)`? А вот и нет: у `MapImpl3` [работает EBO](https://godbolt.org/z/exo5zP64E)!
|
||||
|
||||
Ну ладно. В этом есть какая-то логика и закономерность. Это еще можно принять. Хотя... На самом деле вы были правы! Ведь если у вас компилятор msvc, то `[[no_unique_address]]` просто [не работает](https://godbolt.org/z/6364qzYe6). И не будет работать. Потому что msvc долгое время просто игнорировал не знакомые ему аттрибуты. И если поддержать `[[no_unique_address]]`, то сломается бинарная совместимость. Используйте `[[msvc::no_unique_address]]`! EBO, правда, пока [не работает](https://godbolt.org/z/GTjrsbPPK).
|
||||
Ну ладно. В этом есть какая-то логика и закономерность. Это еще можно принять. Хотя... На самом деле вы были правы! Ведь если у вас компилятор msvc, то `[[no_unique_address]]` просто [не работает](https://godbolt.org/z/6364qzYe6). И не будет работать. Потому что msvc долгое время просто игнорировал незнакомые ему атрибуты. И если поддержать `[[no_unique_address]]`, то сломается бинарная совместимость. Используйте `[[msvc::no_unique_address]]`! EBO, правда, пока [не работает](https://godbolt.org/z/GTjrsbPPK).
|
||||
|
||||
|
||||
## zero-size array
|
||||
@@ -115,15 +118,14 @@ struct ImageHeader{
|
||||
int h;
|
||||
int w;
|
||||
};
|
||||
|
||||
struct Image {
|
||||
struct ImageHeader header;
|
||||
char data[];
|
||||
};
|
||||
```
|
||||
|
||||
Поле `data` в структуре `Image` имеет [нулевой размер](https://godbolt.org/z/d3xfdj3Ke). Это FAM (flexible array member). Очень удобная штука, чтобы получать доступ к массиву статически не известной длины, размешенному сразу послу некоторого заголовка в бинарном буфере. Длина массива обычно указавывается в самом заголовке.
|
||||
|
||||
FAM может быть только последним полем в структуре.
|
||||
Поле `data` в структуре `Image` имеет [нулевой размер](https://godbolt.org/z/d3xfdj3Ke). Это FAM (flexible array member). Очень удобная штука, чтобы получать доступ к массиву статически не известной длины, размещенному сразу послу некоторого заголовка в бинарном буфере. Длина массива обычно указавывается в самом заголовке. FAM может быть только последним полем в структуре.
|
||||
|
||||
Стандарт C++ такие фичи не разрешает. Но ведь есть GCC с его нестандартными включенными по умолчанию расширениями.
|
||||
|
||||
@@ -135,7 +137,7 @@ struct S {
|
||||
```
|
||||
Чему будет равень размер структуры `S`?
|
||||
|
||||
В стандратном C пустые структуры в принципе запрещены. И поведение программы с ними не определено. GCC определяет их размер нулевым при компиляции C программ. А при компиляции C++ — размер, как мы выяснили ранее, единичный. Дело пахнет страшными багами и ночными кошмарами при неосторожном проектировании C++ библиотек с сишным интерфейсом или использованием C-библиотек в C++!
|
||||
В стандартном C пустые структуры в принципе запрещены. И поведение программы с ними не определено. GCC определяет их размер нулевым при компиляции C программ. А при компиляции C++ — размер, как мы выяснили ранее, единичный. Дело пахнет страшными багами и ночными кошмарами при неосторожном проектировании C++ библиотек с сишным интерфейсом или использованием C-библиотек в C++!
|
||||
|
||||
Но вернемся все-таки к нашей структуре с FAM. Поле в ней есть. Стандартный C опять-таки требует, чтобы было еще хотя бы одно поле ненулевой длины перед FAM. GNU C же охотно сделает нам структуру нулевого размера.
|
||||
|
||||
@@ -152,7 +154,7 @@ static_assert(sizeof(S1) != sizeof(S2));
|
||||
static_assert(sizeof(S1) == 0);
|
||||
```
|
||||
|
||||
И вот уже внезапно у нас в C++ стуктуры нулевого размера. Только C++ не стандартный.
|
||||
И вот уже внезапно у нас в C++ структуры нулевого размера. Только C++ не стандартный.
|
||||
Каким образом такие структуры будет взаимодействовать с EBO — нужно читать в спецификации к GCC.
|
||||
|
||||
## tag dispatching
|
||||
@@ -193,6 +195,6 @@ int add(int x, int y) {
|
||||
|
||||
|
||||
## Полезные ссылки
|
||||
1. https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/
|
||||
1. https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch
|
||||
2. https://en.cppreference.com/w/cpp/language/attributes/no_unique_address
|
||||
3. https://en.cppreference.com/w/cpp/language/ebo
|
||||
3. https://en.cppreference.com/w/cpp/language/ebo
|
||||
|
||||
Reference in New Issue
Block a user