【问题标题】:Swap without worrying about the data type交换而不用担心数据类型
【发布时间】:2018-02-12 12:53:44
【问题描述】:

该实现是否有效且安全地交换两个数组或变量而不用担心它们的数据类型?还是应该使用函数指针?

这种类型的代码专注于使用 void 指针的良好实现来交换,而不用担心数据类型。

#include <stdio.h>
#include <string.h>

void swap(void *, void *, int);

int main(void) {

  char a[] = "home";
  char b[] = "door";

  printf("%s %s\n", a, b);
  swap(&a, &b, sizeof(a));
  printf("%s %s \n", a, b);

  return 0;
}

void swap( void *a, void *b, int siz){
  char buff[siz]; // I voluntarily omitted dynamic allocation.
  memcpy(buff,a,siz);
  memcpy(a,b,siz);
  memcpy(b,buff,siz);
}

【问题讨论】:

  • char buff[siz]; 不是有效的(标准)C++,所以从选择一种语言开始
  • 只因为这个问题被标记为[C++],我就提一下std::swap...
  • 这根本不安全。如果sizeof(a) != sizeof(b) 有问题。
  • 为什么我们不能只交换变量的地址?我的意思是这样的东西不起作用:char *c = a, *d = b; a = d; b = c;
  • 这可能是XY Problem。出于什么原因需要交换两个数组的内容?答案可能取决于这个原因。

标签: c pointers


【解决方案1】:

在 C 语言中,这种方法通常是可以的(它被标准函数使用,例如 qsort())。表明您不能使用 this 的禁忌症是当有任何指向您的对象或其成员的指针时。另外,请注意多线程代码。

请注意,在 C++ 中,我们有 std::swap(),它将尊重用户定义的复制/移动构造函数;通过简单地复制其内存来复制 C++ 对象(通常)是无效的。

【讨论】:

    【解决方案2】:

    这个swap() 函数与它使用的memcpy() 一样好/坏。

    • 如果数据结构只是一些数据结构(intfloat 等),它就像一个魅力。

    • 如果你将两个指针传递给不同的结构,所有的地狱都会崩溃。违规代码:

      Foo* myFoo = ...;
      Bar* myBar = ...;
      swap(myFoo, myBar, sizeof(*myFoo));
      

      请注意,您的编译器不会对此抱怨,因为这两种指针类型都可以隐式转换为 swap() 所期望的 void*s。但是编译的结果会是废话。

    • 如果您复制的结构包含指向自身的指针,则该指针将指向swap() 之后的另一个对象。以下struct 将违反此规定:

      typedef struct {
          char* data;
          size_t length, allocatedLength;
          char shortStringBuffer[32];
      } myString;
      

      这背后的想法是,短字符串将存储在shortStringBuffer 中,而data 将指向shortStringBuffer。超过 31 个字符的字符串将存储在动态分配的内存中,并且可以通过 data 成员再次访问。留给读者作为练习来弄清楚,当您尝试使用 memcpy() 复制此内容时会发生什么。

    你必须明白的是,memcpy() 真的只是复制字节,而有些数据对于它的存储位置并不是不变的。因此,memcpy() 的每次使用都必须附有证明,证明它在这种特殊情况下做了正确的事情。嗯,它应该。由于某种原因,我从未在评论中看到过这样的证明......

    【讨论】:

      【解决方案3】:

      您可以只交换它们的地址而无需进一步操作来交换两个变量/数组。我试过了,它有效:

      #include <stdio.h>
      
      int main(void) {
          char *a = "home";
          char *b = "root";
          char *c = a, *d = b;
          printf("%s %s\n", a, b);
          a = d;
          b = c;
          printf("%s %s \n", a, b);
          return 0;
      }
      

      输出:

      home root
      root home 
      

      https://ideone.com/MMCOpf

      【讨论】:

      • 是的,在任何代码中使用交换的变量而不交换它们的依赖项(例如已经分配给变量或其任何部分的指针等)是绝对不安全的。但是这样,您可以确定它完全各个方面都换了。
      • 这在后续代码通过指针访问对象时有效(并且通常是最好的)。不过,有时我们需要按副本移动。
      【解决方案4】:

      使用通用指针进行交换是安全的,但是您必须确保大小正确,并且不会溢出任何数组或对象:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      int swap(void *a, void *b,
          size_t a_size, size_t b_size);
      
      int main(void) {
      
          char a[] = "home";
          char b[] = "door";
      
          printf("%s %s\n", a, b);
      
          int ret = swap(a, b, sizeof(a), sizeof(b));
          if(ret) {
              printf("%s %s \n", a, b);
          }
          return ret;
      }
      
      int swap(void *a, void *b,
          size_t a_size, size_t b_size)
      {
          if (b_size != a_size ) {
              return 0;
          }
          void *tmp = malloc(a_size);
          if(!tmp) {
              return 0;
          }
          memcpy(tmp, a, a_size);
          memcpy(a, b, b_size);
          memcpy(b, tmp, a_size);
          free(tmp); // tmp no longer needed.
          return 1;
      }
      

      【讨论】:

        【解决方案5】:

        我用C解决了这个问题,代码供大家参考。

        当两个大的uint8_t变量相加时,需要使用uint16_t来保持进位。

        int main(){
            uint8_t tmp[9] = {0x0};
            for (int i=9-1; i>=0 ; i--) *(tmp+i) = 0xff-i;
            uint16_t tmp2[9];
            for (int i=0; i<9; i++) tmp2[i] = tmp[i];
            byte_swap_uint8_data(tmp2, 9);
        }
        
        void byte_swap_uint8_data(uint16_t* data, int w) {
            if (w < 2) return;
            for (int i=0; i<w/2; i++) {
                data[i] += data[w-1-i];
                data[w-1-i] = data[i] - data[w-1-i];
                data[i] = data[i] - data[w-1-i];
             }
        }
        

        【讨论】:

        • 这与问题有什么关系?它不交换 2 个变量,而是反转一个数组。由于某种原因,您似乎要求 uint16_t 数组仅保存 uint8_t 数据。仅使用临时变量比像您那样添加和减去东西更有效。
        • 感谢您的建议,我分享了我解决此问题的经验和代码,很抱歉对您没有帮助。
        • 不好意思提一下,但是代码并没有解决这个问题。它解决了一个完全不同的问题。
        猜你喜欢
        • 2021-06-24
        • 1970-01-01
        • 1970-01-01
        • 2011-03-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-09
        相关资源
        最近更新 更多