У меня есть указатель char, который будет использоваться для хранения строки. Он используется позже в программе.
Я объявил и инициализировал так:
char * p = NULL;
Мне просто интересно, является ли это хорошей практикой. Я использую gcc 4.3.3.
У меня есть указатель char, который будет использоваться для хранения строки. Он используется позже в программе.
Я объявил и инициализировал так:
char * p = NULL;
Мне просто интересно, является ли это хорошей практикой. Я использую gcc 4.3.3.
Да, это хорошая идея. Google Code Style рекомендует:
Инициализируйте указатели NULL
, int
0 и float
0.0 - просто для лучшей читабельности.
int i = 0;
double x = 0.0;
char* c = NULL;
0
(в отличие от 0.0
).
- person AnT; 03.11.2009
0.0
не улучшает читаемость и вводит избыточную зависимость типа между левой и правой частями инициализации. Использование NULL
для указателей может быть допустимо, но во всех других случаях рекомендуется использовать простой 0
для инициализации всего, что может быть инициализировано с его помощью. По-видимому, Google Code Style более произволен, чем я думал.
- person AnT; 03.11.2009
Рекомендуется инициализировать все переменные.
Вы не можете хранить строку в указателе.
Ваше определение 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
, это не будет иметь никакого значения для функциональности. вашей программы.
Обратите внимание, что в программировании важно понимать каждую строку кода — зачем она здесь и что именно она делает. Не делайте вещей, не зная, что они означают, или не понимая, почему вы это делаете.
char *c; c = "hello, world";
. Так же и следующее: char *c = malloc(13); free(c); /* c points to memory I don't own! */ c = NULL;
- person Steve Jessop; 02.11.2009
Другой вариант - не определять переменную до того места в вашем коде, где у вас есть доступ к ее начальному значению. Поэтому вместо того, чтобы делать:
char *name = NULL;
...
name = initial_value;
Я бы изменил это на:
...
char *name = initial_value;
Затем компилятор не позволит вам ссылаться на переменную в той части кода, где она не имеет значения. В зависимости от специфики вашего кода это не всегда возможно (например, начальное значение установлено во внутренней области, но переменная имеет другое время жизни), перемещение определения как можно позже в коде предотвращает ошибки.
Тем не менее, это разрешено только начиная со стандарта c99 (это также допустимо для C++). Чтобы включить функции 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 и C++. К тому времени, когда вам понадобится ваш объект, вы обычно уже знаете подходящий инициализатор для этого объекта и поэтому можете легко придумать элегантное объявление с хорошим инициализатором.
С другой стороны, в C89/90 объявления могут быть размещены только в начале блока. В этот момент, в общем случае, у вас не будет значимых инициализаторов для всех ваших объектов. Должны ли вы просто инициализировать их чем-то, чем угодно (например, 0
или NULL
), просто чтобы они инициализировались? Нет!!! Никогда не делайте бессмысленных вещей в своем коде. Это ничего не улучшит, что бы вам ни говорили различные «гайды по стилю». На самом деле бессмысленная инициализация может скрывать ошибки в вашем коде, что затрудняет их обнаружение и исправление.
Обратите внимание, что даже в C89/90 всегда полезно стремиться к лучшей локальности объявлений. т.е. известное руководство по хорошей практике гласит: всегда делайте свои переменные настолько локальными, насколько это возможно. Не накапливайте все объявления локальных объектов в самом начале функции, а переместите их в начало наименьшего блока, максимально плотно охватывающего все время жизни объекта. Иногда может быть даже хорошей идеей ввести фиктивный, в противном случае ненужный блок, просто для улучшения локальности объявлений. Следование этой практике поможет вам предоставить хорошие полезные инициализаторы для ваших объектов во многих (если не в большинстве) случаях. Но некоторые объекты останутся неинициализированными в C89/90 только потому, что у вас не будет хорошего инициализатора для них в момент объявления. Не пытайтесь инициализировать их «чем-то» только ради того, чтобы они были инициализированы. Это не принесет абсолютно ничего хорошего и может иметь негативные последствия.
Обратите внимание, что некоторые современные средства разработки (например, MS Visual Studio 2005) перехватывают доступ во время выполнения к неинициализированным переменным в отладочной версии кода. То есть эти инструменты могут помочь вам обнаружить ситуации, когда вы обращаетесь к переменной до того, как она успела получить значимое значение, указывающее на ошибку в коде. Но выполняя безусловную преждевременную инициализацию ваших переменных, вы, по сути, убиваете эту возможность инструмента и заметаете эти ошибки под ковер.
Эта тема уже обсуждалась здесь:
http://www.velocityreviews.com/forums/t282290-how-to-initialize-a-char.html
Он относится к C++, но может быть полезен и вам.
На этот вопрос есть несколько хороших ответов, один из них был принят. Я все равно отвечу, чтобы расширить практические аспекты.
Да, хорошей практикой является инициализация указателей в NULL, а также установка указателей в NULL после того, как они больше не нужны (т.е. освобождены).
В любом случае очень практично иметь возможность проверить указатель до его разыменования. Допустим, у вас есть структура, которая выглядит так:
struct foo {
int counter;
unsigned char ch;
char *context;
};
Затем вы пишете приложение, которое порождает несколько потоков, каждый из которых работает с одной выделенной структурой foo (безопасно) за счет использования взаимного исключения.
Поток A блокирует foo, увеличивает счетчик и проверяет значение в ch. Он не находит его, поэтому не выделяет (или не изменяет) контекст. Вместо этого он сохраняет значение в ch, чтобы вместо этого эту работу мог выполнить поток B.
Поток B видит, что счетчик был увеличен, отмечает значение в ch, но не уверен, сделал ли что-нибудь поток A с контекстом. Если контекст был инициализирован как NULL, поток B больше не должен заботиться о том, что сделал поток A, он знает, что контекст безопасен для разыменования (если не NULL) или выделения (если NULL) без утечки.
Поток B делает свое дело, поток A читает его контекст, освобождает его, а затем повторно инициализирует его значением NULL.
Те же рассуждения применимы к глобальным переменным без использования потоков. Хорошо иметь возможность протестировать их в различных функциях до их разыменования (или попытки выделить их, что приведет к утечке и неопределенному поведению в вашей программе).
Когда становится глупо, когда область действия указателя не выходит за пределы одной функции. Если у вас есть одна функция и вы не можете отслеживать указатели внутри нее, обычно это означает, что функция должна быть реорганизована. Однако нет ничего плохого в том, чтобы инициализировать указатель в одной функции, хотя бы для сохранения единообразных привычек.
Единственный раз, когда я когда-либо видел «уродливый» случай использования инициализированного указателя (до и после использования), это примерно так:
void my_free(void **p)
{
if (*p != NULL) {
free(*p);
*p = NULL;
}
}
Мало того, что разыменование указателя типа с каламбуром не одобряется на строгих платформах, приведенный выше код делает free() еще более опасным, потому что у вызывающих будет некоторое заблуждение относительно безопасности. Вы не можете полагаться на «оптовую» практику, если не уверены, что каждая операция согласована.
Вероятно, гораздо больше информации, чем вы на самом деле хотели.
Предпочтительные стили:
in C: char * c = NULL;
in C++: 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.
}
The macro NULL, defined in any of <clocale>, <cstddef>, <cstdio>, <cstdlib>, <cstring>, <ctime>, or <cwchar>, is an implementation-defined C++ null pointer constant in this International Standard
. Я бы определенно придерживался NULL, кроме того, он более ориентирован на типы и более информативен в исходном коде, чем 0. - person RedGlyph   schedule 02.11.2009int i = NULL;
). - person Steve Jessop   schedule 02.11.2009<clocale>
и т.д. - person MSalters   schedule 02.11.2009