C-何时使用指针算术,何时使用数组索引



在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 ] );

最新更新