Код: Выделить всё
struct Mystruct
{
char *p; // sizeof(p) == 8, alignof(p) == 8
uint32_t sz; // sizeof(sz) == 4, alignof(sz) == 4
}; // sizeof(MyStruct) == 16 (with padding),
// alignof(MyStruct) == 8
union U
{
MyStruct s;
char buf[16];
};
Основная цель – максимально эффективно использовать пространство и при этом работать надежно.
Что я делаю: это объединение является единственным элементом данных для строкового класса (мы назовем его MyString). Внутри этого строкового класса структура MyStruct (на самом деле называемая AllocInfo) при использовании содержит указатель и длину динамически выделяемого блока памяти для строки (char* и uint32_t соответственно), когда хранится строка длиной более 15 символов. buf[] используется для оптимизации коротких строк (SSO): строка сохраняется непосредственно в этом буфере, если она помещается (15 или менее символов). Последний элемент этого массива символов используется как флаг. Когда используется SSO, он устанавливается в 0, а также удваивается как завершающий нулевой символ строки, когда длина хранимой строки равна точно 15. Когда используется динамически выделяемая память, этот флаг устанавливается в 1. Когда мне нужно прочитать string Я всегда сначала читаю этот флаг, чтобы определить, как получить к нему доступ.
Ограничения: я ориентируюсь на C++11. В настоящее время я компилирую для AMD64 Linux, но стараюсь быть как можно более независимым от платформы, планируя в ближайшее время компиляцию для ARM64 Linux (фактический размер массива символов buf[] рассчитывается во время компиляции как гарантированно всегда будет как минимум на один байт больше структуры, а также будет соответствовать alignof(max_align_t) для платформы). Я связываюсь с никакими библиотеками. Это означает, что я не могу использовать ничего из библиотек Standard C или Standard C++, а также ничего из пространства имен std::.
- Насколько я понимаю, я иногда прочитайте флаг в buf[15], когда последний записанный член объединения был структурой MyStruct, которая по определению является неопределенным поведением (для C++11). Я понимаю. Однако я также считаю, что поведение в этом конкретном случае должно быть детерминированным и делать то, что я хочу, из-за требований к союзам (члены союза делят пространство и начальный адрес). Прав ли я в этом предположении?
- В подобных случаях, когда элементы массива в объединении, к которым осуществляется индивидуальный доступ, сами не делят пространство ни с одним другим членом объединения (поскольку начальный адрес элемент находится за последним адресом, использованным любым другим членом союза) действительно ли это должно быть UB в C++?
- Насколько я могу убедиться, код соответствует работает отлично и без проблем как в GCC, так и в Clang. При компиляции с каждым флагом предупреждения и уровнем строгости, который я могу вспомнить, на самом высоком уровне оптимизации диагностика не выводится, и код работает как положено. Однако компиляторы не обязаны выдавать диагностику для UB, поэтому я не могу ничего сделать из этого, верно?
- Я мог бы, конечно, вытащить флаг из объединения и поставить это в другом месте класса строк. Но это будет означать, что я больше не смогу использовать 100% размера экземпляра строки для единого входа, что в некоторых случаях сделает его немного медленнее и немного менее эффективно использует пространство, что меня огорчает. Это также может означать увеличение sizeof(MyString) (в настоящее время 16) или снижение эффективности использования пространства в памяти самим строковым классом, когда многие экземпляры распределяются динамически.
Поскольку кажется, что все работает как есть, мне следует просто остановиться слишком много думаешь?
Подробнее здесь: https://stackoverflow.com/questions/784 ... hare-space