Зачем нужны указатели?

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

В основном у меня три вопроса:

  1. Зачем использовать указатели вместо обычных переменных?
  2. Когда и где использовать указатели?
  3. Как использовать указатели с массивами?

Для списка книг см. stackoverflow.com/questions/388242/…. После Java я нашел Ускоренный C++ очень полезным.

Jabba 16.01.2010 00:22

Еще один указатель обсуждения здесь.

17 of 26 02.10.2008 19:25

Обсуждалась ранее на этот вопрос Надеюсь, что это поможет!

Doug T. 02.10.2008 19:20

Мы используем указатели, потому что легче дать кому-то адрес своего дома, чем дать всем копию своего дома.

Rishi Dua 04.10.2013 18:51

@RishiDua Это единственное лучшее объяснение указателя, с которым я когда-либо сталкивался. Спасибо за это, это расширило мое понимание :)

Josh 07.11.2013 18:54

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

Gaurav 27.11.2015 09:00

Следуя Риши Дуа, ссылки в комментариях выше похожи на указатели. Представьте, если бы вместо этого они скопировали и вставили сюда целые статьи или обсуждения. У нас бы быстро "не хватало места" на странице

dillon.harless 13.09.2019 17:27
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
388
7
274 326
17

Ответы 17

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

Лучшее объяснение указателей и арифметики указателей, которое я читал, находится в Язык программирования C K&R. Хорошая книга для начинающих изучать C++ - Учебник по C++.

благодарю вас! наконец, практическое объяснение преимуществ использования стека! указатель на позицию в массиве также улучшает производительность доступа к значениям @ и относительно указателя?

user1382306 02.10.2013 08:34

Это, наверное, лучшее объяснение, которое я читал. люди умеют "усложнять" вещи :)

Detilium 21.03.2017 14:50

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

Поскольку они являются адресом объекта, они маленькие: они занимают только место в адресе. Поскольку они маленькие, их отправка в функцию обходится недорого. И затем они позволяют этой функции работать с фактическим элементом, а не с копией.

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

Я считаю ошибочным утверждать, что указатели - это массивы. На самом деле имена массивов являются константными указателями на первый элемент массива. Просто потому, что вы можете получить доступ к произвольной точке, как будто это массив, не означает, что это так ... вы можете получить нарушение доступа :)

rmeador 02.10.2008 19:51

Указатели - это не массивы. Прочтите раздел 6 comp.lang.c FAQ.

Keith Thompson 30.05.2013 22:57

Указатели на самом деле не массивы. Кроме того, необработанные массивы также не очень полезны - определенно не после C++ 11 и std::array.

einpoklum 28.05.2019 23:02

@einpoklum - указатели достаточно близки к массивам, чтобы быть эквивалентными в ссылочных операциях и итерациях по массивам :) .... также - C++ 11 находился через 3 года до выпуска, когда этот ответ был написан в 2008 году

warren 29.05.2019 19:18

@warren: указатели - это не массивы и не близкие к массивам, они просто распадаются на массивы. С педагогической точки зрения неуместно говорить людям, что они похожи. Кроме того, вы, безусловно, можете иметь ссылки на массивы, которые не относятся к тому же типу, что и указатели, и сохраняют информацию о размере; см. здесь

einpoklum 29.05.2019 19:28

@einpoklum - очевидно, что вы придерживаетесь другой точки зрения.

warren 29.05.2019 23:53

Потому что копирование больших объектов повсюду тратит время и память.

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

Mats Fredriksson 02.10.2008 19:24

Можно пройти по ссылке. Это предотвратит копирование.

Martin York 02.10.2008 19:25

@MatsFredriksson - Вместо того, чтобы передавать (копировать) большую структуру данных и снова копировать результат, вы просто указываете, где он находится в ОЗУ, а затем изменяете его напрямую.

John U 25.06.2012 17:00

Так что не копируйте большие объекты. Никто не сказал, что для этого нужны указатели.

