Я видел людей как:
char *str = NULL;
и я видел это также,
char *str;
Мне интересно, каков правильный способ инициализации строки? и когда вы должны инициализировать строку w/и w/out NULL?
Я видел людей как:
char *str = NULL;
и я видел это также,
char *str;
Мне интересно, каков правильный способ инициализации строки? и когда вы должны инициализировать строку w/и w/out NULL?
Вы должны установить его перед его использованием. Это единственное правило, которое вы должны соблюдать, чтобы избежать поведения undefined. Независимо от того, инициализируете ли вы его во время создания или назначаете ему непосредственно перед использованием, это не имеет значения.
Лично я предпочитаю, чтобы никогда не были установлены переменные на неизвестные значения, поэтому я обычно делаю первый, если он не будет установлен в непосредственной близости (в нескольких строках).
Фактически, с C99, где вам больше не нужно объявлять локали в верхних частях блоков, я, как правило, откладываю его создание до тех пор, пока оно не понадобится, и в этот момент оно также может быть инициализировано.
Обратите внимание, что переменным присваиваются значения по умолчанию при определенных обстоятельствах (например, если они являются статическими длительностью хранения, например, объявляются на уровне файла, вне любой функции).
Локальные переменные не имеют этой гарантии. Итак, если ваше второе объявление выше (char *str;
) находится внутри функции, у него может быть мусор, и попытка его использования вызовет вышеупомянутое, страшное поведение undefined.
Соответствующая часть стандарта C99 6.7.8/10
:
Если объект с автоматической продолжительностью хранения не инициализируется явно, его значение неопределенно. Если объект, который имеет статическую продолжительность хранения, не инициализируется явно, то:
- если он имеет тип указателя, он инициализируется нулевым указателем;
- если он имеет арифметический тип, он инициализируется нулем (положительным или без знака);
- если он является агрегатом, каждый член инициализируется (рекурсивно) в соответствии с этими правилами;
- Если это объединение, первый именованный элемент инициализируется (рекурсивно) в соответствии с этими правилами.
это общий вопрос о переменных c не только char ptrs.
Считается лучшей практикой для инициализации переменной в точке объявления. то есть
char *str = NULL;
- хорошая вещь. В этом случае у вас никогда не будет переменных с неизвестными значениями. Например, если позже в коде вы делаете
if(str != NULL)
doBar(str);
Что произойдет. str находится в неизвестном (и почти наверняка не NULL) состоянии
Обратите внимание, что статические переменные будут инициализированы нулем /NULL для вас. Его непонятно из вопроса, если вы спрашиваете о локальных или статических
Интересно, каков правильный способ инициализации строки?
Ну, так как второй фрагмент определяет неинициализированный указатель на строку, я бы сказал первый.:)
В общем, если вы хотите играть в это безопасно, полезно инициализировать NULL
все указатели; таким образом, легко обнаружить проблемы, возникающие из неинициализированных указателей, поскольку разыменование указателя NULL
приведет к сбою (фактически, что касается стандарта, это поведение undefined, но на каждой машине, которую я видел это крушение).
Однако вы не должны путать указатель NULL
с строкой с пустой строкой: указатель NULL
на строку означает, что этот указатель указывает на ничего, а пустая строка - "реальная" строка нулевой длины (т.е. содержит только символ NUL
).
char * str=NULL; /* NULL pointer to string - there no string, just a pointer */
const char * str2 = ""; /* Pointer to a constant empty string */
char str3[] = "random text to reach 15 characters ;)"; /* String allocated (presumably on the stack) that contains some text */
*str3 = 0; /* str3 is emptied by putting a NUL in first position */
Глобальные переменные инициализируются значениями по умолчанию компилятором, но локальные переменные должны быть инициализированы.
унифицированный указатель следует рассматривать как undefined, поэтому, чтобы избежать генерации ошибок с помощью значения undefined, всегда лучше использовать
char *str = NULL;
также потому, что
char *str;
это будет просто нераспределенный указатель на то, что будет в основном вызывать проблемы при использовании, если вы забудете выделить его, вам нужно будет выделить его ANYWAY (или скопировать другой указатель).
Это означает, что вы можете выбрать:
NULL
(это своего рода правило для большого пальца)Все зависит от того, как вы собираетесь его использовать. В дальнейшем имеет смысл не инициализировать переменную:
int count;
while ((count = function()) > 0)
{
}
Не инициализируйте все свои переменные указателя NULL при объявлении "на всякий случай".
Компилятор предупредит вас, если вы попытаетесь использовать переменную-указатель, которая не была инициализирована, за исключением случаев, когда вы передаете ее по адресу функции (и вы обычно делаете это, чтобы дать ей значение).
Инициализация указателя на NULL - это не то же самое, что инициализировать его до разумного значения, а инициализация его в NULL просто отключает способность компилятора сообщать вам, что вы не инициализировали его разумным значением.
Только инициализировать указатели на NULL при объявлении, если вы получите предупреждение компилятора, если вы этого не сделаете, или вы передаете их по адресу функции, ожидающей, что они будут NULL.
Если вы не видите как объявление переменной указателя, так и точку, в которой оно впервые задано в одном и том же экране, ваша функция слишком велика.
static const char str[] = "str";
или
static char str[] = "str";
Поскольку free() ничего не делает, если вы передадите ему значение NULL, вы можете упростить свою программу следующим образом:
char *str = NULL;
if ( somethingorother() )
{
str = malloc ( 100 );
if ( NULL == str )
goto error;
}
...
error:
cleanup();
free ( str );
Если по какой-то причине somethingorother() возвращает 0, если вы не инициализировали str, вы будете освободите какой-либо случайный адрес где-нибудь, возможно, вызывая сбой.
Извиняюсь за использование goto, я знаю, что некоторые считают его оскорбительным.:)
Ваш первый фрагмент - это определение переменной с инициализацией; второй фрагмент - это определение переменной без инициализации.
Правильный способ инициализации строки - предоставить инициализатор при его определении. Инициализация его до NULL или что-то еще зависит от того, что вы хотите с ней сделать.
Также помните, что вы называете "строка". C
не имеет такого типа: обычно "строка" в контексте C
означает "массив [некоторое число] char". У вас есть указатели на char в вышеприведенных фрагментах.
Предположим, у вас есть программа, которая хочет имя пользователя в argv [1] и копирует ее в строку "name". Когда вы определяете переменную name
, вы можете сохранить ее неинициализированной или инициализировать ее до NULL (если это указатель на char) или инициализировать с именем по умолчанию.
int main(int argc, char **argv) {
char name_uninit[100];
char *name_ptr = NULL;
char name_default[100] = "anonymous";
if (argc > 1) {
strcpy(name_uninit, argv[1]); /* beware buffer overflow */
name_ptr = argv[1];
strcpy(name_default, argv[1]); /* beware buffer overflow */
}
/* ... */
/* name_uninit may be unusable (and untestable) if there were no command line parameters */
/* name_ptr may be NULL, but you can test for NULL */
/* name_default is a definite name */
}
Собственно, вы имеете в виду ошибку? хорошо, это зависит от ситуации. Но есть некоторые эмпирические правила, которые я могу порекомендовать.
Во-первых, обратите внимание, что строки в C не похожи на строки на других языках.
Они являются указателями на блок символов. Конец которого заканчивается 0 байтом или NULL-терминатором. следовательно, строка с нулевым завершением.
Так, например, если вы собираетесь сделать что-то вроде этого:
char* str;
gets(str);
или взаимодействовать с str каким-либо образом, то это монументальная ошибка. Причина в том, что, как я только что сказал, строки C не являются строками, как другие языки. Они всего лишь указатели. char * str - размер указателя и всегда будет.
Следовательно, вам нужно выделить некоторую память для хранения строки.
/* this allocates 100 characters for a string
(including the null), remember to free it with free() */
char* str = (char*)malloc(100);
str[0] = 0;
/* so does this, automatically freed when it goes out of scope */
char str[100] = "";
Однако иногда вам нужно всего лишь указатель.
например.
/* This declares the string (not intialized) */
char* str;
/* use the string from earlier and assign the allocated/copied
buffer to our variable */
str = strdup(other_string);
В общем, это действительно зависит от того, как вы ожидаете использовать указатель строки. Моя рекомендация состоит в том, чтобы использовать форму массива фиксированного размера, если вы только собираетесь использовать его в рамках этой функции, а строка относительно невелика. Или инициализируйте его NULL. Затем вы можете явно протестировать строку NULL, которая полезна, когда она передается в функцию.
Помните, что использование формы массива также может быть проблемой, если вы используете функцию, которая просто проверяет наличие NULL относительно того, где находится конец строки. например strcpy или strcat не волнует, насколько велик ваш буфер. Поэтому рассмотрите возможность использования альтернативы, такой как BSD strlcpy и strlcat. Или strcpy_s и strcat_s (windows).
Многие функции ожидают, что вы также передадите правильный адрес. Итак, помните, что
char* str = NULL;
strcmp(str, "Hello World");
приведет к сбою большого времени, потому что strcmp не любит пропускать NULL.
Вы отметили это как C, но если кто-то использует С++ и читает этот вопрос, переключитесь на использование std::string, где это возможно, и используйте функцию члена .c_str() в строке, где вам нужно взаимодействовать с API, который требует стандартную строку c с нулевым завершением.