Это ошибка повторного использования хранилища (aliasing) в gcc 6.2?

Из того, что я могу сказать, следующий код должен иметь 100% определенное поведение при любом разумном прочтении Стандарта для платформ, которые определяют int64_t, и где long long имеет одинаковый размер и представление независимо от того, long long распознается как совместимый с псевдонимом.

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

typedef long long T1;
typedef int64_t T2;
#define T1FMT "%lld"
#define T1VALUE 1
#define T2VALUE 2

T1 blah3(void *p1, void *p2)
{
  T1 *t1p, *t1p2;
  T2 *t2p;
  T1 temp;

  t1p = p1;
  t2p = p2;
  *t1p = T1VALUE;   // Write as T1
  *t2p = T2VALUE;   // Write as T2
  temp = *t2p;      // Read as T2
  t1p2 = (T1*)t2p;  // Visible T2 to T1 pointer conversion
  *t1p2 = temp;     // Write as T1
  return *t1p;      // Read as T1
}

T1 test3(void)
{
  void *p = malloc(sizeof (T1) + sizeof (T2));
  T1 result = blah3(p,p);
  free(p);
  return result;
}
int main(void)
{
  T1 result = test3();
  printf("The result is " T1FMT "\n", result);
  return 0;      
}

См. Код на https://godbolt.org/g/75oLGx (GCC 6.2 x86-64 с использованием -std=c99 -x c -O2)

Правильный код для test3 следует выделить некоторое хранилище, тогда:

  1. Пишет long long со значением 1.
  2. Устанавливает эффективный тип хранилища int64_t написав int64_t со значением 2.
  3. Читает хранилище как int64_t (его эффективный тип), который должен дать 2
  4. Устанавливает эффективный тип хранилища long long храня long long с вышеупомянутым значением (которое должно быть 2).
  5. Читать хранилище как тип long long, который должен дать 2.

Однако gcc x86-64 6.2 на сайте Godbolt не дает 2; вместо этого он возвращает 1. Я не нашел никакой другой комбинации типов, для которой gcc ведет себя так. Я думаю, что происходит то, что gcc решает, что хранилище до * t1p2 может быть опущено, потому что оно не имеет никакого эффекта, но не может распознать, что хранилище имело эффект изменения действующего типа хранилища с int64_t в long long,

Пока считаю сомнительным решение не признавать int64_t а также long long как совместимые с псевдонимами, я не вижу в Стандарте ничего, что оправдывало бы неспособность gcc распознать повторное использование хранилища для хранения значения 2 после того, как оно ранее имело значение 1. Ничто никогда не читается как любой тип, кроме типа с который был написан, но я думаю, что gcc решает, что два указателя, переданные "бла", не могут быть псевдонимами.

Я что-то упускаю или это полная ошибка?

1 ответ

Код не нарушает строгое правило псевдонимов, как вы объясняете. По факту, T1 а также T2 это могут быть любые типы (например, присваивания не являются несовпадениями типов), для них нет необходимости иметь одинаковый размер или что-либо еще.

Я бы согласился, что вывод 1 это ошибка компилятора На сайте Godbolt все версии gcc, похоже, содержат ошибку, тогда как clang выдает правильный вывод.

Однако при локальной установке gcc 4.9.2 (x86_64-win32-seh-rev1) я получаю правильный вывод 2. Так что, похоже, проблема существует только в некоторых сборках gcc.

Другие вопросы по тегам