einpoklum 28.05.2019 23:05
  1. Указатели позволяют ссылаться на одно и то же пространство в памяти из разных мест. Это означает, что вы можете обновлять память в одном месте, а изменение можно увидеть из другого места в вашей программе. Вы также сэкономите место, имея возможность совместно использовать компоненты в своих структурах данных.
  2. Вы должны использовать указатели в любом месте, где вам нужно получить и передать адрес в определенное место в памяти. Вы также можете использовать указатели для навигации по массивам:
  3. Массив - это блок непрерывной памяти, выделенный для определенного типа. Имя массива содержит значение начальной точки массива. Когда вы добавляете 1, вы попадаете на второе место. Это позволяет вам писать циклы, которые увеличивают указатель, который скользит вниз по массиву, без явного счетчика для использования при доступе к массиву.

Вот пример на C:

char hello[] = "hello";

char *p = hello;

while (*p)
{
    *p += 1; // increase the character by one

    p += 1; // move to the next spot
}

printf(hello);

отпечатки

ifmmp

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

because it takes the value for each character and increments it by one. Есть в представлении ascii или как?

Eduardo Sebastian 04.06.2020 06:24

p=0; сбрасывает указатель?

GRquanti 07.03.2021 02:36

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

В C++ лучше использовать ссылки, чем указатели. Хотя ссылки по сути являются указателями, C++ до некоторой степени скрывает этот факт и создает впечатление, будто вы передаете по значению. Это позволяет легко изменить способ получения значения вызывающей функцией без изменения семантики его передачи.

Рассмотрим следующие примеры:

Использование ссылок:

public void doSomething()
{
    int i = 10;
    doSomethingElse(i);  // passes i by references since doSomethingElse() receives it
                         // by reference, but the syntax makes it appear as if i is passed
                         // by value
}

public void doSomethingElse(int& i)  // receives i as a reference
{
    cout << i << endl;
}

Использование указателей:

public void doSomething()
{
    int i = 10;
    doSomethingElse(&i);
}

public void doSomethingElse(int* i)
{
    cout << *i << endl;
}

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

SpoonMeiser 02.10.2008 20:36

Да, это, наверное, самое большое преимущество использования ссылок. Спасибо, что указали на это. Никакой каламбур :)

trshiv 02.10.2008 21:19

Вы наверняка может передаете нулевую ссылку. Это не так просто, как передать нулевой указатель.

n0rd 31.05.2013 00:54

согласен с @ n0rd. Если вы думаете, что не можете передать пустую ссылку, вы ошибаетесь. Легче передать висящую ссылку, чем нулевую, но в конечном итоге вы можете сделать и то, и другое достаточно просто. Ссылки - это не серебряная пуля, защищающая инженера от ранения себе в ногу. Смотрите вживую.

WhozCraig 09.09.2013 22:14

@ n0rd: Это явно неопределенное поведение.

Daniel Kamil Kozar 20.12.2013 18:04

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

einpoklum 28.05.2019 22:59

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

Указатели полезны там, где требуется высокая производительность и / или компактный объем памяти.

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

Вот немного другой, но проницательный взгляд на то, почему многие функции C имеют смысл: http://steve.yegge.googlepages.com/tour-de-babel#C

По сути, стандартная архитектура ЦП - это архитектура фон Неймана, и чрезвычайно полезно иметь возможность ссылаться на расположение элемента данных в памяти и выполнять с ним арифметические операции на такой машине. Если вы знаете какой-либо вариант языка ассемблера, вы быстро поймете, насколько это важно на низком уровне.

C++ делает указатели немного запутанными, поскольку иногда управляет ими за вас и скрывает их эффект в виде «ссылок». Если вы используете прямой язык C, потребность в указателях гораздо более очевидна: нет другого способа выполнить вызов по ссылке, это лучший способ сохранить строку, это лучший способ перебрать массив и т. д.

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

В старые времена DOS вы могли напрямую обращаться к видеопамяти видеокарты, объявляя указатель на:

unsigned char *pVideoMemory = (unsigned char *)0xA0000000;

Многие встроенные устройства до сих пор используют эту технику.

Нет причин использовать указатель - структура типа span, которая также содержит длину, гораздо более уместна. Сейчас это gsl::span, а скоро будет std::span.

einpoklum 28.05.2019 23:02
  • Зачем использовать указатели вместо обычных переменных?

