Неважно, насколько быстро работает ваш код, если его выбросят, потому что никто другой не может его понять и поддерживать; Кроме того, хитрый код иногда может помешать вашему компилятору улучшить оптимизацию.
Например:
int main(void)
{
int arr[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int *p = *arr; // == arr[0] == &arr[0][0]
int x;
x = arr[2][3]; // Straightforward array access
x = *(*(arr+2)+3); // Ugly pointer arithmetic
x = *(ptr + 11); // Slightly less ugly pointer arithmetic
return 0;
}
Я запустил вышеупомянутое через gcc -c -g -Wa,-a,-ad > foo.lst
, чтобы сгенерировать сгенерированную сборку и исходный код.
Вот перевод x = arr[2][3];
:
movl -16(%ebp), %eax
movl %eax, -8(%ebp)
Вот перевод x = *(*(arr+2)+3);
:
leal -60(%ebp), %eax
addl $44, %eax
movl (%eax), %eax
movl %eax, -8(%ebp)
И, наконец, перевод x = *(ptr + 11);
:
movl -12(%ebp), %eax
addl $44, %eax
movl (%eax), %eax
movl %eax, -8(%ebp)
Не пытайтесь перехитрить ваш компилятор . Это уже не 1970-е годы. gcc знает, как эффективно осуществлять доступ к массивам, не сообщая об этом.
Вы даже не должны думать о производительности на этом уровне , если вы не настроили свой алгоритм и структуры данных, не использовали самые высокие параметры оптимизации на своем компиляторе (FWIW, -O1
генерирует один и тот же код для всех трех версий), и вы все еще не можете выполнить жесткие требования к производительности (в этом случае правильным решением обычно является покупка более быстрого оборудования). И вы не должны изменять что-либо без предварительного запуска кода через профилировщик, чтобы найти реальные узкие места. Измерьте , не угадайте.
EDIT
Естественно, когда литералы 2
и 3
заменяются переменными, ситуация меняется. В этом случае *(ptr + offset);
выглядит лучше всего. Но не очень. И я все еще буду утверждать, что ясность важнее на этом уровне.