Является ли приведение целочисленного значения к пустоте * часто используемой парадигмой в обратных вызовах? - PullRequest
1 голос
/ 21 декабря 2009

Вместо того, чтобы отправлять фактический указатель на значение, значение приводится к указателю. Я нашел эти примеры в коде графического интерфейса программы GTK.

g_signal_connect (pastebutton[pane],
                  "clicked",
                  G_CALLBACK(on_paste_button_pressed),
                  (void*)((long)pane<<4));

В приведенном выше примере я имею в виду последний параметр g_signal_connect. Когда GTK2 вызывает on_paste_button_pressed, on_paste_button_pressed возвращает указатель void user_data следующим образом:

int pane = ((long)user_data) >> 4;

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

Часто ли используется этот способ приведения значения к адресу указателя, и есть ли причины, по которым его не следует использовать?

редактирование:

Приведение от целого числа к пустому указателю также может быть достигнуто так:

void* void_ptr = some_int - NULL;

Ответы [ 7 ]

3 голосов
/ 21 декабря 2009

Это используется . используется довольно часто, когда он делает то, что требуется.

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

Еще одна причина не использовать его в том, что он позволяет передавать только один фрагмент целочисленных данных в обратный вызов. Если в будущем вам понадобится добавить еще один фрагмент данных (или переключиться на нецелое число), вам нужно будет найти и переписать каждое место, где обратный вызов делает доступ к переданным данным. Так что, если есть вероятность, что вам придется расширять данные в будущем, лучше создать struct (даже если он содержит только один int в это время) и передать указатель на это struct.

Но если вы уверены, что вам никогда не придется передавать ничего, кроме этого единственного целого числа и что ваше целое число вписывается в void *, то этот метод никоим образом не нарушен.

PS Говоря педантично, ни C, ни C ++, по-видимому, не имеют гарантии возврата туда и обратно для преобразования целое в пустое * в целое, т.е. они не гарантируют, что оно будет работать и восстанавливать исходное интегральное значение.

3 голосов
/ 21 декабря 2009

Вы должны использовать макросы GINT_TO_POINTER () и GPOINTER_TO_INT () для приведения между указателями и целыми числами.

glib: Макросы преобразования типов

1 голос
/ 21 декабря 2009

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

Сдвиг битов - плохая идея, потому что он может вызвать переполнение.

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

0 голосов
/ 21 декабря 2009

Это из C-FAQ:

Q: Как целые числа преобразуются в и из указателей? Могу ли я временно вставить целое число в указатель или наоборот?

A: Когда-то было гарантировано, что указатель может быть преобразован в целое число (хотя никто не знал, может ли потребоваться int или long), и что целое число могло быть преобразовано в указатель, и что указатель остался неизменным при преобразовании в (достаточно большое) целое число и обратно и что преобразования (и любое отображение) должны были быть «неудивительными для тех, кто знает структуру адресации машины». Другими словами , есть некоторый прецедент и поддержка для целочисленных / указательных преобразований, но они всегда были машинно-зависимыми и, следовательно, не переносимыми. Явные приведения всегда требовались (хотя ранние компиляторы редко жаловались, если вы их опускали).

Стандарт ANSI / ISO C для обеспечения широкой реализации C ослабил эти более ранние гарантии. Преобразования указателя в целое и целого в указатель определяются реализацией (см. Вопрос 11.33), и больше нет никакой гарантии, что указатели могут быть преобразованы в целые и обратно без изменений.

Заставлять указатели на целые числа или целые числа на указатели никогда не было хорошей практикой. Когда вам нужен универсальный слот, в котором могут храниться данные любого типа, объединение - гораздо лучшая идея.

См. Также вопросы 4.15, 5.18 и 19.25.

Ссылки: K & R1 Sec. А14.4 с. 210 K & R2 Sec. А6.6 с. 199 ISO Sec. 6.3.4 Обоснование с. 3.3.4 H & S Sec. 6.2.3 с. 170, с. 6.2.7 с. 171-2

0 голосов
/ 21 декабря 2009

Я нахожу это просто отлично. У вас нет статистики о частоте использования, но действительно ли вам все равно?

Я не уверен, что понимаю, как помогает сдвиг битов.

0 голосов
/ 21 декабря 2009

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

0 голосов
/ 21 декабря 2009

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

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

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

Единственная причина, по которой я могу думать, что кастинг может быть там, - это исключить возможность предупреждений выравнивания. Раздел 6.3.2.3 проекта C1x гласит:

Целое число может быть преобразовано в любой тип указателя. За исключением случаев, указанных ранее, результат определяется реализацией, может быть неправильно выровнен, может не указывать на объект ссылочного типа и может быть представлением ловушки.

...