什么是令人信服的例子,指针算法比数组下标更可取?

时间:2009-03-28 17:19:38

标签: c pointer-arithmetic

我正在为介绍性C类准备一些幻灯片,并且我试图提供使用指针算法而不是数组下标的好例子(和动机)。

我在书中看到的很多例子都相当。例如,许多书籍都展示了如何反转字符串中所有值的大小写,但除了用* p替换a [i]之外,代码是相同的。

我正在寻找一个使用单维数组的好(和简短)示例,其中指针算法可以产生明显更优雅的代码。有什么想法吗?

9 个答案:

答案 0 :(得分:14)

再次获取指针而不是值:

当他们想要再次获得指针时,通常会使用指针算法。要在使用数组索引时获取指针:您是1)计算指针偏移量,然后2)获取该存储器位置的值,然后3)您必须使用&再次获得地址。这是更多的打字和不太干净的语法。

示例1:假设您需要一个指向缓冲区中第512个字节的指针

char buffer[1024]
char *p = buffer + 512;

比以下更清洁:

char buffer[1024];
char *p = &buffer[512];

示例2:更高效的strcat

char buffer[1024];
strcpy(buffer, "hello ");
strcpy(buffer + 6, "world!");

这比以下更清洁:

char buffer[1024];
strcpy(buffer, "hello ");
strcpy(&buffer[6], "world!");

使用指针算术++作为迭代器:

在迭代元素数组中的每个元素时,使用++递增指针并使用 - 递减。它比使用用于跟踪偏移的单独变量更清晰。


指针减法:

您可以使用带指针算法的指针减法。在某些情况下,这可能有助于在您指向的元素之前获取元素。它也可以使用数组下标来完成,但它看起来非常糟糕且令人困惑。特别是对于python程序员,其中给出了负的下标来从列表的末尾索引某些东西。

答案 1 :(得分:3)

char *my_strcpy(const char *s, char *t) {
  char *u = t;
  while (*t++ = *s++);
  return u;
}

你为什么要用索引破坏这样的美? (参见K& R,以及它们如何在此风格的基础上进行构建。) 我之所以使用上述签名是有原因的。停止编辑而不先要求澄清。对于那些认为他们知道的人,请查看目前的签名 - 您错过了一些restrict资格。

结构对齐测试和offsetof宏实现。

答案 2 :(得分:2)

指针算术可能看起来很花哨而且“黑客”,但我从未遇到过比标准索引更快的情况。恰恰相反,我经常遇到一些情况,当它将代码放慢一个很大的因素时。

例如,通过具有指针的数组的典型顺序循环可能不如在支持SSE扩展的现代处理器上使用经典索引进行循环。循环中的指针算法足以阻止编译器执行循环矢量化,这可以产生典型的2x-4x性能提升。此外,使用指针而不是简单的整数变量可能会由于指针别名而导致不必要的内存存储操作。

因此,通常不建议使用指针算术而不是标准索引访问。

答案 3 :(得分:1)

迭代二维数组,其中基准的位置实际上并不重要 如果你不使用指针,你必须跟踪两个下标
使用指针,您可以指向数组的顶部,并使用单个循环,拉链整个

答案 4 :(得分:1)

如果您使用的是旧编译器或某种专业嵌入式系统编译器,可能会有轻微的性能差异,但大多数现代编译器可能会优化这些(微小的)差异。

以下文章可能是您可以借鉴的内容 - 取决于学生的水平:

http://geeks.netindonesia.net/blogs/risman/archive/2007/06/25/Pointer-Arithmetic-and-Array-Indexing.aspx

答案 5 :(得分:1)

你特别询问C,但C ++也是基于此:

大多数指针算法自然地推广到Forward Iterator概念。由于运算符重载,使用*p++遍历内存可用于任何有序容器(链表,跳过列表,向量,二叉树,B树等)。

答案 6 :(得分:1)

有趣的是我希望你永远不必处理:指针可以别名,而数组则不能。别名可能导致各种非理想的代码生成,其中最常见的是使用指针作为另一个函数的out参数。基本上,编译器不能假设函数使用的指针不会对自身或该堆栈帧中的任何其他内容进行别名,因此每次使用时都必须从指针重新加载值。或者更确切地说,为了安全起见。

答案 7 :(得分:0)

通常选择只是风格之一 - 对于特定情况,一种看起来或感觉比另一种看起来更自然。

还有一个论点,即使用索引会导致编译器不得不重复重新计算循环内的偏移量 - 我不确定这种情况经常发生的情况(除非在非优化版本中),但我想它发生了,但它可能很少成为问题。

从长远来看,我认为一个重要的领域(可能不适用于介绍性的C类 - 但我早期学习它们)是使用指针算法适用于C ++ STL中使用的习语。如果让他们理解指针算法并使用它,那么当他们继续使用STL时,他们将对如何正确使用迭代器有所了解。

答案 8 :(得分:0)

#include ctype.h
void skip_spaces( const char **ppsz )
{
  const char *psz = *ppsz;
  while( isspace(*psz) )
    psz++;
  *ppsz = psz;
}

void fn(void)
{
  char a[]="  Hello World!";
  const char *psz = a;
  skip_spaces( &psz );
  printf("\n%s", psz);
}