使用指针交换对象

11

我正在尝试交换对象,这是一个使用void指针交换对象的作业问题。我的函数声明必须为:

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???

谢谢!


1
你已经接近成功了。但是,解引用 void * 是不允许的。考虑其他方法来移动信息,特别是考虑 size 参数以及它对算法的影响。 - Carl Norum
改变指针是一个不错的尝试,但是不会起作用,因为它们是按值传递的。因此,在调用之前,调用者将具有相同的指针值。 - Thomas Matthews
10个回答

21

交换指针并不会改变所指向的值。如果这样做了,就像交换信封上的地址标签,把我搬进你的房子,你搬进我的房子。

你已经接近成功:

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,并且不使用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);
  }
}

1
正确的想法,但应该是memcpy(dest, src, size); - Andrew Johnson
@Earwicker:如果我要使用不在C89中的东西,我更愿意使用C99而不是alloca。 :) - Roger Pate
当我们谈论C99时:您的swap()是一个函数,其中将restrict添加到指针参数中是合适的;选择2的幂作为阈值也可能是一个好主意。 - Christoph
Christoph:两个观点都很好,我感觉在处理VLA与malloc的问题时已经足够复杂了,甚至不考虑其他问题。 :) 我试图暗示阈值可能会改变,这也是它在一个地方定义的主要原因之一。 - Roger Pate
memcpy 函数区分堆指针和栈指针吗?如果是栈指针,那么 memcpy 在复制时应该减小地址。对于堆指针,应该增加地址(如果考虑 naive 的 for 循环实现方式)。是这样的吗? - Ayrat

3
回答你的第一个问题,让我们填入一些值来看看发生了什么:
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 的例程将一个指针指向的数据移动到另一个指针所指向的位置。

2

参数类似于本地变量,在函数开始执行之前将值复制到它们中。 这个原型:

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

这意味着两个地址被复制到新变量中,分别称为ab。所以如果你改变了ab中存储的内容,在swap返回后不会有任何影响。


2

我在C课程中有一个类似的问题。我认为memcopy可能是最好的选择,但你也可以尝试这个:

    typedef unsigned char * ucp;

    void swap(void *a, void *b, int size){
      ucp c=(ucp)a;
      ucp d=(ucp)b;
      for(int i=0; i<size; i++){
        int temp=(int)c[i];
    c[i]=(int)d[i];
    d[i]=temp;
      }

    }

基本上,这样做的作用是将两个指针都转换为无符号字符指针类型。然后你增加指针,在无符号字符的情况下,每次增加一个字节。然后你所做的就是逐字节地复制内存中的内容。如果有人想纠正或澄清这一点,我也会感激不尽。

1
如果您正在编写一个函数来交换两个整数,给定它们的指针,那么您所提供的交换指向值的解决方案是可行的。然而,请考虑以下情况:
struct {
  int a;
  int b;
} a, b;

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

你需要想出一种方法,在不知道每个值实际包含什么的情况下交换它们的内容。


1

你很接近了。

问题在于:你只交换了函数内的本地变量指针ab

我猜想在函数外面你有一些变量,我们称之为:

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

当你调用时:

swap(x, y, some_size);

ab 分别指向与 xy 相同的对象。现在,当您交换 ab 指向的内容时,xy 仍然指向它们之前指向的内容。

要更改 xy 指向的内存,您需要传递一个指向 x 变量的指针,因此是指针的指针 :)

由于无法更改函数声明,因此只能交换 x(和 a)和 y(和 b)指向的内存内容。其他答案中有一些解决方案 :) 通常情况下,您需要使用 memcpy


1

首先,注意函数内部指针的任何更改都不会传播到函数外部。因此,您需要移动内存。

最简单的方法是使用memcpy - 在堆栈上分配缓冲区,从amemcpy适当大小到它,从b memcpya,最后一个memcpy从temp到b


0
我们在交换两个指针时不需要使用memcpy,以下代码可以很好地工作(已测试交换int*和char*字符串)。
void swap(void **p, void **q)
{
    void *t = *p;
    *p = *q;
    *q = t;
}

0

如果你想在函数内改变指针的值,并且保证函数外部值也发生了变化,你需要使用传递指针引用或双重指针的方法。

如果你的函数必须要像下面这样:

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
为了产生任何实际效果,您需要执行您提到的第二个代码块的等效操作:
void *temp;
*temp = *a;
*a = *b;
*b = *temp;

这里的问题在于'void'没有大小,因此您不能直接分配'void'。您需要为temp分配空间,然后使用类似memcpy()的函数来复制值。

@pm100 - 他实际上在答案中解释了那个。 - Daniel Earwicker
1
@pm100:与编译器不同的是,你需要同时关注代码和注释! - Jerry Coffin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接