Когда использовать указатель void?

Я понимаю использование указателя void для реализации malloc.

void* malloc  ( size_t size );

Может ли кто-нибудь предложить другие причины или предоставить некоторые сценарии, где это полезно на практике.

Спасибо

Ответ 1

Один хороший сценарий void* используется, когда вы хотите внедрить любые общие ADT, если вы просто не знаете, какой тип данных он будет хранить и решать. Например, связанный список выглядит следующим образом:

typedef struct node_t node;
struct
{
    void* data;
    node* prev, next;
} node_t;

typedef struct list_t list;
typedef void* (func)(void*) cpy_func;
typedef void (func)(void*) del_func;
struct
{
   node* head, tail, curr;
   cpy_func copy;
   del_func delete;
} list_t;

initializeLinkedList(cpy_func cpy, del_func del);
//here you keep going defining an API

Здесь, например, вы передадите указатели функций инициализации на другие функции, которые будут способны копировать ваш тип данных в ваш список и освобождать его потом. Поэтому, используя void*, вы делаете свой список более общим.

Я думаю, что void* остался на С++ только из-за обратной совместимости, поскольку на С++ у вас есть более безопасные и изощренные способы достижения такого же результата, как шаблоны, функторы и т.д., и вам не нужно использовать malloc while программирование С++.

Что касается С++, у меня нет конкретных полезных примеров.

Ответ 2

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

Указатели Void, вероятно, не должны использоваться очень часто, но они могут помочь, когда вы пытаетесь использовать библиотечную функцию, которая работает с произвольными указателями, и все равно, какие данные представлены этой памятью.

Ответ 3

void указатели должны использоваться в любое время, когда содержимое блока данных не имеет значения. Например, при копировании данных содержимое области памяти копируется, но формат данных не важен.

Для функций, работающих на блоках памяти без необходимости понимать содержимое с помощью указателей void, разъясняет дизайн пользователям, чтобы они знали, что функция не заботится ни о каком формате данных. Часто функции кодируются, чтобы взять char * для обработки блоков памяти, когда функция на самом деле является агностической.

Ответ 4

В С++ я нашел наиболее убедительный прецедент для указателей void *, чтобы предоставить код для хранения произвольных "пользовательских данных" на объекте, который они уже используют.

Скажем, вы написали класс, представляющий Car, для использования в программном обеспечении, которое делает полезные вещи с объектами Car (имитация трафика, инвентарь для проката автомобилей, что угодно). Теперь скажите, что вы оказались в ситуации, когда ваше приложение хочет отслеживать произвольное содержимое ствол Car. Детали того, что хранится в багажнике, не важны для класса Car и могут быть любыми - это действительно зависит от цели приложения, использующего класс Car. Введите указатель void *.

class Car
{
    public:

        // Existing methods of your Car class

        void setContentsOfTrunk(void* contentsOfTrunk);
        void* contentsOfTrunk() const;

    private:

        void* m_contentsOfTrunk;
}

Теперь любое приложение, использующее класс Car, имеет возможность присоединить произвольный объект данных к существующему объекту Car, чтобы его можно было получить из любого кода, который имеет объект Car. Содержимое туловища "перемещается" с объектом Car, где бы он ни находился в вашем коде.

В этом случае есть два варианта использования void *.

Первым является шаблон вашего класса, основанный на типе содержимого содержимого соединительной линии:

template <class TrunkContentsType>
class Car
{
    public:

        // Existing methods of your Car class

        void setContentsOfTrunk(TrunkContentsType contentsOfTrunk);
        TrunkContentsType contentsOfTrunk() const;

    private:

        TrunkContentsType m_contentsOfTrunk;
}

Это кажется излишне инвазивным. Тип содержимого багажника важен только для приложения. Алгоритмы и структуры данных, работающие с объектами Car, не заботятся о том, что в багажнике. Шаблозируя класс, вы вынуждаете приложения, используя класс, выбирать тип содержимого соединительной линии, но во многих случаях приложения также не заботятся о содержимом соединительной линии.

Второй альтернативой является вывод нового класса из Car, который добавляет член данных и аксессоров для содержимого соединительных линий:

class Car
{
    public:

        // Existing methods of your Car class
        // No methods having anything to do with trunk contents.

    private:

        // No data member representing trunk contents.
}

class CarWithTrunkContents
{
    public:

        // Existing methods of your Car class

        void setContentsOfTrunk(TrunkContentsType contentsOfTrunk);
        TrunkContentsType contentsOfTrunk() const;

    private:

        TrunkContentsType m_contentsOfTrunk;
}

Новый класс CarWithTrunkContents - это класс, специфичный для приложения, который добавляет член данных типа, в котором приложение должно хранить содержимое багажника на автомобиле. Это также кажется излишне тяжелым. Почему вы должны получить целый новый класс для добавления дополнительной части данных, которая не влияет на поведение класса? И если для приложений, использующих класс Car, для хранения содержимого соединительных линий достаточно распространено, зачем заставить каждое приложение выводить новый класс для своего конкретного типа содержимого соединительной линии?

