Хобрук: Ваш путь к мастерству в программировании

Указатель + int против точки - int

У меня есть немного памяти, и я пытаюсь выделить ее несколькими кусками. Итак, у меня есть связанный список. Каждый узел отслеживает размер выделенной части памяти и следующего узла.

Когда я возвращаю указатель запрашивающей стороне, я возвращаю указатель сразу после завершения этого узла (скажем, return newNode + sizeOf(node)), потому что запрашивающей стороне нужна только память для использования.

Проблема в том, что когда я пытаюсь освободить его, изменив файл node. Когда myFree вызывается с указателем, и я делаю указатель - sizeOf(node), чтобы добраться до узла, это не работает.

Что я делаю не так?

Я не думаю, что это полезно, но вот код:

#define HEADER(24)
printf("Original pointer %-10p\n", pointer);
head *toUse = pointer + HEADER;
printf("Pointer to memory to be used %-10p\n", toUse);
printf("Trying to read the header again %-10p\n", toUse - HEADER);

Первый и третий printf дают мне разные адреса. Это проблема.

Что касается тестирования, я просто выделяю один кусок памяти в начале, и он все равно не работает.

11.10.2012

  • Если вы выделяете несколько фрагментов, возможно, ваши фрагменты не расположены рядом друг с другом, поэтому pointer-sizeof(node) не обязательно помещает вас в начало предыдущего узла. 11.10.2012
  • Я не думаю, что это допустимый C, #define неверен. 11.10.2012
  • @Mat: это не так, между HEADER и (24) нужен пробел. 11.10.2012
  • Вы не понимаете арифметику указателей. pointer + HEADER и toUse - HEADER будут иметь разные результаты в зависимости от типов toUse и указателя. Покажите код, определяющий эти типы. 11.10.2012

Ответы:


1

В C (pointer + n) эквивалентен &pointer[n]... то есть индекс подсчитывает элементы, на которые указывает указатель, а не байты. Если вам нужно смещение в байтах, используйте ((char*)pointer + n). Но в вашем случае вам не нужно смещение байта; вместо

return newNode + sizeof(node);

ты можешь просто сделать

return newNode + 1;

or

return &newNode[1];

Хотя вы, вероятно, захотите привести их к (void*), если вы возвращаете указатель на что-то, что вызывающая сторона может использовать как любой тип. Чтобы вернуться к исходному узлу из указателя (void*), используйте либо (node*)vp - 1, либо (node*)((char*)vp - sizeof(node)).

Также,

#define HEADER(24)

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

11.10.2012

2

Вы не дали нам достаточно кода для решения проблемы. Тем не менее, я могу предположить.

Покажите нам декларацию pointer. Я предполагаю, что это не head *pointer;. Арифметика указателя учитывает тип указателя.

Когда вы (например) добавляете 1 к указателю на int, указатель увеличивается на 1 * sizeof int байт. Если бы тип был short, то он был бы увеличен на 1 * sizeof short байт. Не обязательно одно и то же.

Итак, если pointer (опять же, например) объявлено как char *pointer, то добавление к нему HEADER увеличивает указатель на HEADER (24) байт (поскольку sizeof char всегда 1). Однако, когда вы позже вычитаете HEADER из toUse, которое имеет тип head*, оно уменьшается на HEADER * sizeof head байт. Опять же, не то же самое.

11.10.2012

3

Можно использовать еще немного кода, чтобы быть уверенным здесь... но я собираюсь бросить это вам. Вы сказали:

have some memory and I try to allocate it in several pieces. So I have a linked list. Each node keeps track of the size the allocated piece of memory is and the next node.

Итак, я предполагаю, что вы создаете узлы и malloc() выделяете им некоторую память, а затем пытаетесь сделать что-то вроде:

ptr_to_next_node = ptr_to_current_node+sizeof(node);

Ну, это не работает. Вы не можете выполнять арифметические операции с указателями в связанных списках, потому что это не непрерывная память. Причина, по которой арифметика указателей работает с массивами, заключается в том, что память, которую вы получаете, будет непрерывной:

 char array [0][1][2][3]
             ^  ^  ^  ^
             |  |  |  +----------0x86C00004
             |  |  +-------------0x86C00003
             |  +----------------0x86C00002              
             +-------------------0x86C00001

 linked_list 

    +-------+        +-------+        +-------+
    | node1 |        | node2 |        | node3 |
    | next---------->| next---------->| next---------->NULL
    +-------+        +-------+        +-------+
    (0x86C0001)      (0x86C000A)      (0x86C00BC)

Итак, значения в памяти могут не иметь особого смысла, но вы можете понять, что я пытаюсь здесь проиллюстрировать. Массивы sizeof(type) отделены друг от друга, память, которую вы выделяете своим узлам, может быть практически где угодно, мы не можем просто добавить смещение, поэтому у них есть указатели на next.

Если вы хотите иметь возможность прыгать вперед и назад, вам нужно добавить указатели вперед next и назад prev.

11.10.2012
  • Да, я понимаю. Я не использовал malloc, но мои указатели были другого типа. Я смог это исправить. 12.10.2012
  • Новые материалы

    Получение стоковых обновлений с помощью Python
    Для начинающего финансового аналитика Введение Описание: Этот проект Python создает скрипт для получения текущих обновлений акций с финансового веб-сайта Yahoo. Для этого проекта мы..

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

    Текстовый графический интерфейс с Lanterna на Java
    Мой опыт работы с компьютерами (и текстовыми графическими пользовательскими интерфейсами) начался еще в восьмидесятых, когда я был ребенком, на дне рождения друга. Это был «новенький» Amstrad..

    Перезарядите свой мозг: умопомрачительный потенциал мозговых компьютерных интерфейсов
    Способность читать свои мысли и управлять объектами разумом долгое время были предметом человеческого любопытства, ограниченного областью научной фантастики… то есть до сих пор? С технологией,..

    Основы C# — Нулевой оператор объединения (??)
    Оператор ?? называется null-coalescing operator . Этот оператор используется для предоставления значения по умолчанию, если значение операнда в левой части оператора равно null ...

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

    В мир искусственного интеллекта…
    ИИ — это новое топливо в современном мире. Куда бы вы ни обратились, с кем бы вы ни разговаривали — они, как правило, упоминают об ИИ хотя бы раз в ходе разговора. ИИ гудит повсюду. У каждого..