У меня есть указатель char, который будет использоваться для хранения строки. Он используется позже в программе.
Я объявил и инициализировал это следующим образом:
char * p = NULL;
Мне просто интересно, хорошо ли это. Я использую gcc 4.3.3.
У меня есть указатель char, который будет использоваться для хранения строки. Он используется позже в программе.
Я объявил и инициализировал это следующим образом:
char * p = NULL;
Мне просто интересно, хорошо ли это. Я использую gcc 4.3.3.
Да, это хорошая идея. Стиль Google Code рекомендует:
Инициализировать указатели на NULL
, int
на 0 и float
на 0,0 - только для лучшей читаемости.
int i = 0;
double x = 0.0;
char* c = NULL;
Хорошей практикой является инициализация всех переменных.
Вы не можете сохранить строку в указателе.
Ваше определение 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 */
}
Если вы спрашиваете, нужно ли это, или стоит ли инициализировать переменную до NULL
, прежде чем вы установите ее на что-то еще позже: не нужно инициализировать ее до NULL
, она выиграла ' t не имеет значения для функциональности вашей программы.
Обратите внимание, что в программировании важно понимать каждую строку кода - почему она там и что именно она делает. Не делайте ничего, не зная, что они означают или не понимают, почему вы их делаете.
Другая опция - не определять переменную до места в коде, где у вас есть доступ к ее начальному значению. Итак, вместо этого:
char *name = NULL;
...
name = initial_value;
Я бы изменил это на:
...
char *name = initial_value;
Затем компилятор помешает вам ссылаться на переменную в той части кода, где она не имеет значения. В зависимости от специфики вашего кода это может быть не всегда возможно (например, начальное значение задается во внутренней области, но переменная имеет другое время жизни), перемещение определения как можно позже в коде предотвращает ошибки.
Тем не менее, это разрешено только начиная с стандарта c99 (это также действительный С++). Чтобы включить функции c99 в gcc, вам нужно либо сделать:
gcc -std=gnu99
или если вы не хотите расширения gcc для стандарта:
gcc -std=c99
Нет, это не очень хорошая практика, если я правильно понял ваш контекст.
Если ваш код на самом деле зависит от 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), например, будут захватывать доступ к неинициализированным переменным в отладочной версии кода. Эти инструменты могут помочь вам обнаружить ситуации, когда вы обращаетесь к переменной до того, как у нее появилась возможность получить значимое значение, указывая на ошибку в коде. Но, выполняя безусловную преждевременную инициализацию ваших переменных, вы по существу убиваете эту возможность инструмента и подметаете эти ошибки под ковер.
Этот раздел уже обсуждался здесь:
http://www.velocityreviews.com/forums/t282290-how-to-initialize-a-char.html
Это относится к С++, но это может быть полезно и вам.
Есть несколько хороших ответов на этот вопрос, один из них принят. Я собираюсь ответить в любом случае, чтобы расширить практичность.
Да, это хорошая практика, чтобы инициализировать указатели до 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() еще более опасным, потому что у абонентов будет некоторое заблуждение безопасности. Вы не можете полагаться на "оптовую" практику, если не уверены, что каждая операция согласна.
Вероятно, гораздо больше информации, чем вы хотели.
Предпочтительные стили:
в C: char * c = NULL;
в С++: char * c = 0;
Мое логическое обоснование заключается в том, что если вы не инициализируетесь с помощью NULL
, а затем забываете инициализировать в целом, типы ошибок, которые вы получите в коде, когда разыменование гораздо труднее отслеживать из-за потенциального мусора, содержащегося в памяти в этой точке. С другой стороны, если вы выполняете инициализацию до NULL
, большую часть времени вы получите только segmentation fault
, что лучше, учитывая альтернативу.
Инициализация переменных, даже если вы не нуждаетесь в их инициализации сразу, является хорошей практикой. Обычно мы инициализируем указатели на NULL, int до 0 и плаваем до 0.0 в качестве условного обозначения.
int* ptr = NULL;
int i = 0;
float r = 0.0;
Всегда хорошо инициализировать переменные-указатели в C++, как показано ниже:
int *iPtr = nullptr;
char *cPtr = nullptr;
Поскольку инициализация, как указано выше, поможет в условиях, подобных приведенным ниже, поскольку nullptr может быть преобразован в bool, в противном случае ваш код будет выдавать некоторые предупреждения компиляции или неопределенное поведение:
if(iPtr){
//then do something.
}
if(cPtr){
//then do something.
}