Указатели C: назначение из несовместимого типа указателя - PullRequest
0 голосов
/ 19 марта 2019

Когда я компилирую этот код с помощью gcc 7.3.0, я получаю «назначение из несовместимого типа указателя».

int intVar = 1;
char* charPointer;

charPointer = &intVar;
printf("%d", *charPointer);

Пока все хорошо. Я могу справиться с этим, выполнив указатель следующим образом:

charPointer = (char*)&intVar;

Теперь я сомневаюсь: чем отличается второй случай? Я все еще могу испортить ситуацию, если я не приведу charPointer к int *, когда я, например, увеличу его на n или разыменую. Так почему же компилятор действует по-разному в этих двух случаях? Почему он должен заботиться, если тип указателя не совпадает во время присваивания? Я просто хотел бы понять логику этого.

Ответы [ 3 ]

1 голос
/ 19 марта 2019

Преобразования указателя опасны.

Если тип указателя, из которого выполняется преобразование, недостаточно выровнен для целевого типа, вы получите UB (== неопределенное поведение; прочитайте, что это, если вы уже не) уже при преобразовании.

В противном случае, если вы обычно получаете UB при разыменовании, потому что строгие правила псевдонимов C требуют, чтобы вы обращались к объектам через типы lvalue, достаточно совместимые с их эффективным типом.

Хотя последний абзац не совсем применим к преобразованиям в указатели на символы, так как указатели на символы могут иметь псевдоним любого типа, предупреждение (компиляторы также могут сделать это серьезной ошибкой) по-прежнему полезно, поскольку преобразование все еще опасно.

printf("%d", *(char*)&(int){0xFFFF});

даст вам только первый байт (это зависит от порядка байтов, является ли он наиболее значимым или наименее значимым), напечатав 255 (если тип реализации char не подписан) или -1 (если он подписан).

printf("%d", *&(int){0xFFFF});

даст вам всебайты, находящиеся в int.

Если компилятор позволяет назначить char * из int * только с предупреждением, он должен вести себя так же, как и при приведении, но вам нужноприведение в соответствие вашего C (и компилятор, который молчит о преобразовании).

1 голос
/ 19 марта 2019

Из-за случайного изменения типа int * на char * компилятор предупреждает о потенциальных ловушках. С приведением типа компилятор предполагает, что кодеры знают, что они делают.


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

Очень часто для всех указателей объектов (int*, char *, struct foo *) используется один и тот же размер и кодировка, но, как правило, это не требуется.

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

char * и void * имеют одинаковый размер, а указатели кодировки и символов указывают на данные с минимальным требованием выравнивания. Преобразование не char* указателя объекта в char * всегда «работает». Обратное не всегда верно. приводя к неопределенному поведению (UB).

Преобразование в не символьный указатель также создает риск сглаживания (необходимость для компилятора отслеживать, что изменения данных через один тип указателя отражаются в другом). Очень странное неопределенное поведение может привести.> Лучший код позволяет избежать изменения типа указателя 1 и, при необходимости, является явным с приведением типа.


1 Исключения включают изменения указателя объекта на void * или форму void*, когда нет проблем с выравниванием.

0 голосов
/ 19 марта 2019

Как говорит @Mike Holt, на самом деле он не отличается, за исключением того, что вы сказали компилятору: «Не волнуйтесь, я хочу это сделать».

Компилятор действительно беспокоится, поскольку присваивает указательдругой тип, как правило, не то, что вы хотите сделать.Ваш код говорит компилятору "Обработать память, содержащую эту переменную, как если бы она содержала переменную другого типа".Это почти наверняка поведение платформы и, возможно, неопределенное поведение в зависимости от типов.

...