#c #casting #linked-list #integer #void-pointers
#c #Кастинг #связанный список #целое число #void-указатели
Вопрос:
Я видел много тем о типизации целых чисел в void *, но мне все еще трудно понять, почему это работает без их приведения и почему решение этих предупреждений занимает так много времени, по крайней мере, так, как я это вижу.
Я попытался сделать эту тему как можно меньше, но пытаюсь все объяснить, но я думаю, что людям намного легче понять мою проблему, объяснив шаги, которые я сделал, как я пытался это сделать и т. Д.
Я работаю со связанными списками, и моя структура :
typedef struct list_t {
struct list_t *prev;
void *data;
struct list_t *next;
}list_s;
Вначале я просто устанавливаю data
тип как целое число, потому что я всегда буду использовать целые числа в своей программе. Моя единственная проблема заключалась в том, что я хотел установить значение данных равным NULL, чтобы проверить, есть ли что-то в моем связанном списке (если я уже использовал его один раз).
Некоторые люди сказали "just set it to zero"
бы, но у меня может быть нулевое значение в моем списке, поэтому в какой-то момент может возникнуть ошибка.
Я думал о just setting a variable
таких, как "has_list_been_already_used"
или что-то в этом роде, но я думаю, что это просто сделало бы мой код намного больше просто так.
Моя первая мысль после этого предупреждения :
warning: assignment to ‘void *’ from ‘int’ makes pointer from integer without a cast
было: «о, я просто приведу его к void *, (void*)my_atoi(datastring);
и все.
но я получил еще одно предупреждение :
warning: cast to pointer from integer of different size
На данный момент я не знал, что делать, и на самом деле не обнаружил никаких проблем с приведением типов таким образом. С другой стороны, я нашел другой способ сделать это, но мне интересно, правильно ли это и есть ли другой способ, который не приводит к изменению почти всего моего кода и его расширению. Позвольте мне объяснить :
Кто-то сказал, что вы могли бы просто использовать integers
void *
этот способ :
int x = 10;
void *pointer = amp;x;
и получение его в коде, таком как :
int y = *((int *) pointer);
В моем коде, везде, где я буду извлекать свои данные из своей структуры, мне всегда придется делать это таким образом.
Это действительно единственный вариант, который у меня есть? И почему простое преобразование типа из an integer
в a void*
не выполняет эту работу, особенно если оно «работает хорошо», но на самом деле имеет предупреждения.
Спасибо.
Комментарии:
1. Возможно, вы могли бы использовать объединение { int, void *}, но нет способа отличить нулевые значения, поскольку 0 является допустимым целым числом. В противном случае обычным способом ввода int в data было бы data = malloc(sizeof(int)); *(int *)данные = значение;
2. Будет ли эта реализация жизнеспособной, поскольку я работаю со структурами? Я не вижу способа просто ввести
data
тип поля из моей структуры, я могу просто привести весьstructure->data
«тип». Я не уверен, как подойти к этому наилучшим образом, поскольку я немного новичок в работе со связанными списками и структурами таким образом.3. Почему у вас должен быть «элемент в списке, который не используется»? Если вы это сделаете, и у вас нет уникального значения (например,
INT_MIN
), чтобы пометить его, было бы проще ввести другойstruct
элемент для выполнения задания, чем использовать переменную-указатель для хранения целочисленных данных.4. Вы должны быть в состоянии избавиться от
warning: cast to pointer from integer of different size
предупреждения, приведя целое число кintptr_t
первому (например(void*)(intptr_t)my_atoi(datastring)
). Это немного взлом, потому что, хотя преобразование изvoid *
вintptr_t
и обратно вvoid *
гарантированно сравнивается с оригиналомvoid *
, преобразование изintptr_t
вvoid *
и обратно вintptr_t
не гарантировано сравнивается с оригиналомintptr_t
. На практике это вряд ли будет проблемой.5. Все это ненужный взлом, и его следует избегать.
Ответ №1:
Указатель преобразования типов обычно ничего не делает внутри. Однако он сообщает вашему компилятору, что он должен обрабатывать указатель как типизированный указатель.
Итак, указатель — это буквально это. Он указывает на место в памяти. И какой тип объекта хранится в этом месте, не всегда известно. Вы можете сообщить своему компилятору, что это целое число, путем приведения: ( int* iptr = (int*) voidptr
) Теперь ваш компилятор будет считывать ячейку памяти, на которую указывает указатель, как если бы это было целое число. Если это не целое число, вы получите искаженные данные.
Теперь этот пример:
int x = 10;
void *pointer = amp;x;
Это нормально, потому что вы говорите «сделать указатель (на что угодно) из этого указателя на int», что нормально, потому что указатель на int всегда указывает на что угодно.
Но если вы сделаете это другими способами, вы должны явно указать, что вы уверены, что там хранится int , потому что компилятор не знает.
Если вы хотите, вы можете просто создать linkedlist целых чисел. Тогда вам даже не нужно использовать указатели. Просто поместите целое число в структуру вместо void*
. Причина, по которой эта структура linkedlist написана таким образом, заключается в том, что она позволяет использовать данные любого типа или структуру данных. Вот почему они добавили такие вещи, как шаблоны в c . (Но сейчас мы говорим о c)
Правильным способом c для этого, вероятно, было бы создать макрос, подобный
#define declare_LL_type(type) ...
Что создало бы пользовательское типизированное определение структуры linkedlist.
Комментарии:
1. Изначально я использовал
integers
вместоvoid *
, но, как я уже говорил выше, мне нужен был способ проверить, не в первый ли раз я проходил свой цикл. Но я предполагаю, что я избегу всего этого, используя целое число вместо void * в своей структуре и используя другое значение, чтобы узнать, когда я прошел свой цикл один раз, например, установив для переменной значение 1 перед вводом в цикл, и если оно равно 1, я просто установлю его равным нулюи он больше не будет входить.