Портативность литой пустоты ** - PullRequest
0 голосов
/ 15 мая 2018

Я читал http://c -faq.com / ptrs / genericpp.html , когда обнаружил, что ((void **) & ptr); «Не переносимо», это правильно? потому что, кажется, работает ...

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void *wrapper_free(void **p){

     if(p){
       free(*p);
       *p=NULL;
     }

     return NULL;
}

int main() {

    int *ptr=malloc(sizeof(int));
    *ptr=20;

     printf("%d\n",ptr);

     wrapper_free((void **)&ptr); //Not portably?

     printf("%d",ptr);

     return 0;
}

Есть ((void **) & ptr); переносимо

Ответы [ 3 ]

0 голосов
/ 15 мая 2018

Причина, по которой код не является переносимым, заключается в том, что второй уровень косвенности (вторая звездочка) поверх void* не может иметь какой-либо специальной обработки от компилятора. Когда вы конвертируете указатель любого типа в / из void*, компилятор знает, как вставлять преобразования в обе стороны, поэтому конструкция гарантированно будет работать. Когда вы добавляете вторую звездочку, тип выражения больше не void*, поэтому компилятор не делает (и не может) вносить необходимые изменения.

Вот как сделать код в вашем примере переносимым:

void* tmp = ptr; // (1)
wrapper_free(&tmp);
ptr = tmp;       // (2)
printf("%p\n", (void*)ptr);

Это переносимо, потому что tmp равно void*, поэтому &tmp равно void**. Компилятор получает возможность совершать «магию» по строкам, отмеченным (1) и (2). Обратите внимание, что компилятор должен будет что-то сделать с тем же эффектом, если он хочет правильно реализовать void**.

0 голосов
/ 15 мая 2018

Оба предыдущих ответа показали, почему код не переносим в соответствии со стандартом.Я просто хочу добавить пример, где это может пойти плохо.

Предположим, что для цели int* не имеет того же представления, что и void*, а void* имеет значение ловушки.

Теперь ptr присваивается значение из void*, и представление битов автоматически изменяется неявным преобразованием.

Предположим, что битовая комбинация, хранящаяся в ptr, точно соответствует битовой комбинации для представления прерываний для void*

Теперь вы передали &ptr, где ожидается void** с использованием явногобросать.Когда wrapper_free пытается разыменовать p (как void*), он разыменовывает значение ловушки, и ничего нельзя предсказать о поведении программы за этой точкой.

Вместо этого, если вы создадите временную переменную типа void* и присвоите ей int*, произойдет преобразование обратно из int* в void*, и вы гарантированно не попадете в бит ловушки.представление.

Надеюсь, это поможет лучше понять проблему.

0 голосов
/ 15 мая 2018

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

Это означает, что вы не можете неявно преобразовать из void** в int** или наоборот, преобразование должно быть выполнено явно с помощью приведения типа, как в вашем коде. Если / как это работает, определяется реализацией. Это означает, что то, что произойдет, зависит от системы и компилятора - это действительно не переносимо (C11 6.3.2.3/7).

Чтобы сделать ваш фрагмент на 100% переносимым, вам нужно написать:

void* vptr = ptr;
wrapper_free(&vptr);
ptr = vptr;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...