10

我正在为介绍性 C 课程准备一些幻灯片,并且我正在尝试展示在数组下标上使用指针算术的好例子(和动机)。

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

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

4

9 回答 9

16

再次获取指针而不是值:

当他们想要再次获得指针时,通常会使用指针算法。要在使用数组索引时获取指针:您是 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 程序员,其中给出了一个负下标来索引列表末尾的某些内容。

于 2009-03-28T17:23:20.387 回答
3
char *my_strcpy(const char *s, char *t) {
  char *u = t;
  while (*t++ = *s++);
  return u;
}

为什么要用索引来破坏这样的美丽?(参见 K&R,以及他们如何建立这种风格。)我使用上面的签名是有原因的。不先要求澄清就停止编辑。对于那些认为他们知道的人,请查看现在的签名——你错过了一些restrict资格。

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

于 2009-03-28T17:23:30.637 回答
2

指针算术可能看起来很花哨和“hackerish”,但我从未遇到过它比标准索引更快的情况。恰恰相反,我经常遇到这样的情况,它会大大降低代码速度。

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

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

于 2009-10-15T15:44:38.243 回答
1

遍历二维数组,如果不使用指针,数据的位置并不重要
,您必须使用指针跟踪两个下标
,您可以指向数组的顶部,并使用单个循环,通过整个事情拉链

于 2009-03-28T17:26:08.213 回答
1

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

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

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

于 2009-03-28T17:33:59.630 回答
1

您是在专门询问 C,但 C++ 也以此为基础:

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

于 2009-03-28T18:39:59.113 回答
1

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

于 2009-03-28T18:44:02.440 回答
0

通常选择只是一种风格——在特定情况下,一种看起来或感觉比另一种更自然。

还有一个论点是,使用索引会导致编译器不得不在循环中重复重新计算偏移量——我不确定这种情况的频率(除了非优化的构建),但我想它会发生,但是这可能很少有问题。

我认为从长远来看很重要的一个领域(可能不适用于介绍性 C 类 - 但我说要尽早学习)是使用指针算术适用于 C++ STL 中使用的习语。如果你让他们理解并使用指针算法,那么当他们转向 STL 时,他们就会在如何正确使用迭代器方面占得先机。

于 2009-03-28T18:22:56.610 回答
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);
}
于 2011-08-18T10:21:07.020 回答