【问题标题】:Unexplainable side effects of a function on other unrelated ones in C一个函数对 C 中其他不相关的函数的无法解释的副作用
【发布时间】:2016-10-11 06:07:36
【问题描述】:

下面的程序应该在每个函数中使用不同的指针引用符号将源数组的元素复制到目标数组中:

#include <stdio.h>

void copy_arr(double [], const double [], int);
void copy_ptr(double *, const double *, int);
void copy_ptrs(double *, const double *, double *);
void print_arr(const double *, const double *);

int main(void)
{
  double source[5] = { 0.1, 2.2, 4.3, 6.4, 8.5};
  double target1[5];
  double target2[5];
  double target3[5];

  copy_arr(target1, source, 5);
  copy_ptr(target2, source, 5);

  copy_ptrs(target3, source, source + 5);

  print_arr(target1, target1 + 5);
  print_arr(target2, target2 + 5);
  print_arr(target3, target3 + 5);

  return 0;
}

void copy_arr(double target[], const double source[], int num)
{
  for (int i = 0; i < num; ++i)
    target[i] = source[i];
}

void copy_ptr(double *target, const double *source, int num)
{
  for (int i = 0; i < num; ++i)
    *(target+i) = *(source+i);
}

void copy_ptrs(double *target, const double *source, double *end)
{
  for (; target < end; ++target, ++source)
    *target = *source;
}

void print_arr(const double *start, const  double *end)
{
  while ( start < end)
    printf("%.1lf, ", *start++);
  printf("\n");
}

这会产生如下输出:

0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1、2.2、4.3、6.4、8.5、

或用垃圾代替零。

但是,当我不小心将 copy_ptrs 函数更改为:

void copy_ptrs(double *target, const double *source, double *end)
    {
      for (; *target < *end; ++target, ++source) // notice the asterisks
        *target = *source;
    }

我得到以下输出:

0.1 2.2 4.3 6.4 8.5 0.1 2.2 4.3 6.4 8.5 0.0 0.0 0.0 0.0 0.0

显然,“错误”导致 copy_arrcopy_ptr 正常运行,但是当我从 copy_ptrs 的 for 循环中的变量名中删除星号时,它导致前两个功能故障,而它本身工作。

重新迭代:取消引用targetend 变量会为第三个函数生成正确的输出,但会“破坏”前两个函数;并且不这样做会破坏其各自的功能。我认为不取消引用 startend 指针是正确的方法,因为 print_arr 不会取消引用它们,而是按预期运行(根据我的理解,这是正确的方法)。

此更改如何影响本应在 应该到达之前运行的代码。总的来说,这个程序有什么问题?

我在 Linux 上使用 GCC 4.8.5。

【问题讨论】:

  • 您正在从*target 读取未初始化的数据;关于你得到什么,所有的赌注都没有了。
  • 当目标被print_arr 读取时,它们已被copy_ 函数复制到其中。
  • 问题出在第二个copy_ptrs()*target &lt; *end的使用上。你在给*target赋值之前阅读它,实际上*end也没有确定的值。

标签: c linux function pointers gcc


【解决方案1】:

copy_ptrs()最大的问题是结束条件:

void copy_ptrs(double *target, const double *source, double *end)
{
  for (; target < end; ++target, ++source)
    *target = *source;
}

请注意,这是通过以下方式调用的:

copy_ptrs(target3, source, source + 5);

您应该检查source 是否小于end

void copy_ptrs(double *target, const double *source, double *end)
{
  for (; source < end; ++target, ++source)
    *target = *source;
}

因此,您在原始位置周围复制不确定数量的数据,践踏了数组的边界。很大程度上取决于编译器如何布置数组。看起来您很幸运,target3 的地址低于source,因此在覆盖关键内容并崩溃之前,增加target 经常足以到达end

【讨论】:

  • 另外,你知道我的错误是如何影响之前的功能,即使它应该在之后执行吗?
  • 因为各种各样的数据都是从天知道从哪里复制到其他目标数组的。在修改 source 之前,您可能会在 sourcetarget1target2 之后复制 10 个 double 的数据,然后停止。
  • 如果您想进行实验,请将 source 的大小增加到至少 20 个元素(为值添加一些独特的东西)——而不更改复制函数的调用。然后,使用不正确的copy_ptrs(),注意结果中的那些独特值。你也可以打印source。您可以在设置后立即打印target1,并在设置target2 后再次打印(当然还打印target2),然后在错误(并且已修复!)copy_ptrs() 调用之后再次打印它们。
  • 这证明了这个问题。非常感谢! :)
猜你喜欢
  • 2021-07-30
  • 1970-01-01
  • 2018-12-11
  • 2021-03-31
  • 1970-01-01
  • 2019-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多