DO и Donts при использовании указателей - PullRequest
7 голосов
/ 12 ноября 2009

Это простой, но важный вопрос. Каковы преимущества и недостатки при использовании указателей в C и C ++, чтобы избежать ошибок SEGMENTATION FAULT в AIX?

Где символ * предпочтительнее, чем массив символов?

Ответы [ 10 ]

18 голосов
/ 12 ноября 2009

C ++ специфично

  • Избегайте ручного управления памятью , вместо этого используйте контейнеры RAII, такие как std::auto_ptr, boost::scoped_ptr, boost::shared_ptr и эквивалентные контейнеры массива. Еще проще, часто std::vector работает просто отлично, и вообще не требует использования указателей. Наконец, вместо передачи указателей на большие или изменяемые структуры данных вы можете передавать ссылки. Указатели могут представлять массивы, тогда как ссылки избегают этой двусмысленности.
  • Индексирование массивов является достойной заменой большинству арифметики указателей. Как правило, оно не медленнее, чем арифметика указателей, которое часто легче ошибиться и труднее читать и поддерживать. Как правило, не используйте микрооптимизацию за счет читабельности.
  • Вызовите правильную процедуру освобождения: delete[] для массивов, выделенных только через new ...[], delete для указателей только на отдельные объекты и free(...) только для памяти, выделенной через C api (например, malloc(..)). Это не должно быть перепутано; Процедуры освобождения C ++ включают вызовы деструкторов и, следовательно, должны вызываться правильно.
  • Явно инициализируйте и установите бессмысленные указатели на NULL. Проще отладить случайную нулевую разыменование, чем неправильный доступ к памяти. Можно освободить указатель NULL, поэтому вам не нужно загромождать деструктор проверками, чтобы избежать этого. Если вы delete объект преждевременно, вам следует установить его указатель на NULL, чтобы избежать двойного удаления одного и того же указателя и избежать случайного разыменования свисающего указателя.
  • Если вы используете наследование C ++ и переопределяете деструктор, ознакомьтесь с виртуальными деструкторами; они необходимы для корректности (короче говоря, базовый класс должен явно пометить деструктор как виртуальный).
  • Знайте, кому «принадлежит» указатель , если вам нужно вручную управлять памятью. Только владелец должен освобождать объект или массив, на который указывает указатель, и никакой другой объект не может использовать объект после того, как владелец delete указывает на него. boost::shared_ptr - это достаточно простой и довольно дешевый контейнер, который часто подходит, когда вам нужно поделиться указателем.
12 голосов
/ 12 ноября 2009
  1. Всегда проверяйте, инициализированы ли ваши указатели
  2. Всегда знать длину выделенной области памяти
  3. Всегда проверять значение NULL при работе с указателем
  4. Всегда удаляйте указатели после их использования
  5. Никогда не перезаписывать память за концы указателя
  6. Никогда не доверяйте указателям достоверности, предоставленным вашим методам
  7. Никогда не помещайте пользовательский ввод в указатель (например, получает)
  8. Никогда не смешивайте malloc / free с новым / delete
  9. Никогда не назначайте указатели объектам класса с помощью malloc
  10. Никогда не освобождайте указатели на объекты класса бесплатно

И последнее, но не менее важное

Никогда не используйте указатели, если вам не нужно ... Существуют (const) ссылки, чтобы избежать копирования объектов, передаваемых в функции, а также контейнеры и строки STL для других требований к хранилищу и использовать smart (boost: : shared_ptr) указатели, если вы действительно нуждаетесь в указателях и не хотите отслеживать их вручную.

3 голосов
/ 12 ноября 2009

В С

  • Не указывайте на память, которой вы не "владеете".
  • Не разыменовывать указатели NULL.
  • Делайте бесплатные ресурсы, когда они вам не нужны.
  • Не упускайте из виду ваши указатели (остерегайтесь неудачных перераспределений).
  • Не злоупотребляйте void*.
  • Не смешивайте типы указателей: char* - это не то же самое, что double**.
  • Проверьте возвращаемое значение функций, которые возвращают указатели.
2 голосов
/ 12 ноября 2009

Написать стандартный совместимый код. Это должно заботиться о платформе. И много других вопросов. И используйте const. Это решает больше вопросов.

1 голос
/ 12 ноября 2009

ПРИМЕЧАНИЕ: C конкретный ответ ->

Ниже приведены типичные причины этого нарушения сегментации или ошибки сегментации:

Неправильная строка управления форматом в инструкциях printf или scanf: Убедитесь, что строка управления форматом имеет то же количество спецификаторов преобразования (%), что и у printf или scanf аргументы для печати или чтения, соответственно, и что спецификаторы соответствуют типу переменной, которая будет напечатана или прочитана. Это также относится к fprintf и fscanf.

Забывает использовать «&» в аргументах для scanf: Функция scanf принимает в качестве аргументов строку управления форматом и адреса переменных, в которые она будет помещать данные, в которые она читает. Оператор «&» (address of) используется для предоставления адреса переменной. Обычно забывают использовать «&» с каждой переменной в вызове scanf. Пропуск знака «&» может вызвать нарушение сегментации.

Доступ за пределы массива: Убедитесь, что вы не нарушили границы любого используемого вами массива; т.е. вы не подписали массив со значением, меньшим, чем индекс его самого низкого элемента или больше, чем индекс его самого высокого элемента.

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

Неправильное использование операторов «&» (адрес) и «» (разыменование): Убедитесь, что вы понимаете, как работают эти операторы. Знайте, когда их следует применять, а когда не применять. Как упоминалось выше, часто забывают использовать «&» с каждой переменной в вызове scanf. Помните, что scanf требует адрес переменных, в которых он читает. Особенно, знайте, когда «&» и «» абсолютно необходимы и когда лучше избегать их использования.

Устранение проблемы:

Проверьте КАЖДОЕ место в вашей программе, которое использует указатели, подписывает массив или использует оператор адреса (&) и оператор разыменования (*). Каждый из них является кандидатом на то, чтобы стать причиной нарушения сегментации. Убедитесь, что вы понимаете использование указателей и связанных с ними операторов. Если программа использует много указателей и имеет много вхождений & и *, добавьте несколько операторов printf, чтобы точно определить место, в котором программа вызывает ошибку, и исследуйте указатели и переменные, связанные с этим оператором.

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

1 голос
/ 12 ноября 2009

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

1 голос
/ 12 ноября 2009

Я присоединился некоторое время назад к проекту, в котором несколько хороших разработчиков использовали только голые указатели. Несмотря на то, что они обладают отличными навыками, их программа (работающая на Windows / Linux / HP-UX) время от времени вызывала ошибки в работе системы, и было несколько утечек памяти.

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

0 голосов
/ 12 ноября 2009

Не совсем ответ на вопрос "Где символ * предпочтительнее, чем массив символов?" вопрос, но одна вещь, которая недавно укусила кого-то, кого я рассматривал, была "sizeof".

Учтите это:

char *a = new char[3];
char b[3];

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

strncpy(b, some_string, sizeof(b));    
strncpy(a, some_string, sizeof(a));

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

0 голосов
/ 12 ноября 2009

Не пропустите проверку границ.

0 голосов
/ 12 ноября 2009

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

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

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