Подтвердить что ты не робот

Что означает преобразовать int в void * или наоборот?

Что означает преобразование целочисленного значения в void* или наоборот с точки зрения памяти? Мое понимание void* - это адрес в блок памяти неопределенной длины.
Это похоже на сравнение яблока с апельсинами.

int myval = 5;
void* ptr = (void*)myval;
printf("%d",(int)ptr);

Я понял, что должен был дать точный контекст, где это используется.

int main(int argc, char* argv[]) {
long       thread;  /* Use long in case of a 64-bit system */
pthread_t* thread_handles; 

/* Get number of threads from command line */
if (argc != 2) Usage(argv[0]);
thread_count = strtol(argv[1], NULL, 10);  
if (thread_count <= 0 || thread_count > MAX_THREADS) Usage(argv[0]);

thread_handles = malloc (thread_count*sizeof(pthread_t)); 

for (thread = 0; thread < thread_count; thread++)  
  pthread_create(&thread_handles[thread], NULL, Hello, (void*) thread);  

printf("Hello from the main thread\n");

for (thread = 0; thread < thread_count; thread++) 
  pthread_join(thread_handles[thread], NULL); 

free(thread_handles);
return 0;
}  /* main */

/*-------------------------------------------------------------------*/
void *Hello(void* rank) {
long my_rank = (long) rank;  /* Use long in case of 64-bit system */ 

printf("Hello from thread %ld of %d\n", my_rank, thread_count);

return NULL;
}  /* Hello */

Этот код из книги Питера Пачечо о параллельном программировании.

4b9b3361

Ответ 1

Отбрасывание int в void * довольно бессмысленно и не должно выполняться так, как вы пытаетесь применить указатель к указателю. Цитирование C99 standard, раздел 6.3.2.3, пункт 5:

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

Вы можете использовать int * to void * (любой указатель конвертируется в void *, который вы можете представить как "базовый тип" всех указателей).

Отбрасывание void * до int не переносится и может быть полностью неправильным в зависимости от используемой вами платформы (например, void * может быть 64 бита в ширину и int может быть только 32 бит). Повторное цитирование стандарта C99, раздел 6.3.2.3, пункт 6:

Любой тип указателя может быть преобразован в целочисленный тип. Кроме того, ранее указанный, результат определяется реализацией. Если результат не может быть представлен в целочисленном типе, поведение undefined. Результат не должен находиться в диапазоне значений любых целочисленный тип.

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

Ответ 2

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

Ответ 3

В стандарте C указывается, что должно быть возможно преобразовать указатель void в интегральный тип, так что преобразование интегрального типа обратно в указатель void даст тот же указатель. Я не уверен, требует ли это, чтобы преобразование нулевого указателя в целое число приводило к нулевому значению или если только числовые литеральные нули распознаются как специальные. Во многих реализациях целочисленное значение представляет собой аппаратный адрес, но стандарт не дает такой гарантии. Было бы вполне возможно, что на аппаратном обеспечении, которое включало специальную ловушку для аппаратного адреса 0x12345678, преобразование указателя в целое число вычитало бы 0x12345678 с аппаратного адреса, а преобразование целого числа в указатель добавило бы 0x12345678 назад (таким образом, целочисленное значение ноль будет представлять нулевой указатель).

Во многих случаях, особенно при разработке для встроенных контроллеров, поставщик компилятора явно укажет, к какому аппаратным адресам будет обращаться при преобразовании определенного целочисленного значения в тип указателя. На процессорах с одним линейным адресным пространством преобразование целочисленного значения 0x12345678 в указатель обычно дает указатель, который ссылается на адрес 0x12345678; в справочном руководстве по оборудованию будет указано, было ли что-то особенное в этом местоположении. На процессорах с более "интересными" адресными пространствами может быть необходимо использовать что-то иное, кроме аппаратного адреса, как указатель. Например, на антивирусных компьютерах IBM PC буфер отображения отображался на аппаратном адресе 0xB8000, но в большинстве компиляторов адрес был бы выражен как (short far *) 0xB8000000.

Ответ 4

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

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

Это может привести к тому, что ваш номер будет испорчен, если по какой-то причине "int" длиннее, чем "void *" на вашей платформе.

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

Ответ 5

Полезно иметь возможность указывать указатель на целочисленный тип для арифметики указателя. Например, макрос offsetof(), который вычисляет смещение члена внутри структуры, нуждается в таком преобразовании.

Однако необходимо убедиться, что примитивный тип, используемый для этого, способен обрабатывать указатель: здесь, например, для 64 Linux, при использовании gcc это не так: void * имеет размер 8, int имеет размер 4.

Макрос offsetof определяется как таковой:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

Он широко используется в реализации двуядерного списка ядра Linux, который очень просто определяется как:

struct list_head { struct list_head *prev, *next; }

Эта структура встроена в структуры ядра, как в:

struct something {
    //...
    struct list_head list;
    //...
}

При прохождении списка код должен захватить указатель на структуру кодирования. Это делается как это (упрощенное определение):

#define container_of(ptr, type, member) \
    (type *)((char *)ptr - offsetoff(type, member))
#define list_entry(list, type, member) \
    container_of(list, type, member)

И в коде вы очень часто увидите:

struct something *s;
list_for_each(listptr, somehead) {
   s = list_entry(listptr, struct something, list);
   //...
}

который просто не выполним без такой магии и арифметики указателя. Но он очень зависит от инструментальной цепочки.

Ответ 6

Это было бы похоже на сравнение яблок с апельсинами, если бы делались какие-либо попытки сделать какое-либо сравнение. Но нет. В основном, в большинстве архитектур, там int может быть отправлен в указатель void без потери какой-либо информации, а указатель void также может быть возвращен в int, снова без потери какой-либо информации. Конечно, вы не должны пытаться разыменовать этот указатель, потому что он не указывает на какое-либо значимое место в памяти: это просто переменная, содержащая тот же бит-шаблон, что и битовый шаблон, который целое, которое было использовано перед литой.