Инициализация указателей char

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

Я объявил и инициализировал это следующим образом:

char * p = NULL;

Мне просто интересно, хорошо ли это. Я использую gcc 4.3.3.

Ответ 1

Да, это хорошая идея. Стиль Google Code рекомендует:

  • Чтобы инициализировать все ваши переменные, даже если они вам не нужны прямо сейчас.
  • Инициализировать указатели на NULL, int на 0 и float на 0,0 - только для лучшей читаемости.

    int i = 0;
    double x = 0.0;
    char* c = NULL;
    

Ответ 2

Хорошей практикой является инициализация всех переменных.

Ответ 3

Вы не можете сохранить строку в указателе.

Ваше определение mgt_dev_name является хорошим, но вам нужно указать его где-нибудь с пространством для вашей строки. Либо malloc(), это пространство, либо использовать ранее определенный массив символов.

char *mgt_dev_name = NULL;
char data[4200];

/* ... */

mgt_dev_name = data; /* use array */

/* ... */

mgt_dev_name = malloc(4200);
if (mgt_dev_name != NULL) {
    /* use malloc'd space */
    free(mgt_dev_name);
} else {
    /* error: not enough memory */
}

Ответ 4

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

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

Ответ 5

Другая опция - не определять переменную до места в коде, где у вас есть доступ к ее начальному значению. Итак, вместо этого:

char *name = NULL;

...

name = initial_value;

Я бы изменил это на:

...

char *name = initial_value;

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

Тем не менее, это разрешено только начиная с стандарта c99 (это также действительный С++). Чтобы включить функции c99 в gcc, вам нужно либо сделать:

gcc -std=gnu99

или если вы не хотите расширения gcc для стандарта:

gcc -std=c99

Ответ 6

Нет, это не очень хорошая практика, если я правильно понял ваш контекст.

Если ваш код на самом деле зависит от mgt_dev_name, имеющего начальное значение нулевого указателя, то, конечно, включение инициализатора в объявление является очень хорошей идеей. То есть если вам все равно придется это делать

char *mgt_dev_name;

/* ... and soon after */
mgt_dev_name = NULL;

тогда всегда лучше использовать инициализацию вместо назначения

char *mgt_dev_name = NULL;

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

В C89/90, с другой стороны, объявления могут быть размещены только в начале блока. В этот момент, в общем случае, у вас не будет значимых инициализаторов для всех ваших объектов. Если вы просто инициализируете их чем-то, что угодно (например, 0 или NULL), чтобы инициализировать их? Нет!!! Никогда не делайте бессмысленных вещей в своем коде. Это ничего не улучшит, независимо от того, что могут сказать вам различные "руководства по стилю". В действительности, бессмысленная инициализация может фактически покрыть ошибки в вашем коде, что затрудняет их обнаружение и исправление.

Обратите внимание, что даже в C89/90 всегда полезно стремиться к лучшей локализации деклараций. То есть хорошо известное руководство по эффективной практике: всегда делайте свои переменные такими же локальными, какими они могут быть. Не накапливайте все свои объявления локальных объектов в самом начале функции, а скорее переносите их в начало самого маленького блока, который максимально охватывает весь срок службы объекта. Иногда даже может быть хорошей идеей ввести фиктивный, в противном случае ненужный блок, чтобы улучшить локальность деклараций. Следуя этой практике, вы сможете предоставить полезные полезные инициализаторы для ваших объектов во многих случаях (если не в большинстве). Но некоторые объекты останутся неинициализированными в C89/90 только потому, что у вас не будет хорошего инициализатора для них в точке объявления. Не пытайтесь инициализировать их "чем-то" только ради их инициализации. Это не достигнет абсолютно ничего хорошего и может иметь негативные последствия.

Обратите внимание, что некоторые современные средства разработки (например, MS Visual Studio 2005), например, будут захватывать доступ к неинициализированным переменным в отладочной версии кода. Эти инструменты могут помочь вам обнаружить ситуации, когда вы обращаетесь к переменной до того, как у нее появилась возможность получить значимое значение, указывая на ошибку в коде. Но, выполняя безусловную преждевременную инициализацию ваших переменных, вы по существу убиваете эту возможность инструмента и подметаете эти ошибки под ковер.

Ответ 8

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

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

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

struct foo {
    int counter;
    unsigned char ch;
    char *context;
};

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

Thread A получает блокировку на foo, увеличивает счетчик и проверяет значение в ch. Он не находит его, поэтому он не выделяет (или не изменяет) контекст. Вместо этого он сохраняет значение в ch, чтобы поток B мог выполнять эту работу.

Thread B Видит, что счетчик был увеличен, отмечает значение в ch, но не уверен, что поток A сделал что-либо с контекстом. Если контекст был инициализирован как NULL, потоку B больше не нужно заботиться о том, что сделал поток A, он знает, что контекст безопасен для разыменования (если не NULL) или выделяет (если NULL) без утечки.

Thread B выполняет свою деятельность, поток A считывает его контекст, освобождает его, а затем повторно инициализирует его до NULL.

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

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

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

void my_free(void **p)
{
    if (*p != NULL) {
        free(*p);
        *p = NULL;
    }
}

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

Вероятно, гораздо больше информации, чем вы хотели.

Ответ 9

Предпочтительные стили:

в C: char * c = NULL;

в С++: char * c = 0;

Ответ 10

Мое логическое обоснование заключается в том, что если вы не инициализируетесь с помощью NULL, а затем забываете инициализировать в целом, типы ошибок, которые вы получите в коде, когда разыменование гораздо труднее отслеживать из-за потенциального мусора, содержащегося в памяти в этой точке. С другой стороны, если вы выполняете инициализацию до NULL, большую часть времени вы получите только segmentation fault, что лучше, учитывая альтернативу.

Ответ 11

Инициализация переменных, даже если вы не нуждаетесь в их инициализации сразу, является хорошей практикой. Обычно мы инициализируем указатели на NULL, int до 0 и плаваем до 0.0 в качестве условного обозначения.

int* ptr = NULL;
int i = 0;
float r = 0.0;

Ответ 12

Всегда хорошо инициализировать переменные-указатели в C++, как показано ниже:

int *iPtr = nullptr;
char *cPtr = nullptr;

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

if(iPtr){
   //then do something.
}

if(cPtr){
  //then do something.
}