Короткий ответ: не надо. ;-) Указатели следует использовать там, где больше ничего нельзя использовать. Это либо из-за отсутствия соответствующей функциональности, отсутствия типов данных, либо из-за чистой производительности. Подробнее ниже ...

  • Когда и где использовать указатели?

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

  • Как использовать указатели с массивами?

Приложив немного усилий и много путаницы. ;-) Если мы говорим о простых типах данных, таких как int и char, разница между массивом и указателем небольшая. Эти объявления очень похожи (но не одинаковы - например, sizeof вернет разные значения):

char* a = "Hello";
char a[] = "Hello";

Вы можете получить доступ к любому элементу в массиве следующим образом

printf("Second char is: %c", a[1]);

Индекс 1, поскольку массив начинается с элемента 0. :-)

Или вы могли бы сделать то же самое

printf("Second char is: %c", *(a+1));

Оператор указателя (*) необходим, поскольку мы сообщаем printf, что хотим напечатать символ. Без * было бы напечатано символьное представление самого адреса памяти. Теперь мы используем самого персонажа. Если бы мы использовали% s вместо% c, мы бы попросили printf распечатать содержимое адреса памяти, на который указывает 'a' плюс один (в этом примере выше), и нам бы не пришлось ставить * спереди:

printf("Second char is: %s", (a+1)); /* WRONG */

Но это не просто напечатало бы второй символ, а вместо этого все символы в следующих адресах памяти, пока не был бы найден нулевой символ (\ 0). И здесь все становится опасным. Что, если вы случайно попытаетесь напечатать переменную типа integer вместо указателя char с помощью программы форматирования% s?

char* a = "Hello";
int b = 120;
printf("Second char is: %s", b);

Это напечатает все, что найдено по адресу памяти 120, и продолжит печать, пока не будет найден нулевой символ. Выполнять этот оператор printf неправильно и незаконно, но он, вероятно, все равно будет работать, поскольку во многих средах указатель фактически имеет тип int. Представьте себе проблемы, которые вы могли бы вызвать, если бы вместо этого использовали sprintf () и назначили таким образом слишком длинный «массив символов» другой переменной, для которой было выделено только определенное ограниченное пространство. Скорее всего, вы закончите записывать что-то еще в памяти и вызвать сбой вашей программы (если вам повезет).

Да, и если вы не присваиваете строковое значение массиву / указателю char при его объявлении, вы ДОЛЖНЫ выделить ему достаточный объем памяти, прежде чем присвоить ему значение. Использование malloc, calloc или подобных. Это потому, что вы объявили только один элемент в своем массиве / один адрес памяти, на который нужно указывать. Итак, вот несколько примеров:

char* x;
/* Allocate 6 bytes of memory for me and point x to the first of them. */
x = (char*) malloc(6);
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* Delete the allocation (reservation) of the memory. */
/* The char pointer x is still pointing to this address in memory though! */
free(x);
/* Same as malloc but here the allocated space is filled with null characters!*/
x = (char *) calloc(6, sizeof(x));
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* And delete the allocation again... */
free(x);
/* We can set the size at declaration time as well */
char xx[6];
xx[0] = 'H';
xx[1] = 'e';
xx[2] = 'l';
xx[3] = 'l';
xx[4] = 'o';
xx[5] = '\0';
printf("String \"%s\" at address: %d\n", xx, xx);

Обратите внимание, что вы все еще можете использовать переменную x после того, как вы выполнили free () выделенной памяти, но вы не знаете, что там находится. Также обратите внимание, что два printf () могут дать вам разные адреса, поскольку нет гарантии, что второе выделение памяти будет выполнено в том же пространстве, что и первое.

Ваш пример со 120 ошибочен. Он использует% c, а не% s, поэтому ошибки нет; он просто печатает строчную букву x. Более того, ваше последующее утверждение, что указатель имеет тип int, неверно и очень плохой совет для программиста на C, не имеющего опыта работы с указателями.

R.. GitHub STOP HELPING ICE 05.07.2010 15:29

@R ..% c был изменен на% s

Kelly S. French 23.09.2010 19:05

