在C中,什么时候可以使用一个而不是另一个?
这实际上是一个风格和编码约定的问题,因为在C中,p[i]
被定义为与*(p+i)
相同,其中p
是指针,i
是积分索引。AFAIK你甚至可以写i[p]
,但那太难看了。
这在C++中并不总是正确的(它提供了定义operator []
等的能力)
我个人不喜欢手动编码&p[i]
,在这种情况下更喜欢p+i
。
这通常取决于情况。我认为没有经验法则。
在某些情况下,数组索引更好。例如,当您分配了一个阵列时
char* ptr = malloc(SIZE);
并且您需要ptr
的值不变,因为您希望稍后释放它,然后可以使用索引。
或者如果你得到一个指针作为函数参数
void func(char* ptr)
并且您需要在数组上运行,然后可以增加指针本身,并且不需要创建新的变量用作索引。
然而,在大多数情况下,这取决于您自己的偏好。
"数组索引"只是包装"指针算术"的语法糖。这纯粹是化妆品,只是另一种符号,意味着使用一种或另一种将是个人偏好的问题。
通常隐藏在你所问问题背后的真正问题是什么时候使用随机访问,什么时候使用顺序访问("指针算术"意味着指针的顺序递增/递减不超过1)。答案是:尽可能使用顺序访问,只有在必要时才使用随机访问。
只要性能不受影响,依靠最小的需求集来实现算法总是一个更好的主意。随机访问是比顺序访问更强的要求,这意味着在合理可能的情况下应避免前者。
就性能而言,最好使用指针算术(至少在禁用编译器优化的情况下),因为在数组上迭代时,不必增加单独的变量。另请参见K&R第97页(第二版)。
否则,这只是一个编码风格的问题。
当编译时数据结构的大小未知时,指针非常有用。例如,当你不知道字符串的长度,或者你期望有多少整数等等。在这种情况下,指针可以根据需求动态分配内存。
另一方面,阵列降低了灵活性。
然而,除此之外还有更多的区别。
根据MISRA C++2008(规则5-0-15):数组索引应是指针算术的唯一形式。
但这个规则有一个例外:
递增/递减运算符可以在由指向数组的指针实现的迭代器上使用
template < typename IterType >
uint8_t sum_values ( IterType iter, IterType end )
{
uint8_t result = 0;
while ( iter != end )
{
result += *iter;
++iter; // Compliant by exception
}
return result;
}
void my_fn ( uint8_t * p1, uint8_t p2[ ] )
{
uint8_t index = 0;
uint8_t * p3;
uint8_t * p4;
*p1 = 0;
++index;
index = index + 5;
p1 = p1 + 5; // Non-compliant – pointer increment
p1[ 5 ] = 0; // Non-compliant – p1 was not declared as array
p3 = &p1[ 5 ]; // Non-compliant – p1 was not declared as array
p2[ 0 ] = 0;
p2[ index ] = 0; // Compliant
p4 = &p2[ 5 ]; // Compliant
}
uint8_t a1[ 16 ];
uint8_t a2[ 16 ];
my_fn ( a1, a2 );
my_fn ( &a1[ 4 ], &a2[ 4 ] );
uint8_t a[ 10 ];
uint8_t * p;
p = a;
*( p + 5 ) = 0; // Non-compliant
p[ 5 ] = 0; // Compliant
sum_values ( &a1[ 0 ], &a1[ 16 ] );