Форум программистов, компьютерный форум, киберфорум
C для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.73/40: Рейтинг темы: голосов - 40, средняя оценка - 4.73
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231

Указатель на void и работа с ниим

28.04.2021, 18:12. Показов 8730. Ответов 45

Студворк — интернет-сервис помощи студентам
Здравствуйте!
Недавно начал знакомство с программированием на C.
Читал про указатели. Наткнулся на указатели на void. Насколько я понимаю, эта штука позволяет реализовывать обобщённые алгоритмы?!
Предположим, я хочу реализовать алгоритм линейного поиска в массиве. Какой дизайн данной функции?
Мой массив хранит указатели типа void? Как мне в таком случае пробежаться по массиву? Мне нужно знать размер элемента? Как мне сравнить элементы? Я должен передавать в функцию компаратор (в виде указателя на функцию?).
Вопросы, одни вопросы. Ответов на которые у меня нет.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
28.04.2021, 18:12
Ответы с готовыми решениями:

Указатель типа void. Использование косвенного связывания через универсальный указатель
Необходимо использовать косвенного связывания через универсальный указатель, примерный вид: struct обобщение{ключ; void* на что угодно}...

Функция принимает указатель на void и возвращает указатель на int
Запишите прототип функции, которая принимает указатель на void и возвращает указатель на int.

Дописать методы void pop_back(void); T& back(void); T& front(void); void erase (iterator p)
Задали задание, и не получается дописать оставшиеся методы: void pop_back(void); T& back(void); T& front(void); void erase...

45
844 / 353 / 68
Регистрация: 20.11.2012
Сообщений: 852
28.04.2021, 22:21
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от schoolboy_ Посмотреть сообщение
v->data = malloc(sizeof(void *) * v->capacity);
Зачем sizeof(void*)? Нужно выделять кратно размеру элемента.
Цитата Сообщение от schoolboy_ Посмотреть сообщение
Кажется что память по указателю x утекает.
free обычно применяют к тому указателю, для которого выделялась память. free(vector->data) освобождает память, что выделена в init. x не освобождается.
Зачем так делать вообще? К типу void* можно привести любой указатель. Т.е.
C
1
2
int x = 1;
vector_push_back(&v, (void *)&x);
Ну и в самой функции push_back делать
C
1
memcpy( (char*)data + index*elemsize, x, elemsize );
1
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13214 / 6847 / 1825
Регистрация: 18.10.2014
Сообщений: 17,333
28.04.2021, 22:29
Цитата Сообщение от WhiteP Посмотреть сообщение
Что именно тут непонятно?
C
1
2
3
4
int comp (const int *i, const int *j)
{
    return *i - *j;
}
Тут непонятно все. Числа типа int никто никогда не сравнивает путем вычитания, ибо при переполнении получится чушь.

Цитата Сообщение от WhiteP Посмотреть сообщение
C
1
qsort(arr, 10, sizeof (int), (int(*) (const void *, const void *)) comp);
И тут непонятно все. Никто никогда не пишет функции сравнения для qsort с параметрами конкретных типов, чтобы потом заниматься "овнокодингом": пионэрским приведением типа с неопределенным поведением.

Функции сравнения для qsort всегда имеют параметры типа const void * и только const void *, без вариантов. Сравнение двух int делается так

C
1
2
3
4
5
int comp(const void *p1, const void *p2)
{
  int i = *(const int *) p1, j = *(const int *) p2;
  return (i > j) - (i < j);
}
и, соответственно, вызов без "овнокодинга"

C
1
qsort(arr, 10, sizeof *arr, comp);
---

Цитата Сообщение от schoolboy_ Посмотреть сообщение
Такой случай интересен. Предположим, у меня есть такая функция:
C
1
void set(void* arr, size_t number, size_t size, size_t  index, void* item)
Как мне положить item по i-му индексу. Если я знаю, что размер его size_t size,?
Во-первых, при чем здесь этот случай, если сначала вы вели речь об "массиве указателей типа void *". В этом примере ничего подобного нет.

Во-вторых, item вы в таком случае получаете через memcpy

C
1
2
3
unsigned char *src_item = arr;
src_item += index * size;
memcpy(item, src_item, size);
Добавлено через 2 минуты
Цитата Сообщение от schoolboy_ Посмотреть сообщение
Если у меня массив типа void*. А я хочу хранить в нём целый числа. ... Если на псевдокоде, что я хочу такое.