-1 Вы начали хорошо, но первый пример неверный. Нет, это не то же самое. В первом случае a - это указатель, а во втором - a - это массив. Я уже упоминал об этом? Это не одно и то же! Убедитесь сами: сравните sizeof (a), попробуйте присвоить массиву новый адрес. Это не сработает.

sellibitze 23.09.2010 19:20

«Выполнение этого оператора printf не является неправильным или незаконным». Это совершенно неправильно. Этот оператор printf имеет неопределенное поведение. Во многих реализациях это вызовет нарушение прав доступа. Указатели на самом деле не относятся к типу int, они на самом деле указатели (и ничего больше).

Mankarse 14.08.2011 07:58

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

asveikau 18.06.2012 07:43

Раздел 6 comp.lang.c FAQ очень хорошо объясняет взаимосвязь между массивами и указателями в C. Это не ответ.

Keith Thompson 30.05.2013 22:56

«Не надо» -1. Необязательные (нулевые) аргументы. Значения, «необязательно» возвращаемые функциями.

SigTerm 30.05.2013 22:59

-1 Полиморфизм в C++ требует использование указателей.

Sildoreth 30.05.2013 23:06

@Sildoreth На самом деле это не так. void do_something(Base& x) { x.virtual_method(); } int main() { Derived d; DoSomething(d); } вызывает виртуальные функции без использования указателей. Указатели упрощают обмен, копирование и почти все остальное.

milleniumbug 31.05.2013 00:16

@milleniumbug: boost не является частью C++.

SigTerm 31.05.2013 00:18

@SigTerm извините, тег C++ в вопросе меня смутил.

milleniumbug 31.05.2013 00:18

@milleniumbug: это необязательная сторонняя библиотека. Если они решат преобразовать :: optional в основной язык, ваш контраргумент может стать действительным.

SigTerm 31.05.2013 00:21

@milleniumbug Собственно, в вашем примере задействованы указатели являются. '&' - это просто синтаксический сахар, который делает его Посмотрите, как будто вы не используете указатели.

Sildoreth 31.05.2013 00:21

@SigTerm Я читал на isocpp, что он будет частью C++ 14.

milleniumbug 31.05.2013 00:23

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

Shoe 26.09.2013 19:37

@Jefffrey Если вы не нашли документацию, в которой говорится, что нет, как это работает, я поддерживаю то, что я сказал. Вот так просто работает имеет. Используя ссылки, вы имеете в виду значение, которое потенциально принадлежит другому уровню стека. И его изменение фактически изменяет значение в исходном месте. Именно так работают указатели. Кроме того, эта страница говорит, что значения не копируются при вызове функции. Указатели должен задействованы.

Sildoreth 11.10.2013 22:06

@Sildoreth, стандарт C++ 11 никогда не определяет, как будут реализованы ссылки должен. Это выбор компилятора и ты не должен полагаться на это. Тот факт, что большинство компиляторов реализует это с помощью указателей не имеет ничего общего с определенный, при использовании ссылок задействован указатель.

Shoe 11.10.2013 22:15

@ Джефффри Достаточно справедливо. Возможно, хотя и маловероятно, что существует неэффективный компилятор, который копирует значения в параметры, а затем копирует их обратно. В любом случае, суть в том, что вам нужно косвенное обращение к той или иной форме, чтобы использовать полиморфизм подтипов в C++. А переход по ссылке - это форма косвенного обращения.

Sildoreth 11.10.2013 22:31

-1: «Эти декларации одинаковы char* a = "Hello"; char a[] = "Hello";» - черт возьми, нет. Они НЕТ одинаковые.

milleniumbug 21.06.2014 23:57

Это был хороший ответ в 2008 году, но его действительно стоит обновить с помощью std::array и, возможно, строковых представлений.

einpoklum 28.05.2019 22:57

В java и C# все ссылки на объекты являются указателями, а в C++ у вас больше контроля над тем, куда вы указываете. Помните, что великая сила влечет за собой большую ответственность.

Один из способов использования указателей над переменными - исключить необходимость дублирования памяти. Например, если у вас есть большой сложный объект, вы можете использовать указатель, чтобы указывать на эту переменную для каждой ссылки, которую вы делаете. С переменной нужно дублировать память для каждой копии.