Наконец, хотя мой надуманный пример содержимого соединительной линии может рисовать яркую картину произвольного содержимого соединительной линии, перемещающегося с объектом Car, на практике вы, скорее всего, обеспечите еще более общий механизм для привязки данных, относящихся к конкретным приложениям, к Car:

class Car
{
    public:

        // Existing methods of your Car class

        void setUserData(void* userData);
        void* userData() const;

    private:

        void* m_userData;
}

Таким образом, приложение может присоединить объект, представляющий содержимое соединительной линии, или объект, представляющий лицензию и регистрацию водителя, или объект, представляющий договор аренды, или что-то еще. Я видел такой указатель void *, который называется userData (т.е. понимается пользователем класса), "blindData" (т.е. Класс слеп к содержимому объекта, который он несет) или "applicationData" ( т.е. данные типа и цели, определенные приложением).

Ответ 5

Другим примером таких C "generics", реализованных с помощью void *, является стандартная функция qsort:

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

Вы можете отсортировать массив любого типа: int, long, double, char * или некоторые указатели на структуру...

Ответ 6

БОЛЬШОЙ способ узнать все о void * и других темах C - посмотреть первую половину фантастического Стэнфорда "Программирующие парадигмы" на iTunes-U. Это действительно объясняет void * (C generics) и указатели в целом фантастически! Это определенно помогло мне лучше узнать C...

Одним из самых больших применений является использование void *, если вы хотите иметь возможность принимать различные типы данных в функции. (heres пример: http://142.132.30.225/programming/node87.html)

Вот еще один пример того, что вы можете использовать для:

  int i;
  char c;
  void *the_data;

  i = 6;
  c = 'a';

  the_data = &i;
  printf("the_data points to the integer value %d\n", *(int*) the_data);

  the_data = &c;
  printf("the_data now points to the character %c\n", *(char*) the_data);

Если вы не хотите смотреть бесплатные классы stanford, я бы рекомендовал указатель void googling и прочитать весь материал там.

Ответ 7

Он обычно используется в числовом коде, например, функция C root solver может выглядеть так:

double find_root(double x0, double (*f)(double, void*), void* params)
{
/* stuff */
y = f(x, params);
/* other stuff */
}

params отбрасывается f в некоторую структуру, о которой он знает, но find_root не делает.

Ответ 8

Рядом с интерфейсом с C я обнаруживаю, что использую только указатели void, когда мне нужно отлаживать/трассировать некоторый код и, как знать адрес определенного указателя.

SomeClass * myInstance;
// ...
std::clog << std::hex << static_cast< void* >(myInstance) << std::endl;

Выведет что-то вроде

0x42A8C410

И, на мой взгляд, хорошо документирует то, что я пытаюсь сделать (знайте адрес указателя, а не что-нибудь об этом экземпляре)

Ответ 9

Указатели Void полезны, когда вы пишете код, который должен запускаться во многих операционных системах, и должен быть достаточно агностиком для базовых API-интерфейсов.

Например, OS X, Windows и Linux имеют базовую концепцию оконного объекта, но все они очень разные. Таким образом, у меня есть общий код, который передает их как void *, а затем конкретные реализации платформы, которые передают void * на собственный тип (HWND и т.д.).

Но, как говорили другие в этой теме, такого рода вещи следует избегать, если это необходимо.

Ответ 10

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

Важно то, что sqlite3_exec() не волнует, что делает обратный вызов и какой указатель вы передаете. void * точно для таких указателей.

Ответ 11

void * действительно является C-ism и позволяет C делать некоторые вещи, которые он не мог бы разумно сделать в противном случае.

char * нельзя использовать с любой точки зрения, поскольку разные платформы могут создавать разные типы указателей. char * не обязательно обрабатывается одинаково (или имеет одинаковый размер) как void *.

Итак, когда тип данных не известен в C (или является полиморфным или иным образом динамическим), то void * позволяет вам генерировать правильный базовый тип указателя - тот, который может правильно указать что-либо.

В С++ void * обычно никогда не должно появляться, кроме как в контексте взаимодействия с устаревшим кодом C в той или иной форме.

Ответ 12

Существует большое преимущество использования указателя void. Переменная указателя - это переменная, которая хранит адрес другой переменной. Пример:

int a;
int *x= &a;

Теперь 'x' хранит адрес целочисленной переменной.

Но это не удается:

float f;
int *x = &f;

Поскольку переменная-указатель Integer может хранить только адрес целочисленной переменной. таким же образом он применяется для других типов данных.

Когда вы используете указатель void *, он дает ребро для хранения адреса любой переменной TYPE.

void *pointer = &i;
void *pointer = &f;

при извлечении его необходимо отложить.

*((int*)pointer)

Итак, аккуратно используйте указатель void.

Это может помочь вам, спасибо.

Ответ 13

int (*f) (void);
f =(void*) getprocaddress(dll,"myfunction");

чтобы сделать компилятор счастливым