Что делает указатели быстрее, чем массивы? - PullRequest
9 голосов
/ 31 августа 2011

Я гуглил и нашел следующий синтаксис для указателей

  void main()
 {
  char a[10]="helloworld";
  char *p=a;
  printf("%c",p[0]);
 }

Я не знал, что указатели могут быть доступны и в виде массива. Я использовал * для операций с указателями Я использовал [0] для массивов и * p для операций с указателями, поэтому я не знал других двух вещей. Теперь из вышесказанного мы можем получить доступ ко второму элементу массива любым из следующих способов

  printf("%C",a[1]);   \\ this is the array
  printf("%c",*(a+1));  \\ this is the array using *
  printf("%c", p[1]);     \\ using the pointer 
  printf("%C",*(p+1));    \\ using the pointer

Теперь мне интересно: какая операция быстрее? Я читал, что операции с использованием указателей выполняются быстрее, и именно поэтому C остается на вершине для быстрого выполнения, и что ни один другой язык не может сравниться с его быстротой.

Теперь главный вопрос: что ускоряет операции с указателями?

1) * (p + 0) * (значение по адресу), которое делает трюк или

2) p [0]

так как мы используем

 *(a+1) or *(p+1) both are same 
  a[1] or p[1] both are same 

когда обычный массив можно использовать как * (a + 1) (который использует * значение по адресу) как указатель. почему мы используем указатели для более быстрой работы? Когда оба имеют одинаковый синтаксис, когда обычный массив и указатель используют * в этих синтаксисах, почему указатели быстрее?

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

Ответы [ 6 ]

10 голосов
/ 31 августа 2011

Я бы не ожидал, что *(ptr + offset) будет быстрее, чем ptr[offset].Фактически, на моем компьютере следующие функции скомпилированы в один и тот же код сборки:

int
ArrayRef(int* array, int index)
{
    return array[index];
}

int
PointerRef(int* array, int index)
{
    return *(array + index);
}

, который (очищен) выглядит так:

ArrayRef:
    pushq   %rbp
    movq    %rsp, %rbp
    movq    %rdi, -8(%rbp)
    movl    %esi, -12(%rbp)
    movl    -12(%rbp), %eax
    cltq
    salq    $2, %rax
    addq    -8(%rbp), %rax
    movl    (%rax), %eax
    leave
    ret

PointerRef:
    pushq   %rbp
    movq    %rsp, %rbp
    movq    %rdi, -8(%rbp)
    movl    %esi, -12(%rbp)
    movl    -12(%rbp), %eax
    cltq
    salq    $2, %rax
    addq    -8(%rbp), %rax
    movl    (%rax), %eax
    leave
    ret

(gcc 4.5.0), x86_64, без оптимизаций).Или с -O3

ArrayRef:
    movslq  %esi, %rsi
    movl    (%rdi,%rsi,4), %eax
    ret

PointerRef:
    movslq  %esi, %rsi
    movl    (%rdi,%rsi,4), %eax
    ret
3 голосов
/ 31 августа 2011

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

Например, если вы напишите свой массив как:Чтобы получить доступ к значению на array[3], компилятор выдаст только простую команду, например (это для x86):

MOV -8(%ebp), %eax

Это потому, что если мы посмотрим на стек, то увидим следующее:

EBP + 4 : Return Address
EBP     : Previous function's stack activation record
EBP - 4 : array[4]
EBP - 8 : array[3]
EBP - 12: array[2]
EBP - 16: array[1]
EBP - 20: array[0]

Таким образом, чтобы получить доступ к значению на array[3], требуется только одна инструкция.Это очень быстро.

1 голос
/ 31 августа 2011

В приведенных вами примерах p[1] не будет быстрее, чем a[1].

0 голосов
/ 31 августа 2011

Массив - это указатель, разницы между p и a после

char a[10]="helloworld";
char *p=a;

обоих a * нет1011 * и p являются указателями на символ, и они указывают на одно и то же место - начало вашего массива в памяти .

использование "operator []" эквивалентно арифметике указателя

a[i] 

будет заменено на

*(a+i)

, это означает, что указатель на начало массивабудет смещено на i * sizeof (char) на место i-го элемента вашего массива.

Реальная разница во времени появляется, когда вы пытаетесь зациклить все элементы, например, скопируйте строку:

char a[10] = "helloworld";
char b[10];
for (int i = 0; i < 10; ++i) b[i] = a[i]; // using array element accessing method

будет производить арифметику, такую ​​как b + i (aka b shift на i * sizeof (char)) и a + i (aka shift на i * sizeof (char)) для каждой итерации цикла, и

char a[10] = "helloworld";
char b[10];
char *_a, *_b;
for (_a = a, _b = b; *_a != '\0'; ++_a, ++_b) *_a = *_b; // using pointers arithmetic method
*b = '\0';

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

0 голосов
/ 31 августа 2011

Указатели, которые работают быстрее, чем массивы, исходят из следующего примера.

Скажем, вы хотите реализовать функцию strcpy, т.е. скопировать одну строку с нулевым символом в конце в другую. Давайте рассмотрим два примера:

Первый:

char* strcpy(char* dest, const char* src)
{
    int i = 0;
    while( src[i] != '\0' ) {
        dest[i] = src[i];
        i++;
    }
    dest[i] = '\0';

    return dest;
}

Второй:

char* strcpy(char* dest, const char* src)
{
    char *save = dest;
    while( *src != '\0' )
    {
        *dest++ = *src++;
    }
    *dest = '\0';

     return save;
}

Второй пример реализован более эффективно, потому что он делает меньше модификаций памяти на каждой итерации и использует указатели вместо массивов. Но есть две вещи:

  1. Это не быстрые указатели, это алгоритм, использующий их для оптимизации.
  2. Оптимизатор может легко выполнить этот вид оптимизации автоматически, так что вы, вероятно, все равно получите тот же сгенерированный код.
0 голосов
/ 31 августа 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...