Это причина использовать ссылки (или, самое большее, обертки ссылок), а не указатели.

einpoklum 28.05.2019 23:03
  • В некоторых случаях указатели на функции требуются для использования функций из общей библиотеки (.DLL или .so). Это включает выполнение работы на разных языках, где часто предоставляется интерфейс DLL.
  • Создание компиляторов
  • Создание научных калькуляторов, где у вас есть массив, векторная или строковая карта указателей на функции?
  • Попытка изменить видеопамять напрямую - создание собственного графического пакета
  • Делаем API!
  • Структуры данных - указатели узловых ссылок для особых деревьев, которые вы создаете

Для указателей есть много причин. Изменение имен C особенно важно в библиотеках DLL, если вы хотите поддерживать межъязыковую совместимость.

Вот мой ответ, и я не обещаю быть экспертом, но я обнаружил, что указатели отлично подходят для одной из моих библиотек, которую я пытаюсь написать. В этой библиотеке (это графический API с OpenGL :-)) вы можете создать треугольник с переданными в него объектами вершин. Метод рисования берет эти треугольные объекты и ... рисует их на основе созданных мной вершинных объектов. Что ж, все в порядке.

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

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

С указателями вам не нужно обновлять треугольники.

Если бы у меня было три * вершинных объекта на класс треугольника, я не только экономил бы место, потому что у миллиарда треугольников нет трех вершинных объектов, которые сами по себе большие, но также эти указатели всегда будут указывать на вершины, для которых они предназначены, независимо от того, для чего они нужны. как часто меняются вершины. Поскольку указатели по-прежнему указывают на одну и ту же вершину, треугольники не меняются, и процесс обновления легче обрабатывать. Если бы я запутал вас, я бы не сомневался, я не претендую на роль эксперта, просто вкладываю свои два цента в обсуждение.

Необходимость указателей на языке C описана здесь

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

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

PS: Пожалуйста, обратитесь к ссылка предоставлена для подробного объяснения с образцом кода.

Вопрос о C++, этот ответ о C.

einpoklum 28.05.2019 23:06

нет, вопрос помечен как c И C++. Возможно, тег c не имеет значения, но он здесь с самого начала.

Jean-François Fabre 29.05.2019 00:08

В C++, если вы хотите использовать подтип полиморфизм, вы имеют используете указатели. Смотрите этот пост: Полиморфизм C++ без указателей.

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

Идея наличия переменной, содержащей объект неизвестного класса, несовместима с режимом хранения объектов в стеке по умолчанию (без указателя) C++, где объем выделенного пространства напрямую соответствует классу. Примечание: если в классе 5 полей экземпляров вместо 3, потребуется выделить больше места.


Note that if you are using '&' to pass arguments by reference, indirection (i.e., pointers) is still involved behind the scenes. The '&' is just syntactic sugar that (1) saves you the trouble of using pointer syntax and (2) allows the compiler to be more strict (such as prohibiting null pointers).

Нет, вы не можете использовать имеют для использования указателей - вы можете использовать ссылки. А когда вы пишете «за кадром задействованы указатели» - это бессмысленно. Инструкции goto также используются негласно - в инструкциях целевой машины. Мы до сих пор не утверждаем, что используем их.

einpoklum 28.05.2019 23:03

@ einpoklum-reinstateMonica Если у вас есть набор объектов, над которыми вы хотите действовать, присваивая каждый элемент по очереди временной переменной и вызывая полиморфный метод для этой переменной, тогда да, вам НУЖЕН указатель, потому что вы не можете повторно привязать ссылку.

Sildoreth 12.12.2019 16:57

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

einpoklum 12.12.2019 17:13

@ einpoklum-reinstateMonica Это верно. Но вы не можете изменить, на какой объект ссылаются. Поэтому, если вы перебираете список / набор / массив этих объектов, ссылочная переменная не будет работать.

Sildoreth 13.12.2019 02:03

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

saxbophone 28.01.2021 00:55

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

