Какие проблемы программирования лучше всего решать с помощью указателей? [закрыто]


58

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

Какие хорошие проекты или проблемы нужно решить, используя указатели, чтобы я мог лучше их понять?


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

37
Проблемы программирования создаются с помощью указателей, а не решаются. :)
xpda

4
Собственно, интересно, какие проблемы можно решить без указателя. Очень немногие наверняка.
Deadalnix

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

3
тьфу, некоторые из этих ответов ужасны.

Ответы:


68

Манипулирование большими объемами данных в памяти - вот где действительно светятся указатели.

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


12
Это отличный ответ, и я бы добавил, что вам не нужны большие объекты для манипулирования большими объемами данных; иногда это можно сделать, манипулируя большими объектами, но еще более распространенная проблема - действительно очень часто манипулировать кучей мелких объектов. Что, если подумать, касается практически всех компьютерных программ.
Джоккинг

44

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

  1. Сортировка
    При перемещении данных в алгоритме сортировки вы можете перемещать указатель вместо самих данных - подумайте о сортировке миллионов строк в строке из 100 символов; Вы сохраняете много ненужных перемещений данных.

  2. Связанные списки
    Вы можете хранить следующее и / или предыдущее местоположение элемента, а не все данные, связанные с записью.

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

Эта концепция может быть расширена до структур данных, таких как реляционные базы данных, где указатель похож на внешний ключ . Некоторые языки не поощряют использование указателей, таких как C # и COBOL.

Примеры можно найти во многих местах, таких как:

Следующий пост может быть уместным в некотором роде:


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

Хорошее, хорошее объяснение
Сабби

5
Пришлось понизить за комментарий C #. «Ссылки» в C # эквивалентны указателям, просто C # отвлекает вас от понимания того, что на самом деле вы имеете дело только с указателем на объект в управляемой куче.
MattDavey

1
@MattDavey: ссылки на C # не являются указателями. Вы не можете добавлять из них, использовать их для индексации массива, использовать множественные косвенные ссылки и т. Д. У вас не может быть ссылок на ссылки, например.
Билли ONEAL

5
@ Билли ONeal идея, что «это только указатель, если вы можете сделать арифметику на нем», откровенно смешно, но у меня нет терпения, чтобы спорить с вами.
MattDavey

29

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

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


Вы забываете связанный список XOR [=
dan_waterworth

@dan_waterworth: Нет. Я слишком хорошо знаю эту черную магию.
Джон Пурди,

8

Один простой способ в полиморфизме. Полиморфизм работает только с указателями.

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

for(int i = 0; i < size; i++)
   std::cout << array[i];

является эквивалентом

for(int i = 0; i < size; i++)
   std::cout << *(array + i);

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

while( (*array1++ = *array2++) != '\0')

В c ++ вы используете new для выделения памяти для объекта и сохранения его в указателе. Вы делаете это в любое время, когда вам нужно создать объект во время выполнения, а не во время компиляции (т. Е. Метод создает новый объект и сохраняет его в списке).

Чтобы лучше понять указатели:

  1. Найдите несколько проектов, которые играют с традиционными c-струнами и массивами.
  2. Найдите несколько проектов, которые используют наследование.

  3. Вот проект, на котором я порезался:

Считайте из файла две матрицы nxn, выполните с ними основные операции векторного пространства и распечатайте их результат на экране.

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


2
«Полиморфизм работает только с указателями». Не точно: полиморфизм также работает со ссылками. Это, в конечном счете, указатели, но я думаю, что различие важно, особенно для новичков.
Лоран Пирейн

Ваш пример для копирования массива в одну строку действительно для копирования строки с нулевым символом в одной строке, а не какого-либо общего массива.
tcrosley

8

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

Ниже приведен пример выделения стека:

struct Foo {
  int bar, baz
};

void foo(void) {
  struct Foo f;
}

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

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

Например, следующая функция может привести к так называемому «неопределенному поведению».

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  struct Foo f;
  return &f;
}

