Что именно хранят указатели? (С++)

Я знаю, что указатели хранят адрес значения, на которое они указывают, но если вы выведете значение указателя прямо на экран, вы получите шестнадцатеричное число. Если число именно то, что хранит указатель, то при произнесении

pA = pB; //both are pointers

вы копируете адрес. Тогда не будет ли больше накладных расходов на использование указателей при работе с очень маленькими элементами, такими как ints и bools?


person Community    schedule 13.06.2009    source источник
comment
+1, потому что тебе 14 :)   -  person Nick Dandoulakis    schedule 13.06.2009
comment
Проверьте размер каждого объекта. Узнайте, что меньше указателя. sizeof(‹объект›)   -  person Martin York    schedule 13.06.2009
comment
да, действительно очень хорошее мышление для подростка!   -  person none    schedule 13.06.2009


Ответы (8)


Указатель — это просто число. Он хранит адрес в оперативной памяти, где находятся данные. Сам указатель довольно мал (вероятно, такого же размера, как int на 32-битных архитектурах, long на 64-битных).

Однако вы правы в том, что int * не сэкономит места при работе с ints. Но дело не в этом (без каламбура). Указатели существуют для того, чтобы вы могли иметь ссылки на вещи, а не просто использовать вещи сами по себе.

person Zifre    schedule 13.06.2009
comment
В C++ существует четкая концепция ссылки. Указатели существуют для того, чтобы иметь возможность управлять динамически выделяемой памятью, а не ссылками. - person David Rodríguez - dribeas; 13.06.2009
comment
@dribeas: я не имел в виду ссылки на C ++, я имел в виду теоретическую концепцию наличия чего-то, что просто говорит вам, где что-то найти, чтобы вы могли поделиться данными. - person Zifre; 13.06.2009
comment
Указатели существуют также для экономии места и времени. Вот почему у нас есть копирование при записи. - person Elliott; 13.12.2019

Адреса памяти.

Это места в памяти, где другие вещи находятся.

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

person dmckee --- ex-moderator kitten    schedule 13.06.2009
comment
Это верно для одной копии, но вы можете копировать более одного bool или int в течение одного тактового цикла (8x bool в массиве — это то же самое, что копирование одного указателя). Кроме того, хранение указателей в классах излишне увеличивает их размер и снижает скорость выделения. - person Elliott; 13.12.2019

Как уже говорили другие, указатель хранит адрес памяти, который является «просто числом», но это абстракция. В зависимости от архитектуры процессора это может быть более одного числа, например, база и смещение, которые необходимо добавить для разыменования указателя. В этом случае накладные расходы немного выше, чем если бы адрес представлял собой одно число.

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

Я имел в виду накладные расходы времени. Не уверен, что OP больше беспокоился о пространстве или времени.

person Steve Fallows    schedule 13.06.2009

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

person Niki Yoshiuchi    schedule 13.06.2009
comment
На современных машинах int обычно 32-битные, а ссылки на переменные обычно 64-битные. Базовые типы данных, такие как char, всегда были меньше, чем указатели в C/C++. Таким образом, наблюдение OP было правильным: не пытайтесь использовать указатели/ссылки, чтобы попытаться сэкономить время или память, если тип данных меньше, чем ваши указатели. Это распространенная ошибка новичков. - person Elliott; 13.12.2019

В некоторых архитектурах есть дополнительные накладные расходы указателей на символы, поскольку архитектура поддерживает только слова адресации (32- или 64-битные значения). Таким образом, указатель на символ сохраняется как адрес слова и смещение символа в этом слове. Разыменование указателя включает в себя извлечение слова, а затем сдвиг и маскирование его значения для извлечения символа.

person Nat    schedule 13.06.2009

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

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

Для лучшего понимания предположим, что мы хотим, чтобы информация из ячеек памяти находилась в каком-то месте относительно текущей переменной. Можем ли мы использовать идентификатор для извлечения информации из соседних ячеек? Нет. Потому что идентификатор (имя переменной) даст только значение, содержащееся в этой конкретной ячейке.

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

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

Синтаксис: чтобы сохранить адрес переменной, мы можем просто использовать оператор & (адрес-оф).

foo = &bar 

Здесь foo хранит адрес переменной bar.

А что, если мы хотим узнать значение, присутствующее по этому адресу?

Для этого мы можем просто использовать оператор * (разыменование).

value = *foo

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

person abhinav1602    schedule 16.11.2017

Адрес в памяти. Куда-то указывает! :-)

person mduvall    schedule 13.06.2009
comment
Вы не ответили на вопрос ОП, а ОП сказал в вопросе, что он уже знал это. - person Elliott; 13.12.2019

Да, вы правы, как по скорости, так и по памяти.

Указатели почти всегда занимают больше байтов, чем ваши стандартные int и, особенно, bool и char типы данных. На современных машинах указатели обычно имеют размер 8 байт, тогда как char почти всегда занимает всего 1 байт.

В этом примере для доступа к char и bool из Foo требуется больше машинных инструкций, чем для доступа из Bar:

struct Foo
{
    char * c; // single character
    bool * b; // single bool
};

struct Bar
{
    char c;
    bool b;
};

... И если мы решим сделать какие-то массивы, то размер массивов Foo будет в 8 раз больше - и код более разбросан, так что это означает, что у вас будет намного больше промахов кеша.

#include <vector>

int main()
{
    int size = 1000000;
    std::vector<Foo> foo(size);
    std::vector<Bar> bar(size);

    return 0;
}

Как указал dmckee, одна копия однобайтового bool и одна копия указателя работают так же быстро:

bool num1, num2,* p1, * p2;
num1 = num2; // this takes one clock cycle
p1 = p2; // this takes another

Как сказал dmckee, это верно, когда вы используете 64-битную архитектуру.

Однако копирование массивов ints, bools и chars может быть намного быстрее, потому что мы можем втиснуть их кратно в каждый регистр:

#include <iostream>

int main ()
{
    const int n_elements = 100000 * sizeof(int64_t);

    bool A[n_elements];
    bool B[n_elements];

    int64_t * A_fast = (int64_t *) A;
    int64_t * B_fast = (int64_t *) B;

    const int n_quick_elements = n_elements / sizeof(int64_t);

    for (int i = 0; i < 10000; ++i)
        for (int j = 0; j < n_quick_elements; ++j)
            A_fast[j] = B_fast[j];

    return 0;
}

Контейнеры STL и другие хорошие библиотеки делают это за нас, используя type_traits (is_trivially_copyable) и std::memcopy. Использование указателей под ложным предлогом того, что они всегда такие же быстрые, может помешать оптимизации этих библиотек.

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

person Elliott    schedule 13.12.2019