C
1
2
3
4
void* arr;
arr[0] = 1;
arr[1] = 2;
arr[2] = 3
Это вообще какой-то бессмысленный набор букв и бессмысленный псевдокод. Никакого "массива типа void*" тут нет.

Вы сами пока не знаете, чего хотите. Порка вы сами не поймете, чего вы хотите, тратить время в форуме совершенно бесполезно.
2
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231
28.04.2021, 22:42  [ТС]
я понял.
Когда я пишу
C
1
void *data;
то у меня указатель на void*. Если я пишу, void** data, то у меня массив указателей типа void*. Теперь понятно. Большое спасибо.
Цитата Сообщение от WhiteP Посмотреть сообщение
Зачем sizeof(void*)? Нужно выделять кратно размеру элемента.
вы правы. должно быть так.
C
1
v->data = malloc(elem_size * v->capacity);
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13214 / 6847 / 1825
Регистрация: 18.10.2014
Сообщений: 17,333
28.04.2021, 22:48
Цитата Сообщение от schoolboy_ Посмотреть сообщение
Но самое главное, почему не ругается valgrind.
Бессмысленно разглагольствовать на тему того "почему не ругается valgrind", видя лишь какие-то огрызки кода.
0
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231
28.04.2021, 22:50  [ТС]
Получается, если у меня есть такие куски кода:
C
1
 v->data = malloc(elem_size * v->capacity);
C
1
void vector_push_back(vector_t *v, void *elem);
C
1
2
int x = 1;
vector_push_back(&v, (void *)&x);
C
1
memcpy( (char*)data + index*elemsize, x, elemsize );
Получается, теперь память не будет утекать? Когда вызывается vector_push_back то элемент копируется в выделенную память. После этого free освобождает весь выделенный кусок. Кажется так? Память не утекает и нет обращения по невалидной памяти.
0
844 / 353 / 68
Регистрация: 20.11.2012
Сообщений: 852
28.04.2021, 22:51
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Тут непонятно все. Числа типа int никто никогда не сравнивает путем вычитания, ибо при переполнении получится чушь.
Согласен, скопировал не вникая из справочника, про переполнение не подумал.

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Никто никогда не пишет функции сравнения для qsort с параметрами конкретных типов, чтобы потом заниматься "овнокодингом": пионэрским приведением типа с неопределенным поведением.
Принято, из того же источника.
Однако почему приведение типа функции это UB? Размер указателя на void и указателя на int равны ведь? (x86) push 4 байта, push 4 байта, call func. В стандарте Си это есть? Надо посмотреть.
0
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231
28.04.2021, 22:51  [ТС]
WhiteP, вы мне очень помогли. Большое спасибо.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13214 / 6847 / 1825
Регистрация: 18.10.2014
Сообщений: 17,333
28.04.2021, 22:57
Цитата Сообщение от WhiteP Посмотреть сообщение
Однако почему приведение типа функции это UB? Размер указателя на void и указателя на int равны ведь? push 4 байта, push 4 байта, call func. В стандарте Си это есть? Надо посмотреть.
Само приведение типа функции - это не UB. Приведение между типами указателей на функции разрешено в любых комбинациях и направлениях. Однако вызывать функцию через результат приведения разрешается только в том случае, если типы функций совместимы. Иначе будет UB. (http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p8)

