Есть ли применение для неинициализированных указателей в C или C ++? - PullRequest
4 голосов
/ 16 декабря 2009

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

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

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

Ответы [ 10 ]

17 голосов
/ 16 декабря 2009

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

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

Когда мы загружали данные, они загружались в один большой блок. Все данные, на которые ссылается корень, были вне корневого объекта. Мы могли бы сделать в корне «новое» на месте, которое инициализировало бы правильные таблицы VF для объекта и исправляло бы все присоединенные блоки (выполняя новые на месте и затем исправляя присоединенные блоки соответственно).

Нам понадобились конструкторы, вызванные (вместо новых), чтобы генерировать правильные таблицы VF в объектах. Однако если бы указатели были автоматически очищены до NULL во время конструктора, мы бы потеряли данные о смещении и не смогли бы воссоздать указатели между объектами в непрерывном блоке.


FWIW, это распространенная техника в мире видеоигр. Эта статья Gamasutra (не написанная мной или моими коллегами) подробно объясняет то же самое, что они делали в другой компании:

Также эта тема обсуждения на SourceForge .

Было даже несколько выступлений GDC ( Game Developer Conference ) на эту тему.

Поиск в Google «загрузки на месте» даст много других примеров людей, использующих эту технику, для которой в основном требуются неинициализированные указатели.


ПРИМЕЧАНИЕ. В настоящее время это единственный ответ, который фактически отвечает на заданный вопрос («Используются ли неинициализированные указатели в C или C ++?»), Давая конкретное использование указателям, которые должны оставаться унифицированными.

Все остальные ответы являются лучшими ответами на исходный вопрос, на который ссылается («[C ++] Почему указатели не инициализируются с NULL по умолчанию?»), Из-за которого постер задал этот вопрос.

11 голосов
/ 16 декабря 2009

Прежде всего, инициализация указателей (или любых других переменных) по умолчанию не нарушает совместимость с C. Как в C, так и в C ++ утверждается, что значение неинициализированной переменной является неопределенным; на практике это означает, что он может содержать любое значение (включая представление прерываний), но учтите, что 0 принадлежит набору «любых значений»! Таким образом, совместимая реализация может прекрасно инициализировать все указатели на 0. Однако ваша программа, если она будет опираться на это, не будет соответствовать.

Теперь о том, почему вы можете захотеть, чтобы ваш указатель не был инициализирован: в основном, когда он записывается впоследствии. Например:

void foo(int*& p) {
   p = new int;
}

int* p; // why initialize? we overwrite it anyway
foo(p);

Вы можете сказать, что компилятор должен быть в состоянии оптимизировать это. К сожалению, этого нельзя сделать, если определение foo недоступно (например, глобальные оптимизации времени соединения отключены или включены, но функция находится в DLL), поскольку не знает, если foo попытается прочитать из p (и тогда потребуется инициализация), или если он просто запишет в него (и тогда инициализация не нужна). Более того, могут быть случаи, которые сложнее анализировать; например:

bool try_parse_int(const char* s, int& n)
{
    // if parsed successfully, assign result to n and return true
    // if there was error parsing, don't touch n and return false
    ...
}

int n;
if (try_parse_int(s, n)) {
    // use n here
    ...
} else {
   // don't use n here
   ...
}

Этот компилятор гораздо сложнее анализировать, даже если он содержит полные определения всех функций.

8 голосов
/ 16 декабря 2009

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

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

6 голосов
/ 16 декабря 2009

Использовать для унифицированного указателя? Смотрите std :: strtol ().

char * bad;
long n = std::strtol( "123xyz", &bad, 10 );

после этого вызова bad будет содержать указатель на 'x'. Нет смысла его инициализировать.

2 голосов
/ 16 декабря 2009

Это не будет нарушать совместимость с C - реализация C, в которой все не инициализированные указатели фактически инициализируются равными 0 (или даже вообще чем-либо!), Будет полностью соответствовать.

2 голосов
/ 16 декабря 2009

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

1 голос
/ 16 декабря 2009

Не нужно платить за то, что вам не нужно здесь не для совместимости с C. Это также один из самых важных принципов C ++.

0 голосов
/ 16 декабря 2009

Предположим, у вас есть функция, которая является обработчиком прерываний, которая требует большого количества переменных в стеке указателей:

void interruptHandler()
{
    char* ptr0;
    ...
    char* ptrX-1;
    char* ptrX;
}

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

0 голосов
/ 16 декабря 2009

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

0 голосов
/ 16 декабря 2009

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

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

Однако короткий ответ - нет. Просто присвойте ему значение NULL.

...