Если вы хотите вернуть что-то вроде struct Foo *функции, то вам действительно нужно выделить кучу:

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  return malloc(sizeof(struct Foo));
}

Функция mallocразмещает объект в куче и возвращает указатель на этот объект. Обратите внимание, что термин «объект» используется здесь свободно, что означает «что-то», а не объект в смысле объектно-ориентированного программирования.

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

Изменить : я не заметил, что этот вопрос помечен как вопрос C ++. Операторы C ++ newи new[]выполняют ту же функцию, что и malloc. Операторы deleteи delete[]аналогичны free. Хотя newи deleteдолжно использоваться исключительно для выделения и освобождения объектов C ++, использование mallocи freeсовершенно допустимо в коде C ++.


1
(+1), также известный как «статически распределенные переменные» и «динамически распределенные переменные» ;-)
umlcat

4

Напишите любой нетривиальный проект на C, и вам придется выяснить, как / когда использовать указатели. В C ++ вы будете в основном использовать объекты с поддержкой RAII, которые управляют указателями внутри, но в C необработанные указатели играют гораздо более важную роль. Что касается проекта, который вы должны сделать, это может быть что угодно:

  • Крошечный и простой веб-сервер
  • Инструменты командной строки Unix (less, cat, sort и т. Д.)
  • Какой-то конкретный проект, который вы хотите сделать ради себя, а не только для обучения

Я рекомендую последний.


Так что я просто иду на проект, который хочу сделать (например, 2D-игру), и предполагается, что мне понадобятся и будут изучать указатели?
Дисоко

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

3

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

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

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

Что касается того, почему вам нужна косвенность, список довольно длинный, но, по сути, две ключевые идеи:

  1. Копирование значений больших объектов происходит медленно и приводит к тому, что объект будет храниться в ОЗУ дважды (потенциально очень дорого), но копирование по ссылке происходит почти мгновенно, используя только несколько байтов ОЗУ (для адреса). Например, скажем, у вас в памяти ~ 1000 больших объектов (каждый, скажем, около 1 МБ ОЗУ), и ваш пользователь должен иметь возможность выбрать текущий объект (на который будет воздействовать пользователь). Наличие переменной, selected_objectкоторая является ссылкой на один из объектов, гораздо эффективнее, чем копирование значения текущего объекта в новую переменную.
  2. Наличие сложных структур данных, которые ссылаются на другие объекты, такие как связанные списки или деревья, где каждый элемент в структуре данных ссылается на другие элементы в структуре данных. Преимущество ссылки на другие элементы в структуре данных означает, что вам не нужно перемещать каждый элемент списка в памяти только потому, что вы вставили новый элемент в середину списка (может иметь постоянные вставки по времени).

2

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


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

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

@Thomas, хотя вы правы, вопрос касается c ++, который предоставляет указатели разработчику.
Джонатан Хенсон

@Jonathan Несмотря на это, если вы можете решить проблему на языке, который не предоставляет указатели, то также возможно решить эту проблему на языке, который предоставляет указатели без использования указателей. Это делает первое предложение правдивым, а второе - ложным - насколько мне известно, нет проблем, которые невозможно решить без использования указателей.
Томас Оуэнс

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

1

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

Когда их использовать: как можно чаще, потому что копирование данных неэффективно. Когда их не использовать: когда вы хотите две копии, которые могут быть изменены по отдельности. (Что в итоге приведет к копированию содержимого object_1 в другое место в памяти и возвращению указателя - на этот раз указывая на object_2)


1

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

Если вы хотите узнать, почему указатели так важны, я бы посоветовал узнать, что такое связанный список, и попытаться написать его, не используя указатели. Я не ставил перед вами невыполнимую задачу (СОВЕТ: указатели используются для ссылки на места в памяти, как вы ссылаетесь на вещи в массивах?).


