Подтвердить что ты не робот

C - когда использовать арифметику указателя, когда использовать индексирование массива?

В C, когда предпочтительнее использовать один над другим?

4b9b3361

Ответ 1

Обычно это зависит от ситуации. Я не думаю, что существует эмпирическое правило.

В некоторых случаях показатели массива лучше. Например, когда вы выделили массив

char* ptr = malloc(SIZE);

и вам нужно значение ptr, чтобы не меняться, потому что вы хотите освободить его позже, тогда вы можете работать с индексами.

Или, если вы получаете указатель как аргумент функции

void func(char* ptr)

и вам нужно запустить массив, тогда вы можете увеличить сам указатель, и вам не нужно будет создавать новую переменную для использования в качестве индекса.

Однако в большинстве случаев это зависит от ваших собственных предпочтений.

Ответ 2

Это действительно вопрос стиля и условных обозначений кодирования, поскольку в C p[i] определяется как то же, что *(p+i), где p - указатель, а i - интегральный индекс. AFAIK вы даже можете написать i[p], но это уродливо.

Это не всегда верно в С++ (что дает вам возможность определять operator [] и т.д.).

Мне лично не нравится ручное кодирование &p[i] и предпочитает p+i в этом случае.

Ответ 3

"Индексирование массива" - это просто синтаксический сахар, который обертывает "арифметику указателя". Это чисто косметическое, просто другое обозначение, означающее, что использование того или другого будет вопросом личного предпочтения.

Реальный вопрос, который обычно скрывается за тем, что вы просили, когда использовать произвольный доступ и когда использовать последовательный доступ (с "арифметикой указателя", подразумевающей последовательное приращение/уменьшение указателя не более чем на 1). Ответ: предпочитайте использовать последовательный доступ, когда можете, используйте только произвольный доступ, когда вам нужно.

Пока производительность не страдает, всегда лучше использовать алгоритмы, полагаясь на минимальный набор требований. Случайный доступ является более сильным требованием, чем последовательный доступ, что означает, что первое следует избегать, когда это возможно.

Ответ 4

В плане производительности лучше использовать арифметику указателя (по крайней мере, с отключенной оптимизацией компилятора), потому что при итерации по массиву вам не нужно увеличивать отдельную переменную. См. Также K & R стр. 97 (второе издание).

В противном случае это просто вопрос стиля кодирования.

Ответ 5

Указатели полезны, когда размер структуры данных неизвестен во время компиляции. например когда вы не знаете, что такое длина строки или сколько целых чисел вы ожидаете и так далее. В таких ситуациях указатели могут быть динамически распределенной памятью в зависимости от требований.

Массивы, с другой стороны, уменьшают гибкость.

Тем не менее, существует гораздо больше, чем эта разница.

Ответ 6

в соответствии с MISRA С++ 2008 (Правило 5-0-15): Индексация массива должна быть единственной формой арифметики указателя.

но это правило имеет исключение:

Операторы increment/decment могут использоваться на итераторах, реализуемых указателями на массив

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