何时使用指针进行数组与使用访问运算符有什么好的一般规则?

时间:2015-03-06 14:19:52

标签: c arrays performance pointers optimization

访问运算符示例:

void print_elements ( int * arr, int n )
{
    for (int k = 0; k < n; ++k) printf("%d\n", arr[k]);
}

指针算术示例:

void print_elements ( int * arr, int n )
{
    for (int * pa(arr), * pb(arr+n); pa != pb; ++pa) printf("%d\n", *pa);
}

第二个涉及循环的2个变量papb,而第一个只涉及1个额外变量k。然后,在第二个你正在做

increment pa by 1, dereference

而在你正在做的第一个

increment k by 1, add k to the pointer arr, derefence

以便第一个使用较少的操作来迭代循环。

所有事情都考虑了,哪个更快?对于整数类型,比较器<的速度是否比!=快?使用<似乎更安全&#34;因为像pa != pb这样的条件会因为pa如果重建循环内的算法而pb跳过1,所以总是会感到非理性的恐惧。将指针递增1通常比递增大于{{1}}的指针要快吗?我想考虑 一切

3 个答案:

答案 0 :(得分:3)

这将属于&#34;过早优化&#34;的范围。除非你有一个特定的(测量的)原因,否则你通常应该首先选择最简单和最直接的代码(在这种情况下你的第一个函数)。有可能现在任何编译器都会优化两个函数,如果不完全相同的话,大致相同。

如果您怀疑可以进行一些优化,那么您应该通过基准测试/分析来衡量事物第一,尽管即使在这样的简单函数中,您也必须小心,因为您很容易得到错误的结果。 / p>

如果我们用以下内容替换您的功能:

volatile int Output = 0;

void print_elements1(int * arr, int n)
{
    for (int k = 0; k < n; ++k) Output += arr[k];
}

void print_elements2(int * arr, int n)
{
    for (int * pa(arr), *pb(arr + n); pa != pb; ++pa) Output += *pa;
}

原因是printf()是&#34;慢&#34;如果我们对原始功能进行基准测试,我们真的只会测试它的速度。测试这些函数的数组大小为1亿,以获得合适/可重复的时序:

  • print_elements1()= 560 ms
  • print_elements2()= 230 ms

啊,我们看到指针访问速度是数组索引的两倍多......但速度不快!让我们反转测试订单,看看我们得到了什么:

  • print_elements2()= 650 ms
  • print_elements1()= 260 ms
嗯......现在指针访问速度慢了两倍!这是怎么回事?我不确切知道,但可能由于CPU /内存缓存。我们可以尝试通过在基准测试之前运行这两个函数来消除这种影响:

  • print_elements1()= 230 ms
  • print_elements2()= 230 ms

相同的时间,至少在我的基准计时器的误差范围内。

故事的寓意是,如今编译器和计算机都是复杂的机器,很可能你的编译器在优化大多数代码方面做得比你好。当您进行优化时,首先通过分析/基准测量来测量事物,以确定要处理的代码的最有效区域(如果有的话)。

答案 1 :(得分:3)

  

何时使用指针进行数组与使用访问运算符有什么好的一般规则?

始终尽可能使用数组索引语法,因为它更具可读性。指针算术语法难以阅读,通常不应用于迭代。

  

第二个涉及2个变量

源代码中使用的变量数量是性能和内存消耗的非常差的度量。无论是否明确声明变量,实际的机器代码都需要在某处存储结果。如果您声明的变量多于机器代码实际需要的变量,编译器很可能会优化它们。

循环需要知道何时结束迭代。它可以通过在循环中的每一圈运行时计算arr+n(不太可能发生,因为它会很慢),或者可以通过将arr+n保存在临时内存位置来实现,然后再启动环。在第一个示例中,您声明没有这样的变量,因此实际的机器代码中可能会有一个未命名的变量用于此目的。使两个例子都相同。

  

这样第一个使用较少的操作来遍历循环

不是,不。 C标准强制arr[i] 100%等同于*(arr + i)。数组语法只是&#34;语法糖&#34;。两种情况极有可能产生相同的机器代码。

(上述等价规则是C允许some weird, ugly crap

的原因
  

所有事情都考虑了,哪个更快?

出于上述原因,它们同样快速。

  

比较器&lt;快于!=对于整数类型?

一般来说应该没有区别。这一切都归结为给定CPU上可用的汇编指令。所有比较方式都很可能同样快,除了与0比较的那些,它们可以在某些CPU上节省几纳秒。

  

我想考虑一切可能。

然后我强烈建议您考虑程序的可读性和维护,而不是手动微优化。前者是今天成为优秀程序员的原因,而不是后者。我们不再是20世纪80年代了。

答案 2 :(得分:1)

正如其他人所指出的那样,性能问题不会导致我们访问数组元素中的一种或另一种符号。

如果有多种方法可以表达同样的事情,请使用更好理解的方式。

再次尝到味道。

我更喜欢索引运算符[]并执行[1],因为我不需要获取数组元素的地址。在后一种情况下,我使用+ - 运算符,即a + 1而不是&a[1]