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

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

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

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

再次获取指针而不是值:

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

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

你为什么要用索引破坏这样的美? (参见K&R,以及它们如何构建到这种风格。) 我有理由使用上述签名。 停止编辑而不先要求澄清。 对于那些认为他们知道的人,查看目前的签名 – 你错过了一些restrict资格。

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

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

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

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

迭代通过二维数组,其中基准的位置并不重要
如果你不使用指针,你将不得不跟踪两个下标
使用指针,你可以指向数组的顶部,并通过一个循环,拉链整个事物

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

以下文章可能是您可以绘制的内容 – 取决于您的学生的级别:

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

你具体要问C,但C ++也建立在这个基础之上:

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

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

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

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

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

 #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); }