Постоянна ли через неопределенное поведение профсоюза?

StackOverflow https://stackoverflow.com/questions/8836418

Вопрос

В отличие от C ++, C не имеет понятия о const_cast. Анкет То есть нет достоверного способа преобразовать постоянный квалифицированный указатель в несущественный указатель:

void const * p;
void * q = p;    // not good

Во -первых: этот актерский состав действительно неопределенного поведения?

В любом случае, GCC предупреждает об этом. Чтобы сделать «чистый» код, который требует постоянного (то есть, где я могу гарантировать, что я не буду мутировать содержимое, но все, что у меня есть, это изменяемый указатель), я видел следующий трюк «преобразование»:

typedef union constcaster_
{
    void * mp;
    void const * cp;
} constcaster;

Применение: u.cp = p; q = u.mp;.

Каковы правила языка C относительно отмены постоянства через такой союз? Мои знания о C лишь очень неоднородные, но я слышал, что C гораздо более снисходительна в отношении доступа к профсоюзам, чем C ++, поэтому, хотя у меня плохое ощущение этой конструкции, я хотел бы спорить из стандарта (C99, я полагаю,, я полагаю, C99, Хотя, если это изменилось в C11, будет хорошо знать).

Это было полезно?

Решение

Его реализация определена, см. C99 6.5.2.3/5:

Если ценность члена объекта Union используется, когда самый последний магазин для объекта был для другого участника, поведение определяется внедрением.

Обновлять: @Aaronmcdaid прокомментировал, что это может быть четко определенным, в конце концов.

Стандарт указал следующие 6.2.5/27:

Точно так же указатели на квалифицированные или неквалифицированные версии совместимых типов должны иметь одинаковые требования к представлению и выравниванию.27)

27) Те же требования к представлению и выравнивания предназначены для подразумевания взаимозаменяемости в качестве аргументов для функций, возвращающих значений от функций и членов профсоюзов.

И (6.7.2.1/14):

Указатель на объект Союза, который соответственно преобразован, указывает на каждого из его членов (или если участник - немного поля, а затем на подразделение, в котором он находится), и наоборот.

Один мощь прийти к выводу, что, в этом конкретном случае, Есть только место для ровного способа получить доступ к элементам в союзе.

Другие советы

Насколько я понимаю, UB может возникнуть только в том случае, если вы попытаетесь изменить постоянный объект.

Таким образом, следующий код не UB:

int x = 0;
const int *cp = &x;
int *p = (int*)cp;
*p = 1; /* OK: x is not a const object */

Но это UB:

const int cx = 0;
const int *cp = &cx;
int *p = (int*)cp;
*p = 1; /* UB: cx is const */

Использование союза вместо актерского состава не должно иметь никакого значения здесь.

Из спецификаций C99 (6.7.3 типа квалификаторы):

Если предпринимается попытка изменить объект, определяемый с постоянным квалифицированным типом, путем использования LVALUE с неконфессированным типом, поведение не определен.

Инициализация, безусловно, не вызовет UB. Преобразование между квалифицированными типами указателей явно разрешено в § 6.3.2.3/2 (N1570 (C11)). Использование контента в этом указателе впоследствии вызывает UB (см. @rodrigoответ).

Тем не менее, вам нужен явный актерский состав, чтобы преобразовать void* в const void*, потому что ограничение простого назначения по -прежнему требует всего квалификатора на LHS, появляется на RHS.

§6.7.9/11: ... начальное значение объекта - это значение выражения (после преобразования); Применяются те же ограничения и преобразования типа, что и для простого назначения, принимая тип скаляра в неквалифицированную версию его объявленного типа.

§6.5.16.1/1: (Простое назначение / противоречия)

  • ... Оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов, а тип, на который указан левым, есть все квалификаторы типа, на которые указано правой;
  • ... Один операнд является указателем на тип объекта, а другой - указатель на квалифицированную или неквалифицированную версию void, и тип, указанный левым, имеет все квалификаторы типа, на которые указано правой;

Я не знаю, почему GCC просто предупреждает.


А для уловка Союза, да, это не UB, но все же результат, вероятно, не определен.

§6.5.2.3/3 FN 95: Если участник, используемый для чтения содержимого объекта Union, не совпадает с участником, который в последний раз используется для хранения значения в объекте, соответствующая часть представления объекта значения переосмысливается как представление объекта в новом типе как описано в 6.2.6 (процесс иногда называется «тип карагана»). Это может быть представление ловушки.

§6.2.6.1/7: Когда значение хранится в члене объекта типа объединения, байты представления объекта, которые не соответствуют этому члену, но соответствуют другим членам, принимают неуказанные значения. (* Примечание: см. Также §6.5.2.3/6 для исключения, но здесь не применяется)


Соответствующие разделы в N1124 (C99)

  • C11 §6.3.2.3/2 = C99 §6.3.2.3/2
  • C11 §6.7.9/11 = C99 §6.7.8/11
  • C11 §6.5.16.1/1 = C99 §6.5.16.1/1
  • C11 §6.5.2.3/3 fn 95 = отсутствует («Тип каламбура» не появляется в C99)
  • C11 §6.2.6.1/7 = C99 §6.2.6.1/7

Не бросайте это вообще. Это указатель на Const, что означает, что попытка изменить данные не допускается, и во многих реализациях программа будет сбой, если указатель указывает на немодиационную память. Даже если вы знаете, что мемори может быть изменена, могут быть и другие указатели, которые не ожидают, что она изменится, например, если она является частью хранения логически неизменной строки.

Предупреждение есть по уважительной причине.

Если вам нужно изменить содержание константного указателя, портативный безопасный способ сделать это сначала скопировать память, на которую он указывает, а затем изменить это.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top