Доступно ли UB для "заполненных" байтов?
Если у меня есть такой объект, как этот:
struct {
uint32_t n;
uint8_t c;
} blob {};
тогда будет 3 "заполненных" байта.
Доступен ли UB к заполненным байтам? Например:
uint8_t * data = reinterpret_cast<uint8_t*>(&blob);
std::cout << data[4] << data[5] << data[6] << data[7];
Сначала я предположил, что это, вероятно, будет UB, но если это правда, то memcpy также будет UB:
memcpy(buf, &blob, sizeof(blob));
Мои конкретные вопросы:
- Доступно ли UB для доступа к заполненным байтам?
- Если нет, значит ли это, что значения также определены?
Ответы
Ответ 1
Нет, это не UB для доступа к заполнению, когда весь объект был инициализирован нулем (стандарт говорит в § 8.5/5, что заполнение инициализируется до 0 бит, когда объекты инициализируются нулем) или инициализируется инициализацией, и это не является классом с определяемым пользователем конструктором.
Ответ 2
Я думаю, что при правильных обстоятельствах вы могли бы получить UB для этого. Я думаю о том, где у вас есть память либо с проверкой ECC, либо с контролем четности, где бит ecc/четности задается путем записи в память. Если блок памяти не использовался до того, как [никогда не был записан в AT ALL], и вы читаете неинициализированные байты в поле заполнения, это может привести к ошибке ecc/parity, когда память, которая еще не была записана читается.
Конечно, в такой системе вы избегаете целой кучи боли, просто делая "заполнять всю память" в какой-то момент во время загрузки, так как это было бы нечестно:
struct Blob
{
uint32_t n;
uint8_t c;
};
Blob *b = malloc(sizeof(Blob)*10);
for(int i = 0; i < 10; i++)
{
b[i].n = i;
b[i].c = i;
}
...
Blob a[3];
memcpy(a, &b[1], sizeof(a)); // Copies 3 * Blob objects, including padding.
Теперь, поскольку не все биты b [x] установлены, может быть не удалось скопировать данные в memcpy из-за ошибок четности /ecc. Это было бы неплохо. Но в то же время компилятор не может принудительно "устанавливать" все области заполнения.
Я пришел к выводу, что это UB, но вряд ли это вызовет проблемы, если не возникнут особые обстоятельства. Конечно, вы увидите код типа memcpy
, указанный выше, в большом количестве кода.
Ответ 3
В C это не поведение undefined. Единственный раз, когда вы получаете поведение undefined от доступа к неинициализированным материалам (например, отступы в объектах), - это когда объект имеет автоматическую продолжительность хранения и НИКОГДА НЕ ИМЕЕТ ЕГО АДРЕС:
6.3.2.1.2: Если lvalue обозначает объект с автоматическим временем хранения, который мог бы быть объявлен с классом хранения регистров (никогда не был принят его адрес), и этот объект не инициализируется (не объявляется с инициализатором, и никакое присвоение ему не было выполняется до использования), поведение undefined.
Но в этом случае вы берете адрес (с помощью &
), поэтому поведение корректно определено (ошибка не возникает), но вы можете получить произвольное значение.
В С++ все ставки отключены, как это обычно бывает.
Ответ 4
Если это не поведение undefined, оно, безусловно, определено в реализации. Хотя стандарт С++ не гарантирует многого о том, что делает ваша программа, ваша спецификация системы ABI - SysV, если вы используете Linux - будет, Я подозреваю, что, если вы бряцаете в битах, вы, вероятно, больше заинтересованы в том, как ваша программа будет вести себя в вашей системе, чем в том, как она будет вести себя на любой произвольной С++-соответствующей системе.
Ответ 5
Структура POD будет жить в непрерывном блоке памяти по меньшей мере sizeof (struct) байтов (включая любые байты заполнения). Доступ к байтам заполнения (если они существуют) будет UB, только если он не был сначала инициализирован.
memset(&s, 0, sizeof(s));
Это инициализировало бы все байты, включая дополнение. После чего чтение из прокладки не будет UB.
Конечно, memset()
является C-ism, и мы никогда не будем делать это на С++, правильно?