Void указатели: разница между C и С++

Я пытаюсь понять различия между C и С++ в отношении указателей void. следующие компиляции в C, но не С++ (все компиляции, выполненные с помощью gcc/g++ -ansi -pedantic -Wall):

int* p = malloc(sizeof(int));

Потому что malloc возвращает void*, который С++ не позволяет назначать int*, в то время как C разрешает это.

Однако здесь:

void foo(void* vptr)
{
}

int main()
{
    int* p = (int*) malloc(sizeof(int));
    foo(p);
    return 0;
}

Оба С++ и C скомпилируют его без жалоб. Почему?

K & R2 говорят:

Любой указатель на объект может быть преобразован в тип void * без потерь информации. Если результат преобразован обратно в исходный указатель тип, исходный указатель выздоровел.

И это довольно суммирует все, что есть о void* преобразованиях в C. Что диктует стандарт С++?

Ответы

Ответ 1

В C преобразования указателей в и из void* всегда были неявными.

В С++ преобразования от T* до void* являются неявными, но void* для чего-либо еще требует отбрасывания.

Ответ 2

С++ более строго типизирован, чем C. Многие преобразования, особенно те, которые подразумевают различную интерпретацию значения, требуют явного преобразования. Новый оператор в С++ - это безопасный тип размещения памяти в куче без явного приведения.

Ответ 3

Полезно понять, что преобразования типа указателя на самом деле не требуют выполнения дополнительных инструкций ЦП. Они анализируются во время компиляции, чтобы понять намерения разработчика. void * - непрозрачный указатель. Все это говорит о том, что тип заостренного объекта неизвестен. C слабо типизирован. Это позволяет прямое преобразование между (void *) и любым (T*) неявным образом. С++ строго типизирован. Преобразование из (void *) в (T*) не будет действительно хорошим примером для строго типизированного языка. Но С++ должен был оставаться обратно совместимым с C, поэтому он должен был допускать такие преобразования. Тогда руководящий принцип: явный лучше, чем неявный. Следовательно, если вы хотите преобразовать (void*) в какой-то конкретный (T*) указатель, вам нужно явно написать это в коде. Преобразование из (T*) в (void*) не требует явного преобразования, так как нет ничего, что можно было бы сделать с прямым указателем (void *) (можно вызвать free(), хотя). Следовательно, преобразование (T*) в (void*) в значительной степени безопасно.