Типы int (const int *, const int *) и int (const void *, const void *) не совместимы. (http://port70.net/~nsz/c/c11/n1570.html#6.2.7, http://port70.net/~nsz/c/c11/n1570.html#6.7.6.3p15)

Языку С глубоко плевать на то, какие там размеры имеют указатели void * и int *. Это здесь никакой роли не играет. Однако в общем случае в С void * и int * имеют разные размеры.
0
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231
28.04.2021, 23:00  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Языку С глубоко плевать на то, какие там размеры имеют указатели void * и int *. Это здесь никакой роли не играет. Однако в общем случае в С void * и int * имеют разные размеры.
почему? ведь и то и то адрес, который задаётся архитектурой?
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13214 / 6847 / 1825
Регистрация: 18.10.2014
Сообщений: 17,333
28.04.2021, 23:13
Цитата Сообщение от schoolboy_ Посмотреть сообщение
почему? ведь и то и то адрес, который задаётся архитектурой?
И что?

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

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


Язык С гарантирует совпадение представлений указателей только для

1. void *, char *, signed char *, unsigned char *
2. Указатели на квалифицированные версии одного и тот же типа (const, volatile)
3. Все struct * указатели между собой
4. Все union * указатели между собой

Больше никаких совпадений нет.
1
844 / 353 / 68
Регистрация: 20.11.2012
Сообщений: 852
28.04.2021, 23:36
ISO/IEC 9899:201x 6.3.2.3-8
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type,the behavior is undefined.
6.2.5-28
A pointer to void shall have the same representation and alignment requirements as a
pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements.
Все понятно.
Получается, стандарт просто не оговаривает (читай не гарантирует) совпадение представлений для отличных от char* указателей с void*.

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
. Вполне может быть, что особенности хранения int на данной платформе (выравнивание, регион памяти) таковы, что для int * можно использовать более компактный адрес, чем для void *.
Интересно, есть ли такие архитектуры в реальности?
0
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231
29.04.2021, 07:24  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
А int * - это указатель, который может указывать только на int (или быть нулевым). Вполне может быть, что особенности хранения int на данной платформе (выравнивание, регион памяти) таковы, что для int * можно использовать более компактный адрес, чем для void *.
Как это возможно? Получается, если размер указателя меньше машинного слова, то такой указатель не может адресовать любой байт памяти?
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13214 / 6847 / 1825
Регистрация: 18.10.2014
Сообщений: 17,333
29.04.2021, 07:34
Цитата Сообщение от schoolboy_ Посмотреть сообщение
Как это возможно? Получается, если размер указателя меньше машинного слова, то такой указатель не может адресовать любой байт памяти?
Совершенно верно. Указатель int * и не должен уметь адресовать любой байт памяти. Указатель int * должен уметь адресовать только объекты типа int. На некоей машине с 8-байтовым int и строгими требованиями выравнивания в указателе int * три младших бита будут всегда равны нулю - вот мы уже получили указатель, которому "не нужны" его младшие три бита.

А если на какой-то экзотической платформе требования выравнивания для типа int равны 256 - мы получаем указатель, которому "не нужен" его младший байт.
1
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231
01.05.2021, 16:35  [ТС]
Такой вопрос. Предположим, у нас есть такой массив:
C
1
2
3
4
5
6
typedef struct vector_t {
  void *data;
  size_t size;
  size_t capacity;
  size_t elem_size;
} vector_t;
И мы хотим написать функцию, которая удаляет элемент по индексу. Мы ничего не знаем о природе тех объектов, которые мы храним в массиве.
Как правильно организовать функцию удаления? Ну т.е.. если бы мы писали на ООП языке, то можно было бы вызвать деструктор, а как тут?

Добавлено через 3 минуты
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
На некоей машине с 8-байтовым int и строгими требованиями выравнивания в указателе int * три младших бита будут всегда равны нулю -
Но это же именно биты. Мы не можем адресовать отдельные биты? Мы можем адресовать только байты? Так, зачем вот это:

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
вот мы уже получили указатель, которому "не нужны" его младшие три бита.
Добавлено через 45 минут
Я придумал только один способ удаления. Это передавать функцию удаления в качестве параметра! Кажется, что по другому нельзя?!
0
фрилансер
 Аватар для Алексей1153
6501 / 5732 / 1134
Регистрация: 11.10.2019
Сообщений: 15,350
01.05.2021, 18:08
Цитата Сообщение от schoolboy_ Посмотреть сообщение
Кажется, что по другому нельзя
да )
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13214 / 6847 / 1825
Регистрация: 18.10.2014
Сообщений: 17,333
01.05.2021, 18:26
Цитата Сообщение от schoolboy_ Посмотреть сообщение
Но это же именно биты. Мы не можем адресовать отдельные биты? Мы можем адресовать только байты?
Вы что-то совсем не в ту степь полезли. При чем здесь "адресовать отдельные биты"???

Мы адресуем не указатели, мы адресуем объекты типа int - целые числа. Если объекты типа int на некоей платформе выравниваются на границу 8 байтов, то для адресации таких объектов на этой платформе уже не нужен полный адрес, а достаточно "урезанного" адреса: на 3 бита короче, чем полный.

