Свопинг объектов с помощью указателей

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

void swap(void *a, void *b, size_t size);

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

void *temp;
temp = a;
a = b;
b = temp;

только изменения, на что указывают указатели. Это корректно? Если это корректно, почему свопинг указателей на самом деле не изменяет содержание между *a и *b. Поскольку, если бы Ваш указатель указывает на что-то другое, разве Вы не могли бы разыменовать его, и объекты теперь отличались бы?

Точно так же просто переключив значения как:

void *temp;
*temp = *a;
*a = *b;
*b = *temp;

Не корректно также, который я не уверен почему. Поскольку снова, мне кажется, что содержание переключается.

Свопинг объектов означает полный свопинг памяти и значение того, на что указывает указатель?

Таким образом, кажется, что я должен использовать malloc для выделения достаточного количества места для моей подкачки. Если я выделяю достаточно памяти для одного объекта, предполагая, что они - тот же размер, я действительно не вижу, как это отличается, чем другие два метода выше.

void *temp = malloc(sizeof(pa));
// check for null pointer
temp = a;
// do something I'm not sure of since I don't quite get how allocating space is any 
// different than the two above methods???

Спасибо!

8
задан Crystal 6 November 2010 в 17:13
поделиться

8 ответов

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

Вы почти угадали:

void swap(void *a, void *b, size_t size) {
  char temp[size]; // C99, use malloc otherwise
  // char serves as the type for "generic" byte arrays

  memcpy(temp, b,    size);
  memcpy(b,    a,    size);
  memcpy(a,    temp, size);
}

