Правильный способ инициализации строки в C - PullRequest
12 голосов
/ 03 ноября 2010

Я видел код людей как:

char *str = NULL;

, и я видел это также,

char *str;

Интересно, как правильноинициализация строки?и когда вы должны инициализировать строку с / без NULL?

Ответы [ 11 ]

16 голосов
/ 03 ноября 2010

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

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

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

Обратите внимание, что переменным присваиваются значения по умолчанию при определенных обстоятельствах (например, если они являются статической продолжительностью хранения, такой как объявление на уровне файла, вне какой-либо функции),

Локальные переменные не имеют этой гарантии.Итак, если ваше второе объявление выше (char *str;) находится внутри функции, оно может содержать мусор, и попытка его использования вызовет вышеупомянутое, страшное, неопределенное поведение.

Соответствующая частьстандарт C99 6.7.8/10:

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

  • , если он имеет тип указателя, он инициализируется нулевым указателем;
  • , если он имеет арифметический тип,он инициализируется нулевым (положительным или без знака);
  • , если он является агрегатом, каждый элемент инициализируется (рекурсивно) в соответствии с этими правилами;
  • , если это объединение, первое имяэлемент инициализируется (рекурсивно) в соответствии с этими правилами.
5 голосов
/ 03 ноября 2010
Интересно, как правильно инициализировать строку?

Ну, так как второй фрагмент определяет неинициализированный указатель на строку, я бы сказал, первый,:)

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

Однако не следует путать указатель NULL на строку с пустой строкой: указатель NULL на строку означает, что этот указатель не указывает ни на что, а пустая строка является «реальной».", строка нулевой длины (т.е. она содержит только символ NUL).

char * str=NULL; /* NULL pointer to string - there's 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 */
3 голосов
/ 03 ноября 2010

это общий вопрос о переменных c, а не только о символах.

Рекомендуется инициализировать переменную в точке объявления.то есть

char *str = NULL;

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

if(str != NULL)
 doBar(str);

Что произойдет.str находится в неизвестном (и почти наверняка не NULL) состоянии

Обратите внимание, что статические переменные будут инициализированы для нуля / NULL.Из вопроса не ясно, спрашиваете ли вы о местных или статиках

2 голосов
/ 03 ноября 2010

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

1 голос
/ 06 ноября 2010

Поскольку free () ничего не делает, если вы передаете ему значение NULL, вы можете упростить вашу программу следующим образом:

char *str = NULL;

if ( somethingorother() )
{
    str = malloc ( 100 );

    if ( NULL == str )
        goto error;
}

...

error:

cleanup();
free ( str );

Если дляпо какой-то причине someorother () возвращает 0, если вы не инициализировали str, вы освободите какой-нибудь случайный адрес в любом месте, который может вызвать сбой.

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

1 голос
/ 06 ноября 2010
static const char str[] = "str";

или

static char str[] = "str";
1 голос
/ 03 ноября 2010

Не инициализируйте все переменные-указатели в NULL при объявлении «на всякий случай».

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

Инициализация указателя на NULL - это не то же самое, что инициализация его на разумное значение и инициализация его значением NULL просто отключает способность компилятора сообщать вам, что вы не инициализировали его разумным значением.

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

Если вы не видите ни объявления переменной указателя, ниТочка, в которой сначала дается значение на том же экране, ваша функция слишком велика.

1 голос
/ 03 ноября 2010

Это полностью зависит от того, как вы собираетесь его использовать.В дальнейшем имеет смысл не инициализировать переменную:

int count;
while ((count = function()) > 0)
{
}
1 голос
/ 03 ноября 2010

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

char *str = NULL;

также потому, что

char *str;

это будет просто нераспределенный указатель куда-то, что в большинстве случаев будет вызывать проблемы при использовании, если вы забудете выделить его, вам нужно будет выделить его ЛЮБОЙ (или скопировать другой указатель).

Это означает, что вы можете выбрать:

  • если вы знаете, что выделите его вскоре после его объявления, вы можете не устанавливать его как NULL (это своего рода правило для большого пальца)
  • в любом другом случае, если вы хотите быть уверенным, просто сделайте это. Единственная реальная проблема возникает, если вы пытаетесь использовать его, не инициализировав его.
0 голосов
/ 03 ноября 2010

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

Во-первых, обратите внимание, что строки в C не похожи на строки в других языках.

Они являются указателями на блок символов.Конец которого заканчивается 0-байтовым или нулевым терминатором.следовательно, строка с нулевым символом в конце.

Так, например, если вы собираетесь сделать что-то вроде этого:

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's strlcpy & strlcat.Или strcpy_s & strcat_s (windows).

Многие функции ожидают, что вы также передадите правильный адрес.Итак, еще раз, имейте в виду, что

char* str = NULL;
strcmp(str, "Hello World");

будет аварийно завершаться, потому что strcmp не нравится, когда передается NULL.

Вы пометили это как C, но если кто-то использует C ++ ичитает этот вопрос, затем переключается на использование std :: string, где это возможно, и использует функцию-член .c_str () для строки, где вам нужно взаимодействовать с API, который требует стандартной строки c с нулевым символом в конце.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...