То есть указатель int * на такой платформе может быть на 3 бита короче, чем указатель void *. Вот вам готовая потенциальная разница в представлениях между указателями int * и void *. Все.

Это совсем не обязательно означает, что такая разница выльется в физическое "укорачивание" указателя int *. Она может вылиться в укорачивание, а может вылиться в просто обнуление младших битов указателя или еще во что-то. А может вообще ни во что не вылиться. Это лишь абстрактный пример.

Каким боком здесь "адресация битов" и зачем вы ее сюда приплели - мне не ясно.

---

P.S. Именно по этой причине стандарты языков С и С++ говорят, что если вы сделаете преобразование указателей "туда-сюда" по схеме T * -> U * -> T *, то в результате вы гарантированно получите исходное значение указателя только в том случае, когда требования выравнивания типа U не являются более строгими, чем требования выравнивания типа T.
0
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231
01.05.2021, 18:29  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Это совсем не обязательно означает, что такая разница выльется в "укорачивание" указателя int *. Она может вылиться в укорачивание, а может вылиться в просто обнуление младших битов указателя или еще во что-то. Это лишь абстрактный пример.
теперь я понял.
0
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231
07.05.2021, 15:41  [ТС]
Подскажите, пожалуйста, какой дизайн должен быть, если я хочу создать простой связный список, который будет хранить любой тип дынных.
Я думал, сделать так:
C
1
2
3
4
struct list_item_t {
    void* data;
    struct list_item_t* next;
};
Но я хочу, чтобы узел "владел" данными внутри себя. Но сделать поле data типа void я не могу. Как быть?
0
фрилансер
 Аватар для Алексей1153
6501 / 5732 / 1134
Регистрация: 11.10.2019
Сообщений: 15,350
07.05.2021, 16:45
Цитата Сообщение от schoolboy_ Посмотреть сообщение
void* data;
нужно хранить вот такой указатель и ещё ввести числовое поле, которое будет определять тип указуемых данных
1
3 / 3 / 1
Регистрация: 02.03.2013
Сообщений: 231
14.05.2021, 08:48  [ТС]
Как быть, если я хочу написать алгоритм бинарного поиска. Для стандартных типов всё просто, например, для чисел это будет так:
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int binary_search_iterative(int* arr, int key, int n)
{
    int left = 0, right = n;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] == key)
            return mid;
        else if (arr[mid] < key)
            left = mid + 1;
        else
            right = mid - 1;
    }
    return -1;
}

Но как быть, если тип производный?
Когда я писал свою реализацию линейного поиска, то передавал компаратор, и выглядело это так:
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
8
9
10
11
long long vector_find(vector_t *v, void *key, int (*cmp)(void *, void *)) {
  if (v != NULL) {
    for (size_t i = 0; i < v->size; i++) {
      void *current = (char *)v->data + i * v->elem_size;
      if (cmp(current, key))
        return i;
    }
    return -1;
  }
  return -1;
}


Получается, что в случае с бинарным поиском я должен передавать два компаратора? Или можно обойтись одним?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
14.05.2021, 08:48

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

Указатель типа void
Добрый день. enum Type { Double, Int, Float }; void func(int k,enum Type t,...) { void *pv; pv = (...); ... }

Указатель типа void c++
Haпример указатель типа void*ptr char *p void *ptr=&quot;abs&quot;; p=((char *)ptr);Так указатель неопределённого типа, присваивается к...

Указатель void в структуре
Здравствуйте. Вот есть структура, например, и в ней поле void *data. Вопрос такой - я могу в дату повесить массив строк? Т.е. я знаю,...

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


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Модель по догадкам
anaschu 25.08.2026
Прошло две недели. Я уже рассказывал, как разговаривал с сотрудниками у сортировки и как понял, что главная ветка — не про приёмку, а про отбор. Но тогда я думал, что понял механику. На этой неделе я. . .
Запись в регистр сведений независимо от заполненности табличной части
Maks 25.08.2026
Реализация из решения ниже выполнена на нетиповом документе с несколькими табличными частями, разработанного в КА2. Задача: Обеспечить запись документа в регистр сведений независимо от. . .
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru