Я знаю, что void type вообще не имеет значения https://www.gnu.org/software/c-intro-and-ref/manual/html_node/The-Void-Type.html в C. Но у меня есть видел некоторые коды на C, в которых некоторые переменные параметров передавались как указатель void:
return_type my_function_signature(void* param1, int num_count, double real_value){
type_to_cast_to var_param1 = (type_to_cast_to *)param1;
// write something here
enter code here
return return_value;
}
Интересно, какой смысл иметь void* param1 в списке параметров, если в теле функции точно известен тип для приведения?
Это не функция шаблона в C++ https://en.cppreference.com/w/cpp/language/variable_template.
Любая помощь от знатоков C и C++?
Его полезность состоит в том, чтобы скрыть детали реализации. «Внешний» пользователь указателя просто знает, что это непрозрачный указатель, но внутри кода он приводит его к правильному типу.
@Eljay, риск сделать это в C++ заключается в том, что можно попытаться передать необработанные данные в экземпляр класса, который не начинает свое существование и, следовательно, является неверным.
Подумайте, например, о malloc. Он возвращает указатель на что-то, что не имеет определенного типа, пока не будет использовано для хранения чего-либо.
Также взгляните, например, на pthreads и на то, как в него передается функция потока. Если вам нужен несколько «обобщенный» API, вам нужно иметь возможность иметь некий «универсальный» тип, который можно будет законно преобразовать в другие типы.
@Eljay Этого можно добиться с помощью предварительного объявления какого-либо типа. Это имеет то преимущество, что является указателем на правильный тип и не раскрывает реализацию «внешнему» пользователю. void указатели в C используются для передачи указателей на «любые» данные. Как в примере с malloc или передачей непрозрачных пользовательских данных обратно пользователю.
@Pepijn Kramer вопрос касается исключительно буквы «C». Но их нередко можно увидеть и в «C++». Как вы упомянули, устаревшие функции.
Обязательно спросите в контексте C. Если вы спросите в контексте C++, вы просто получите поток вопросов: «Зачем стрелять себе в лицо?» комментарии. Если вам повезет, кто-нибудь напишет ответ, показывающий, как можно избежать указателей void в каждом случае, не связанном с устаревшим кодом. Что отвечает на вопрос: «Обычно бесполезно в C++».
Некоторая путаница может возникнуть из-за того, что тип void обозначает «ничего», а void* — это указатель на «что угодно».
Используется в MPI для параллельной обработки для распространения общего типа данных: например. mpich.org/static/docs/v3.0.x/www3/MPI_Sendrecv.html
@Eljay, для простоты использования я просто смотрю на определение функции и передаю ей количество переменных с соответствующими типами, не заглядывая в исходный файл, в котором она была определена. Так что я не вижу в этом риска. memcpy, memmove и т. д.
Опасность заключается в том, что функция ожидает заданный тип, и компилятор не может проверить, действительно ли тип аргумента соответствует ожидаемому, что позволяет легко случайно передать foo вместо bar и не узнать, пока программа не выйдет из строя во время выполнения. memcpy и memmove просто оперируют сгустками памяти, не применяя никакой интерпретации к тому, что они копируют. Они не могут потерпеть неудачу, но программист все равно может использовать их неправильно и увидеть результаты ошибок позже.
@ЕвгенийШ. pthreads — это устаревший API.
memset, memcpy и memmove также являются API-интерфейсами в стиле «C». И вообще, в современном C++ им уже нет места. Вы можете очистить структуры при инициализации. struct S{ int x; }; S s{}. Вы можете использовать typesafe std::copy вместо memcpy. И т. д... вот что имеет в виду @user4581301, когда говорит, что в C++ есть способы избежать void* в каждом случае.
В ответе ниже в качестве примера используется qsort. Хороший пример, но qsort — это еще одна функция C. Функции сортировки стандартной библиотеки C++ являются шаблонными, что позволяет компилятору определить типы и убедиться, что они имеют смысл при построении программы. Обычно это лучшее время для решения проблемы, чем когда вы находитесь в поле с разгневанным клиентом.
Также обратите внимание, что приведенный выше пример, MPI, представляет собой библиотеку, написанную в основном на C и предоставляющую интерфейс C. Интерфейсы C++, как правило, непереносимы (по нескольким причинам), поэтому вы либо выпускаете библиотеку как код для сборки пользователями (но используете менеджер пакетов для выполнения черновой работы, где это возможно), либо распространяется в виде двоичной оболочки. в интерфейсе C, и здесь вы можете использовать void * в C++.





Использование void * в качестве параметра может быть полезно для функций обратного вызова, где точный тип параметра не обязательно известен функции, принимающей обратный вызов, но известен программисту, который его создает.
Это также может быть полезно при работе с универсальными данными, точный тип которых неизвестен.
Хорошо известным примером того и другого является функция qsort из стандартной библиотеки, которая имеет следующее объявление:
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
Здесь параметр base указывает на начало массива, а указатель функции compar принимает указатели на два объекта для сравнения. В обоих случаях функции qsort неизвестно, какой тип массива/элемента.
Предположим, вы хотите отсортировать массив int и массив float. Вы должны создать следующие функции обратного вызова:
int comp_int(const void *p1, const void *p2)
{
const int *i1 = p1;
const int *i2 = p2;
if (*i1 < *i2) {
return -1;
} else if (*i1 > *i2) {
return 1;
} else {
return 0;
}
}
int comp_float(const void *p1, const void *p2)
{
const float *f1 = p1;
const float *f2 = p2;
if (*f1 < *f2) {
return -1;
} else if (*f1 > *f2) {
return 1;
} else {
return 0;
}
}
А функция qsort будет вызываться так:
int a1[5] = { ... };
float a2[10] = { ... };
qsort(a1, 5, sizeof *a1, comp_int);
qsort(a2, 10, sizeof *a2, comp_float);
Действительно ограничьте свой вопрос либо «C», либо «C++». В C++ используйте
void*только тогда, когда имеете дело с устаревшим API C или когда вы знаете, что означают шаблоны и стирание типов.