Функция memcpy копирует память, что является определением объектов в C. (Для сравнения, в C++ они называются POD или plain ol' data. ) Таким образом, memcpy - это способ присвоения, не заботясь о типе объекта, и вы даже можете писать другие присваивания как memcpy:

int a = 42, b = 3, temp;

temp = b;
b    = a;
a    = temp;
// same as:
memcpy(&temp, &b,    sizeof a);
memcpy(&b,    &a,    sizeof a);
memcpy(&a,    &temp, sizeof a);

Это именно то, что делает приведенная выше функция, поскольку вы не можете использовать присвоение, когда не знаете тип объекта, а void - это тип, обозначающий "неизвестный". (Он также означает "ничего", когда используется как тип возврата функции)


В качестве любопытства, еще одна версия, которая избегает malloc в обычных случаях и не использует VLAs C99:

void swap(void *a, void *b, size_t size) {
  enum { threshold = 100 };
  if (size <= threshold) {
    char temp[threshold];

    memcpy(temp, b,    size);
    memcpy(b,    a,    size);
    memcpy(a,    temp, size);
  }
  else {
    void* temp = malloc(size);
    assert(temp); // better error checking desired in non-example code

    memcpy(temp, b,    size);
    memcpy(b,    a,    size);
    memcpy(a,    temp, size);

    free(temp);
  }
}
20
ответ дан 5 December 2019 в 05:34
поделиться

Этот класс должен выполнять то, что вы хотите сделать:

public class SilverlightishPopup
{
    private Rectangle maskRectangle = new Rectangle { Fill = new SolidColorBrush(Colors.DarkGray), Opacity = 0.0 };

    public FrameworkElement Parent
    {
        get;
        set;
    }

    public FrameworkElement Content
    {
        get;
        set;
    }

    public SilverlightishPopup()
    {
        Button button = new Button();
        button.Width = 100;
        button.Height = 200;
        button.Content = "I am the popup!";

        button.Click += delegate { Close(); };

        Content = button;
    }

    public void Show()
    {
        Grid grid = GetRootGrid();

        if (grid != null)
        {
            DoubleAnimation opacityAnimation = new DoubleAnimation(0.5, new Duration(TimeSpan.FromSeconds(0.5)));

            Storyboard opacityBoard = new Storyboard();
            opacityBoard.Children.Add(opacityAnimation);

            Storyboard.SetTarget(opacityAnimation, maskRectangle);
            Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath("(Opacity)"));

            opacityBoard.Completed += delegate
            {
                ScaleTransform scaleTransform = new ScaleTransform(0.0, 0.0, Content.Width / 2.0, Content.Height / 2.0);
                Content.RenderTransform = scaleTransform;

                grid.Children.Add(Content);

                Storyboard scaleBoard = new Storyboard();

                DoubleAnimation scaleXAnimation = new DoubleAnimation(1.0, TimeSpan.FromSeconds(0.5));

                scaleBoard.Children.Add(scaleXAnimation);

                Storyboard.SetTarget(scaleXAnimation, Content);
                Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));

                DoubleAnimation scaleYAnimation = new DoubleAnimation(1.0, TimeSpan.FromSeconds(0.5));

                scaleBoard.Children.Add(scaleYAnimation);

                Storyboard.SetTarget(scaleYAnimation, Content);
                Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));

                scaleBoard.Begin();
            };

            opacityBoard.Begin();

            grid.Children.Add(maskRectangle);
        }
    }

    public void Close()
    {
        Grid grid = GetRootGrid();

        if (grid != null)
        {
            ScaleTransform scaleTransform = new ScaleTransform(1.0, 1.0, Content.Width / 2.0, Content.Height / 2.0);
            Content.RenderTransform = scaleTransform;

            Storyboard scaleBoard = new Storyboard();

            DoubleAnimation scaleXAnimation = new DoubleAnimation(0.0, TimeSpan.FromSeconds(0.5));

            scaleBoard.Children.Add(scaleXAnimation);

            Storyboard.SetTarget(scaleXAnimation, Content);
            Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));

            DoubleAnimation scaleYAnimation = new DoubleAnimation(0.0, TimeSpan.FromSeconds(0.5));

            scaleBoard.Children.Add(scaleYAnimation);

            Storyboard.SetTarget(scaleYAnimation, Content);
            Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));

            scaleBoard.Completed += delegate
            {
                DoubleAnimation opacityAnimation = new DoubleAnimation(0.5, 0.0, new Duration(TimeSpan.FromSeconds(0.5)));

                Storyboard opacityBoard = new Storyboard();
                opacityBoard.Children.Add(opacityAnimation);

                Storyboard.SetTarget(opacityAnimation, maskRectangle);
                Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath("(Opacity)"));

                opacityBoard.Completed += delegate
                {
                    grid.Children.Remove(maskRectangle);
                    grid.Children.Remove(Content);
                };

                opacityBoard.Begin();
            };

            scaleBoard.Begin();
        }
    }

    private Grid GetRootGrid()
    {
        FrameworkElement root = Parent;

        while (root is FrameworkElement && root.Parent != null)
        {
            FrameworkElement rootElement = root as FrameworkElement;

            if (rootElement.Parent is FrameworkElement)
            {
                root = rootElement.Parent as FrameworkElement;
            }
        }

        ContentControl contentControl = root as ContentControl;

        return contentControl.Content as Grid;
    }
}

Просто установите свойство Parent для любого элемента Framework в родительском окне (оно найдет Окно, чтобы заблокировать его с помощью маски), и установите содержимое на все, что вы хотите, чтобы было показано (и вызовите метод Show, когда вы хотите, чтобы он был показан, конечно). Вы должны будете придумать всплывающую обертку (то есть что-то с границей и кнопкой закрытия, которая вызывает метод закрытия) самостоятельно, но это не должно быть сложно, и, очевидно, удалить кнопку-заполнитель в конструкторе (он просто там, чтобы показать, как он будет выглядеть).

Единственная проблема, связанная с этим, заключается в том, что она будет работать только с окнами с содержимым (т. е. с именем LayoutRoot в Silverlight) - это сетка (по умолчанию при создании нового WPF/Silverlight Window/Page). Я настроил его на работу для всех панелей, но это выглядит странно при использовании со StackPanel или DockPanel (как и ожидалось). Если это не сработает для тебя, дай мне знать, и мы что-нибудь выясним.

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

Дайте мне знать, если у вас есть какие-либо вопросы/проблемы, и я надеюсь, что это решит вашу проблему.

-121--2980844-

