Из того, что я могу сказать, следующий код должен иметь 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 решает, что два указателя, переданные «бла», не могут быть псевдонимом.
Я что-то пропустил или это явный баг?