Проблема с конструкциями C++, которые люди обычно используют для замены указателей, очень зависит от набора инструментов, который вы используете, что нормально, когда у вас есть весь необходимый контроль над исходным кодом, однако, если вы скомпилируете статическую библиотеку, например, с Visual Studio 2008 и попробуйте использовать его в Visual Studio 2010, вы получите массу ошибок компоновщика, потому что новый проект связан с более новой версией STL, которая не имеет обратной совместимости. Все становится еще хуже, если вы компилируете DLL и даете библиотеку импорта, которую люди используют в другом наборе инструментов, потому что в этом случае ваша программа рано или поздно выйдет из строя без видимой причины.

Итак, с целью перемещения больших наборов данных из одной библиотеки в другую вы можете рассмотреть возможность предоставления указателя на массив функции, которая должна копировать данные, если вы не хотите заставлять других использовать те же инструменты, которые вы используете. . Хорошая часть этого заключается в том, что это даже не обязательно должен быть массив в стиле C, вы можете использовать std :: vector и указать указатель, указав, например, адрес первого элемента & vector [0], и использовать std :: vector для внутреннего управления массивом.

Еще одна веская причина для использования указателей в C++ снова относится к библиотекам, подумайте о наличии библиотеки DLL, которая не может быть загружена при запуске вашей программы, поэтому, если вы используете библиотеку импорта, зависимость не удовлетворяется и программа дает сбой. Это имеет место, например, когда вы предоставляете публичный api в dll вместе со своим приложением и хотите получить к нему доступ из других приложений. В этом случае, чтобы использовать API, вам необходимо загрузить dll из ее местоположения (обычно это ключ реестра), а затем вам нужно использовать указатель на функцию, чтобы иметь возможность вызывать функции внутри DLL. Иногда люди, которые создают API, достаточно любезны, чтобы предоставить вам файл .h, содержащий вспомогательные функции для автоматизации этого процесса и предоставить вам все необходимые указатели функций, но если нет, вы можете использовать LoadLibrary и GetProcAddress в окнах и dlopen и dlsym в unix, чтобы получить их (учитывая, что вы знаете всю сигнатуру функции).

Позвольте мне попытаться ответить и на это.

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

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

Зачем использовать указатели вместо обычных переменных? Ответ становится яснее, когда вы имеете дело со сложными типами, такими как классы, структуры и массивы. Если бы вы использовали обычную переменную, вы могли бы сделать копию (компиляторы достаточно умны, чтобы предотвратить это в некоторых ситуациях, и C++ 11 тоже помогает, но мы пока воздержимся от этого обсуждения).

Что произойдет, если вы захотите изменить исходное значение? Вы можете использовать что-то вроде этого:

MyType a; //let's ignore what MyType actually is right now.
a = modify(a); 

Это будет работать нормально, и если вы точно не знаете, для чего используете указатели, вам не следует их использовать. Остерегайтесь причины «они, наверное, быстрее». Запустите свои собственные тесты, и если они действительно быстрее, то используйте их.

Однако предположим, что вы решаете проблему, в которой вам нужно выделить память. Когда вы выделяете память, вам нужно освободить ее. Выделение памяти может быть успешным, а может и нет. Вот где пригодится указатели - они позволяют проверить наличие объекта, которые вы выделили, и они позволяют вам получить доступ к объекту, для которого была выделена память, путем отмены ссылки на указатель.

MyType *p = NULL; //empty pointer
if (p)
{
    //we never reach here, because the pointer points to nothing
}
//now, let's allocate some memory
p = new MyType[50000];
if (p) //if the memory was allocated, this test will pass
{
    //we can do something with our allocated array
    for(size_t i=0; i!=50000; i++)
    {
        MyType &v = *(p+i); //get a reference to the ith object
        //do something with it
        //...
    }
    delete[] p; //we're done. de-allocate the memory
}

Это ключ к тому, почему вы должны использовать указатели - ссылки предполагают, что элемент, на который вы ссылаетесь, уже существует. Указатель - нет.

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

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

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

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

Просто запомните эту разницу: Ссылка - это, по сути, действительный указатель. Указатель не всегда действителен.

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

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

einpoklum 28.05.2019 23:00

Другие вопросы по теме