Создайте экземпляр вашего класса: m = MyClass ()

, затем используйте m.f () , чтобы вызвать функцию

Теперь вы можете задаться вопросом, почему не нужно передавать параметр функции (парам «я»). Это происходит потому, что экземпляр, в котором вызывается функция, фактически передается в качестве первого параметра.

То есть MyClass.f (m) равно m.f () , где m является объектом экземпляра класса MyClass .

Удачи!

-121--4144518-

Чтобы ответить на ваш первый вопрос, давайте заполним некоторые значения, чтобы увидеть, что происходит:

void* a = 0x00001000; // some memory address
void* b = 0x00002000; // another memory address
/* Now we'll put in your code */
void* temp; // temp is garbage
temp = a; // temp is now 0x00001000
a = b; // a is now 0x00002000
b = temp; // b is now 0x00001000

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

Для ответа на второй вопрос нельзя отменить ссылку void * . Причина этого в том, что void не имеет размера, поэтому пытаться отменить привязку или присвоить что-либо, не имеющее размера, бессмысленно. Таким образом, void * - это способ гарантировать, что вы можете указать на что-то , но вы никогда не узнаете, что это что-то без дополнительной информации (следовательно, параметр size для вашей процедуры).

Оттуда, зная указатель и размер данных, на которые указывает указатель,можно использовать подпрограмму memcpy для перемещения данных, на которые указывает один указатель, в местоположение, на которое указывает другой указатель.

3
ответ дан 5 December 2019 в 05:34
поделиться

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

void swap(void *a, void *b, size_t size);

Означает, что два адреса копируются в новые переменные, называемые a и b . Поэтому, если вы измените то, что хранится в a и b , никакие ваши действия не будут иметь никакого эффекта после возврата swap .

2
ответ дан 5 December 2019 в 05:34
поделиться

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

struct {
  int a;
  int b;
} a, b;

swap(&a, &b, sizeof(a));

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

1
ответ дан 5 December 2019 в 05:34
поделиться

Чтобы изменить указатель внутри и оставить его снаружи, вы должны передать указатель по ссылке или двойным указатель.

Если ваша функция должна быть такой:

void swap(void *a, void *b, size_t size);

Я полагаю, вам нужно реализовать что-то вроде:

void * temp;
temp = malloc(size);
memcpy(temp,a,size);
memcpy(a,b,size);
memcpy(b,temp,size);
free(temp);
0
ответ дан 5 December 2019 в 05:34
поделиться

Чтобы получить какой-либо реальный эффект, вам нужно выполнить эквивалент второго блока, который вы упомянули:

void *temp;
*temp = *a;
*a = *b;
*b = *temp;

Проблема здесь в том, что 'void' не имеет размера, поэтому вы не можете назначить 'void'ы как стоять. Вам нужно выделить место для temp , на которое будет указывать, а затем скопируйте значения, используя что-то вроде memcpy () .

0
ответ дан 5 December 2019 в 05:34
поделиться

Вы близки.

Проблема в том, что вы «меняете местами» только указатели a и b , которые являются локальными переменными в функции.

Я предполагаю, что вне функции у вас есть некоторые переменные, давайте назовем их:

void *x = ...;
void *y = ...;

Когда вы вызываете:

swap(x, y, some_size);

a и b указывает на те же объекты, что и x и y соответственно. Теперь, когда вы меняете местами то, что указывает a и b , x и y все еще указывают на то, на что они указывали раньше.

Чтобы изменить количество точек памяти x и y , вам необходимо передать указатель на переменную x , то есть указатель на указатель :)

Поскольку вы не можете изменить объявление функции, вы можете поменять местами только содержимое памяти, где x a ) и y b ) указывает на. Некоторые решения находятся в других ответах :) Обычно memcpy - это то, что вы хотите.

1
ответ дан 5 December 2019 в 05:34
поделиться

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

Проще всего это сделать с помощью memcpy - выделите буфер на стеке, memcpy соответствующий размер из a в него, memcpy из b в a, и последний memcpy из temp в b.

1
ответ дан 5 December 2019 в 05:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: