【问题标题】:void swap(int *a, int *b) not working with arrayvoid swap(int *a, int *b) 不适用于数组
【发布时间】:2014-10-22 18:49:12
【问题描述】:

C 编程新手,尝试创建一个交换两个变量值的 void 函数。当我希望交换两个整数变量的值时,以下函数可以正常工作,例如 a = 11, b = 33 和函数调用 swap(&a, &b):

void swap(int *a, int *b) {
  *a += *b;
  *b = *a - *b;
  *a -= *b; 
}

但是,当我尝试对数组的两个元素执行此操作时,例如 swap(&a[0], &a[2]) 无法正常工作。但是,它确实适用于以下功能:

void swap(int i, int j, int a[]) {
  int h = a[i];
  a[i] = a[j];
  a[j] = h;
}

有谁知道为什么单变量的第一个工作,而不是数组元素?当然,我在这里缺少一个很好的解释。欢迎所有帮助,在此先感谢!

这是完整的程序:

#include <stdio.h>
#include <stdlib.h>


void swap(int *a, int *b) {
  *a += *b;
  *b = *a - *b;
  *a -= *b; 
}

void selectionSort(int a[], int len) {
  int i, j, min;
  for (i = 0; i < len; i++) {
    min = i;
    for (j = i+1; j < len; j++) {
      if (a[j] < a[min]) {
        min = j;
      }
    }
    swap(&a[i], &a[min]);
  }  
}

int main(int argc, char * argv[]) {
  int a[5] = {5, 4, 3, 2, 1};
  int len = 5, i;

  selectionSort(a, len);


  for (i = 0; i < len; i++) {
    printf("%d ", a[i]);
  }
  printf("\n");

    return 0;
}

数组值的输出是 1 2 0 0。

【问题讨论】:

  • “它不能正常工作”是对问题的糟糕描述。
  • 这可能是一个情境问题。如果由于某种原因,您的调用代码对自身执行元素交换( swap(&a[1], &a[1]); ),这将不予检查......我对该方法的初步理解表明它会设置无意中将值设为 0。也许这正在发生?
  • 请注意,有一个更好的(更少的操作,没有由于溢出导致未定义行为的机会)交换两个变量而没有额外空间的方法:*a ^= *b; *b ^= *a; *a ^= *b;a == b 时仍然不起作用。跨度>
  • 我推荐下面@DavidSchwartz 的答案。要自己解决这些问题,建议添加 printf 和/或使用调试器 :-)

标签: c arrays pointers swap


【解决方案1】:

让我猜猜——所有的值都归零了?

如果a == b,您的交换功能将被破坏。试试:

void swap(int *a, int *b) {
  if (a != b)
  {
      *a += *b;
      *b = *a - *b;
      *a -= *b; 
  }
}

【讨论】:

  • 是的,这个有效!太好了,非常感谢。为什么如果两个元素相等,则交换并置为0?
  • @jake1992:当*a == *b(两个元素相等)时,它工作得很好。你的问题是当a == b,即两者都指向同一个元素时。
  • 非常感谢您的解释,学到了关于指针的新知识!
  • 修复 swap 函数是不够的。他正在与自己交换元素这一事实表明排序例程中存在错误。
【解决方案2】:

最好还是写经典的swap函数

void swap( int *a, int *b )
{
   int tmp = *a;
   *a = *b;
   *b = tmp;;
}

或改变功能选择按以下方式排序

   void selectionSort(int a[], int len) {
      int i, j, min;
      for (i = 0; i < len; i++) {
        min = i;
        for (j = i+1; j < len; j++) {
          if (a[j] < a[min]) {
            min = j;
          }
        }
        if ( min != i ) swap(&a[i], &a[min]);
      }  
    }

【讨论】:

    【解决方案3】:

    当两个参数都指向同一个对象时,您的 swap 函数将失败。我发现你是通过在调用swap 之前添加一个printf 调用来做到这一点的,你可以自己尝试一下:

    printf("swap(%d, %d)\n", i, min);
    swap(&a[i], &a[min]);
    

    您在swap 函数中使用的算法:

    *a += *b;
    *b = *a - *b;
    *a -= *b;
    

    似乎是为了避免创建临时变量,但代价是 (a) 当两个对象相同时失败,以及 (b) 如果存在算术溢出则冒着未定义行为的风险。对 2 的补码整数的算术通常会表现出环绕语义,这(我认为)会给你正确的结果,但这是一个不必要的复杂化。如果您正在处理浮点数,它会出现更多问题,其中算术可能会溢出或失去精度。有一个类似的按位 xor hack,它具有大多数相同的问题(并且根本不适用于浮点)。声明一个临时的将解决这两个问题:

    const int old_a = *a;
    *a = *b;
    *b = old_a;
    

    (通常将临时命名为tmp,但我喜欢给它起一个更明确地告诉您它的用途的名称;添加const 告诉读者它不会在您之后更改初始化它。)

    但这不是唯一的问题。您尝试与自己交换数组元素的事实表明您的 selectionSort 函数也有问题;一个正确的排序算法永远不会与它自己交换一个元素,因为这样做是没有用的。

    如果您修复了selectionSort,您可能不需要修复swap,但我还是建议您这样做。

    【讨论】:

      【解决方案4】:

      如果是数组元素,只需使用 a[0] 和 a[2],不要使用“&”运算符,因为数组本身就是指针,因此您无需提供任何参考。

      【讨论】:

      • 数组衰减到一个指针,但[] 包含一个尊重。
      猜你喜欢
      • 2015-03-20
      • 2011-12-24
      • 2014-10-19
      • 1970-01-01
      • 2012-08-01
      • 2010-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多