【问题标题】:Swapping objects using pointers使用指针交换对象
【发布时间】:2011-01-15 00:26:48
【问题描述】:

我正在尝试为使用 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???

谢谢!

【问题讨论】:

  • 你已经接近了。但是,不允许使用 void *。想一想您可以移动信息的其他方式,特别是考虑size 参数以及它对您的算法有什么影响。
  • 更改指针是一个不错的尝试,但由于它们是按值传递的,所以不会起作用。因此,调用者在调用之前将在指针中具有相同的值。

标签: c pointers


【解决方案1】:

我们不需要使用 memcpy 来交换两个指针,以下代码运行良好(已测试交换 int* 和 char* 字符串):

void swap(void **p, void **q)
{
    void *t = *p;
    *p = *q;
    *q = t;
}

【讨论】:

    【解决方案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;
          }
    
        }
    

    基本上,这会将两个指针都转换为无符号字符指针类型。然后增加指针,在无符号字符的情况下,每次增加一个字节。然后你所做的基本上是一次在内存中复制每个字节的内容。如果有人想对此进行更正或澄清,我也将不胜感激。

    【讨论】:

      【解决方案3】:

      交换指针不会改变指向的值。如果是这样,那就像交换信封上的地址标签,让我进入你的房子,你进入我的房子。

      你快到了:

      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 或普通 ol' 数据,以进行比较。)这​​样,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 的 VLA:

      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);
        }
      }
      

      【讨论】:

      • 正确的想法,但它是 memcpy(dest, src, size);
      • @Earwicker:如果我要使用 C89 以外的东西,我宁愿使用 C99 而不是 alloca。 :)
      • 当我们谈论 C99 时:您的 swap() 是一个将 restrict 添加到指针参数的函数是合适的;选择 2 的幂作为阈值可能也是一个好主意
      • Christoph:两者都很好,我觉得在这里处理 VLA 与 malloc 已经足够复杂,甚至可以考虑其他问题。 :) 我试图暗示阈值可能会改变,这就是它在一个地方定义的主要原因之一。
      • memcpy 是否区分堆栈和堆指针?如果它是一个堆栈指针,那么 memcpy 应该在复制时减少地址,如果是堆指针 - 增加(如果考虑 memcpy 的类似 for-loop 的幼稚实现)。对吗?
      【解决方案4】:

      你已经接近了。

      问题是:你只是在“交换”函数中的局部变量指针 ab

      我假设在函数之外你有一些变量,我们称之为:

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

      当你打电话时:

      swap(x, y, some_size);
      

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

      要更改 xy 指向的内存,您必须传递一个指向 x 变量的指针,所以一个指向指针的指针:)

      因为你不能改变函数声明你只能交换 x (and a) 和 y ( b) 指向。一些解决方案在其他答案中:) 通常memcpy 是你想要的。

      【讨论】:

        【解决方案5】:

        要在内部更改指针并将其保留在外部,您必须通过引用或双指针传递指针。

        如果你的函数必须是这样的:

        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);
        

        【讨论】:

          【解决方案6】:

          为了回答你的第一个问题,让我们填写一些值来看看发生了什么:

          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* 是一种保证您可以指向 something 的方式,但如果没有更多信息,您将永远不会知道该 something 是什么(因此 size 参数你的日常)。

          从那里,知道指针和指针指向的数据的大小,您可以使用像memcpy 这样的例程将一个指针指向的数据移动到另一个指针指向的位置。

          【讨论】:

            【解决方案7】:

            如果您正在编写一个函数来交换两个整数,给定指向它们的指针,那么交换指向的值的解决方案将起作用。但是,请考虑以下情况

            struct {
              int a;
              int b;
            } a, b;
            
            swap(&a, &b, sizeof(a));
            

            您需要找出一种方法来交换每个传递的值的内容,而无需知道它们实际包含什么。

            【讨论】:

              【解决方案8】:

              参数就像局部变量,在函数开始执行之前将值复制到其中。这个原型:

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

              表示将两个地址复制到名为ab 的新变量中。因此,如果您更改存储在ab 中的内容,则在swap 返回后,您所做的任何操作都不会产生任何影响。

              【讨论】:

                【解决方案9】:

                要产生任何实际效果,您需要执行您提到的第二个块的等效操作:

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

                这里的问题是 'void' 没有大小,因此您不能按原样分配 'void'。您需要为temp 分配空间以指向,然后使用memcpy() 之类的内容复制值。

                【讨论】:

                • @pm100 - 他实际上在答案中解释了这一点。
                • @pm100:与编译器不同,您需要注意代码 cmets!
                【解决方案10】:

                首先,请注意,对函数内部指针的任何更改都不会传播到函数外部。所以你将不得不移动内存。

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-06-04
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-07-08
                  相关资源
                  最近更新 更多