Конверсии указателей

Я читал, что назначение указателя на тип другому указателю на другой тип является незаконным; например, в этой книге:

C Как программировать 7 изд.вол.299

Общая ошибка программирования 7.7 Присвоение указателю одного типа указателю другого типа, если ни один из типов void * не является синтаксической ошибкой.

или по этому URL-адресу:

но в стандарте C11 написано:

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

Поэтому я понимаю, что только при наличии проблемы выравнивания поведение не определено.

На самом деле компиляторы, такие как GCC 4.8 или cl 2013, выдает только предупреждение о присвоении из несовместимого типа указателя.

Это исключение отсутствует для указателя void.

Так:

int a = 10;
float b = 100.22f;

int *p_a = &a;
float *p_f = &b;

p_a = p_f; // NOT ILLEGAL but WARNING
p_f = p_a; // converted back again. it still works

void *v_p = p_a; // NOT ILLEGAL NO WARNING
p_a = v_p; // converted back again. it still works

Я хорошо понимаю? Или я чего-то не хватает?

PS Может ли кто-нибудь показать мне пример "проблемы выравнивания"?

1 ответ

Вы можете рассматривать указатель как смещение в памяти на заостренные данные, и независимо от того, какой тип он есть. Любой стандартный указатель (не smart) имеет фиксированный размер, в зависимости от системы (32 бит или 64). Поэтому вы можете назначить их каждому другому, современный компилятор предупредит вас об опасных операциях, но проблема в том, что вы пытаетесь разыменовать указатель несовместимого типа. При разыменовании приложение просматривает количество байтов, соответствующих указанному типу, поэтому в следующем примере

int b = 10;
int* pB = &b;
******* pA = pB;

при разыменовании pB вы получите 10 (размер int - 4 байта), а при разыменовании pA вы получите мусор или сбой, потому что следующие 4 байта (****** - 8 байт) могут быть пространством памяти, выделенным для другой переменной.

Вывод: отслеживать типы указателей при назначении их друг другу, и особенно если вы назначаете их косвенно, используя void * в качестве посредника. Законность или незаконность присвоения указателя - это вопрос компилятора, старые не предупреждали вас. Присвоение void * не считается "незаконным", возможно, потому, что оно является универсальным, неопровержимым типом указателя (у него есть неопределенный ссылочный размер, и это ограничение ограничено языком c), поэтому вы не можете получить ошибку, как в примере выше.

licensed under cc by-sa 3.0 with attribution.