+1 для упоминания структур данных, использование в алгоритмах является естественным следствием.
Матье М.

0

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

например, канал ITCH 4.1 имеет концепцию «заменяющих» заказов, где цены (и, следовательно, приоритет) могут изменяться. Вы хотите иметь возможность принимать заказы и перемещать их в другое место. Реализация двусторонней очереди с указателями делает операцию очень простой.


0

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

И эта оценка такова: это очень странный вопрос для программиста на Си. Практически все, что говорит: «Я не знаю C.» Если я проанализирую немного дальше и более цинично, то получу продолжение этого «вопроса»: «Есть ли какой-нибудь способ, которым я могу воспользоваться, чтобы быстро и внезапно получить знания опытного программиста на C, не тратя время? для самостоятельного изучения? Любой «ответ», который кто-то может дать, не может заменить работу и понимание основной концепции и ее использования.

Я чувствую, что более конструктивно лучше изучать язык C из первых рук, чем заходить в Интернет и спрашивать людей об этом. Когда вы хорошо знаете C, вы не будете задавать подобные вопросы, это все равно что спрашивать: «Какие проблемы лучше всего решить с помощью зубной щетки?»


0

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

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

Пока вы можете требовать определенную память во время выполнения и помещать свои данные во вновь полученную память, вы можете делать следующее:

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

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


0

Забавно, я только что ответил на вопрос о C ++ и поговорил об указателях.

Короче говоря, вы НИКОГДА не нуждаетесь в указателях, кроме случаев, когда: 1) библиотека, которую вы используете, заставляет вас 2) вам нужна ссылка на Nullable.

Если вам нужен массив, список, строка и т. Д., Просто поместите его в стек и используйте объект stl. Возврат или передача объектов stl выполняются быстро (факт не проверен), потому что они имеют внутренний код, который копирует указатель вместо объекта и будет копировать данные, только если вы пишете в него. Это обычный C ++, даже не новый C ++ 11, который облегчит работу с авторами библиотек.

Ваш вопрос может быть дан ответ в этой части

Если вы используете указатель, убедитесь, что он находится в одном из этих двух условий. 1) Вы передаете ввод, который может быть обнуляемым. Примером является необязательное имя файла. 2) Если вы хотите передать право собственности. Например, если вы передадите или вернете указатель, у вас не останется ЛЮБЫХ копий и не используйте указатель, который вы отдаете.

ptr=blah; func(ptr); //never use ptr again for here on out.

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

ДОПОЛНИТЕЛЬНОЕ ПРИМЕЧАНИЕ: я замечаю, что пишу свои собственные структуры и раздаю их. Так как мне сделать это без использования указателей? Это не контейнер STL, поэтому передача по ссылке идет медленно. Я всегда загружаю свой список данных / deques / maps и тому подобное. Я не помню, чтобы возвращались какие-либо объекты, если это не был какой-то список / карта. Даже не строка. Я посмотрел на код для отдельных объектов, и я заметил, что я делаю что-то вроде этого, { MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }поэтому я в значительной степени возвращаю объекты (несколько) или предварительно распределяю / передаю ссылку для заполнения (один).

Теперь, если вы писали свою собственную карту, карту и т. Д., Вам нужно использовать указатели. Но вам не нужно. Пусть STL и, возможно, повысить беспокойство об этом. Вам просто нужно написать данные и решения. Не контейнеры для их хранения;)

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


0

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


0

Я вижу указатели как указательный палец, который мы используем для нескольких вещей:

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

пожалуйста, прости этот плохой ответ


0

Поскольку ваш вопрос помечен C ++, я отвечу на ваш вопрос для этого языка.

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

1. Полиморфизм

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

2. Создание постоянных объектов

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

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


Может быть, кто-то предпочел бы нарезать объект, чем использовать указатель. Счастливая отладка: D
deadalnix
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.