Это ошибка повторного использования хранилища (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
следует выделить некоторое хранилище, тогда:
- Пишет
long long
со значением 1. - Устанавливает эффективный тип хранилища
int64_t
написавint64_t
со значением 2. - Читает хранилище как
int64_t
(его эффективный тип), который должен дать 2 - Устанавливает эффективный тип хранилища
long long
храняlong long
с вышеупомянутым значением (которое должно быть 2). - Читать хранилище как тип
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.