GCC: разыменование указателя «void *» при получении адреса - PullRequest
0 голосов
/ 26 января 2019

Я заметил триггеры GCC:

warning: dereferencing ‘void *’ pointer

Получая адрес разыменованного void выражения вроде:

int main()
{
    void *p = "abcdefgh";

    printf("%p\n", p);
    printf("%p\n", &*p);

    return 0;
}

Однако выражение p эквивалентно &*p в соответствии со стандартом C:

§6.5.3.2 Операторы адреса и косвенности

Унарный оператор & возвращает адрес своего операнда. Если операнд имеет тип «тип», результат имеет тип «указатель на тип». Если операнд является результатом унарного оператора *, ни этот оператор, ни оператор & не оцениваются и результат, как если бы оба были опущены, за исключением того, что ограничения на операторы все еще применяются, и результат не является lvalue.

Также важно знать, что Clang не вызывает это предупреждение.

ОТКАЗ

Для лучшего объяснения рассмотрим следующее:

int main()
{
    int *p = NULL;

    printf("%p\n", (void *) p);
    printf("%p\n", (void *) &*p);

    return 0;
}

Этот код отлично компилируется и работает как с Clang, так и с GCC. Стандарт C ясен в отношении void указателей, вы можете разыменовать их (таким образом получая значение void), но вы не можете использовать результат выражения.

1 Ответ

0 голосов
/ 30 января 2019

Я предполагаю, что вопрос здесь «Почему это? / Что дает?». В этом случае это действительно небольшая проблема в разработке стандартов / гибкости для разработчиков компиляторов.

Строго говоря, *(void*) всегда плохо, и компилятор правильно предупредит вас. На практике, как вы правильно заметили, указатель на самом деле никогда не разыменовывается, при оценке того, что вы видите на то, на что он указывает, таким образом, у наивной реализации компилятора не будет проблем с тем, чтобы увидеть это так, как вы делаете, и замаскировать это и говорит «указатели: они числа правильно? это 0xFFF ....».

На практике, хотя иногда это не так. Иногда компиляторы становятся довольно умными. Нет хороших примеров, но это не главное.

Несколько вещей, которые, я думаю, стоит отметить в отношении «это никогда не оценивается»:

Причина, по которой компиляторам предоставляется свобода делать то, что они хотят, отчасти облегчает сам процесс компиляции, а не только выводит лучшие реализации. Возможно, следующий компилятор, на который вы указываете, скажет «нет». Просто потому, что так проще и не нужно.

Во-вторых, на самом деле это не точка предупреждений. Если я видел это в обзоре кода:

int i = 3;
if (3-i) {
    ((int*)(NULL)) += 4;
}

моя реакция будет не : "О, хорошо, все в порядке, это не оценено". но "Вау, подожди секунду: почему?" Это то, что делает компилятор тоже. Он говорит вам, что думает, что вы, вероятно, сделали что-то, что вы, возможно, захотите пересмотреть, в этом случае называя const char * a void *. Я, например, согласен с GCC.

...