Вопросы по C-струнам - PullRequest
6 голосов
/ 30 июня 2010

Я новичок в C, и меня очень смущают строки C. Ниже приведены мои вопросы.

Поиск последнего символа из строки

Как я могу узнать последний символ в строке? Я пришел с чем-то вроде

char *str = "hello";
printf("%c", str[strlen(str) - 1]);
return 0;

Это путь? Я почему-то думаю, что это не правильный путь, потому что strlen должен перебирать символы, чтобы получить длину. Таким образом, эта операция будет иметь сложность O(n).

Преобразование char в char*

У меня есть строка, и мне нужно добавить к ней символ. Как я могу это сделать? strcat принимает только char*. Я попробовал следующее,

char delimiter = ',';
char text[6];
strcpy(text, "hello");
strcat(text, delimiter);

Использование strcat с переменными, имеющими локальную область действия

Пожалуйста, рассмотрите следующий код,

void foo(char *output)
{
   char *delimiter = ',';
   strcpy(output, "hello");
   strcat(output, delimiter);
}

В приведенном выше коде delimiter - это локальная переменная, которая уничтожается после возврата foo. Можно ли добавить его к переменной output?

Как strcat обрабатывает нулевой завершающий символ?

Если я объединяю две строки с нулевым символом в конце, strcat добавляет два символа с нулевым символом в конце к результирующей строке?

Есть ли хорошая статья для начинающих, которая объясняет, как работают строки в C и как я могу выполнять обычные манипуляции со строками?

Любая помощь будет отличной!

Ответы [ 7 ]

7 голосов
/ 30 июня 2010
  1. Последний символ: ваш подход правильный. Если вам нужно много делать с большими строками, ваша структура данных, содержащая строки, должна хранить длины вместе с ними. Если нет, то не имеет значения, что это O (n).

  2. Добавление символа: у вас есть несколько ошибок. Во-первых, ваш буфер слишком мал, чтобы содержать другой символ. Что касается того, как вызывать strcat, вы можете либо поместить символ в строку (массив с 2 записями, второй - 0), либо вы можете просто вручную использовать длину для записи символа в конец. *

  3. Ваше беспокойство по поводу двух нулевых терминаторов необоснованно. Хотя он занимает память, смежную со строкой, и является необходимым, нулевой байт в конце НЕ является «частью строки» в смысле длины и т. Д. Это просто маркер конца. strcat перезапишет старый nul и поместит новый в самый конец после конкатенированной строки. Опять же, вы должны убедиться, что ваш буфер достаточно велик, прежде чем вызывать strcat!

5 голосов
/ 30 июня 2010
  1. O (n) - лучшее, что вы можете сделать из-за того, как работают строки C.
  2. char delimiter[] = ",";.Это делает разделитель символьным массивом, содержащим запятую и NUL. Кроме того, текст должен иметь длину 7. Привет 5, тогда у вас есть запятая и NUL.
  3. Если вы правильно определите разделитель, это нормально (как есть, вы назначаете символ указателю, что неправильно).Содержимое вывода не будет зависеть от разделителя в дальнейшем.
  4. Он перезапишет первый NUL.

Вы на правильном пути.Я настоятельно рекомендую вам прочитать K & R C 2nd Edition.Это поможет вам со строками, указателями и многим другим.И не забывайте справочные страницы и документацию.Они ответят на вопросы наподобие вопроса strcat довольно четко.Два хороших сайта: The Open Group и cplusplus.com.

3 голосов
/ 30 июня 2010

«Строка C» на самом деле представляет собой простой массив из char s, где str[0] содержит первый символ, str[1] второй и так далее. После последнего символа массив содержит еще один элемент , который содержит ноль. Этот ноль по соглашению означает конец строки. Например, эти две строки эквивалентны:

char str[] = "foo"; //str is 4 bytes
char str[] = {'f', 'o', 'o', 0};

А теперь на ваши вопросы:

Поиск последнего символа из строки

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

Преобразование символа в символ *

Как уже было сказано, "строка" - это просто массив char с, с добавленным нулем в конце. Поэтому, если вам нужна строка из одного символа, вы объявляете массив из two char s - ваш символ и конечный ноль, например:

char str[2];
str[0] = ',';
str[1] = 0;

Или просто:

char str[2] = {',', 0};

Использование strcat с переменными, имеющими локальную область видимости

strcat() просто копирует содержимое исходного массива в целевой массив со смещением нулевого символа в целевом массиве. Так что не имеет значения, что происходит с источником после операции. Но вам DO нужно беспокоиться, если целевой массив достаточно велик для хранения данных - в противном случае strcat() перезапишет все данные, которые находятся в памяти, сразу после массива! Необходимый размер strlen(str1) + strlen(str2) + 1.

Как strcat обрабатывает нулевой завершающий символ?

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

1 голос
/ 01 июля 2010

Мне почему-то кажется, что это не правильный путь, потому что strlen должен перебирать символы, чтобы получить длину. Таким образом, эта операция будет иметь сложность O (n).

Вы правы, прочитав Джоэла Спольски на , почему C-струны отстой . Есть несколько способов обойти это. Способы включают в себя либо не использовать строки C (например, использовать строки Pascal и создать собственную библиотеку для их обработки), либо не использовать C (например, использовать C ++, у которого есть строковый класс - что медленно по разным причинам, но вы также можете написать самостоятельно обрабатывать строки на Паскале, например, в Си)

Относительно добавления символа в строку C; строка C - это просто массив символов с нулевым терминатором, если вы сохраняете терминатор, это строка, никакой магии нет.

char* straddch( char* str, char ch )
{
    char* end = &str[strlen(str)] ;
    *end = ch ;
    end++ ;
    *end = 0 ;
    return str ;
}

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

Если я объединяю два нуля прерванные строки, добавит strcat два нулевых завершающих символа в результирующая строка?

Нет, только один, но то, что следует, может случиться так, что это просто nul или что-то, что случилось в памяти. Рассмотрим следующий эквивалент:

char* my_strcat( char* s1, const char* s2 )
{
    strcpy( &str[strlen(str)], s2 ) ;
}

первый символ s2 перезаписывает терминатор в s1.

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

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

1 голос
/ 30 июня 2010

Как узнать последний символ из строки?

Ваш подход почти правильный.Единственный способ найти конец строки C - это перебирать символы в поисках nul.

В вашем ответе есть ошибка (в общем случае).Если strlen (str) равен нулю, вы получаете доступ к символу до начала строки.

У меня есть строка и мне нужно добавить к ней символ.Как я могу это сделать?

Ваш подход неверен.Строка AC - это просто массив символов C, последний из которых '\0'.Таким образом, теоретически вы можете добавить такой символ:

char delimiter = ',';
char text[7];
strcpy(text, "hello");
int textSize = strlen(text);
text[textSize] = delimiter;
text[textSize + 1] = '\0';

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

... delimiterявляется локальной переменной, которая уничтожается после возврата fooМожно ли добавить его к выводу переменной?

Да, все в порядке.strcat копирует символы.Но ваш пример кода не проверяет, что вывод достаточно велик для всего, что вы вкладываете в него.

Если я объединяю две строки с нулевым символом в конце, добавит ли strcat два символа с нулевым символом в конце к результирующей строке?

Нет.

1 голос
/ 30 июня 2010

Как узнать последний символ из строки?

Ваша техника с str[strlen(str) - 1] в порядке.Как уже указывалось, вам следует избегать повторных ненужных вызовов strlen и сохранять результаты.

Мне почему-то кажется, что это неправильный способ, потому что strlen приходится перебирать символы, чтобы получить длину,Таким образом, эта операция будет иметь сложность O (n).

Повторные вызовы strlen могут быть проблемой программ на Си.Однако следует избегать преждевременной оптимизации.Если профилировщик на самом деле демонстрирует точку доступа, где strlen стоит дорого, то вы можете сделать что-то подобное для своего случая с литеральной строкой:

const char test[] = "foo";
sizeof test // 4

Конечно, если вы создадите 'test' наДля стека это приводит к небольшим накладным расходам (увеличение / уменьшение указателя стека), но не требует линейной временной операции.

Литеральные строки, как правило, не будут такими гигантскими.В других случаях, таких как чтение большой строки из файла, вы можете заранее сохранить длину строки как один из примеров, чтобы избежать пересчета длины строки.Это также может быть полезно, так как заранее сообщит вам, сколько памяти выделить для вашего буфера символов.

У меня есть строка, и мне нужно добавить к ней символ.Как я могу это сделать?strcat принимает только char *.

Если у вас есть char и вы не можете сделать из него строку (char * c = "a"), то я считаю, что вы можете использовать strncat (необходима проверка на этом):

char ch = 'a';
strncat(str, &ch, 1);

В приведенном выше коде разделитель - это локальная переменная, которая уничтожается после возврата foo.Можно ли добавить его к выводу переменной?

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

Если я объединяю две строки с нулевым символом в конце, strcat добавит два символа с нулевым символом концав результирующую строку?

Нет, strcat в основном перезапишет нулевой терминатор в строке dest и запишет после него, а затем добавит новый нулевой терминатор, когда он будет завершен.

1 голос
/ 30 июня 2010

Поиск последнего символа из строки

Я предлагаю мысленный эксперимент: если бы вообще было возможно найти последний символ строки за время, превышающее O (n), тоне могли бы вы также реализовать strlen в течение времени, превышающего O (n)?

Преобразование char в char*

Вы можете временно сохранить char в массиве из char, и он превратится в указатель на char:

char delimiterBuf[2] = "";
delimiterBuf[0] = delimiter;
...
strcat(text, delimiterBuf);

Если вы просто используете символьные литералы, вы можете просто использовать строкувместо литералов.

Использование strcat с переменными, имеющими локальную область действия

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

Как strcat обрабатывает нулевой завершающий символ?

"Строки"в C - NUL-концевые последовательности символов.Оба входа в strcat должны быть завершены NUL, а результат будет завершен NUL.Для strcat было бы бесполезно записывать дополнительный NUL-байт в результат, если в этом нет необходимости.

(И если вам интересно, что, если входные строки имеют несколько завершающих NULУже байт, я предлагаю еще один мысленный эксперимент: как бы strcat узнал, сколько концевых NUL-байтов в строке?)

Кстати, так как вы пометили это как "best-Practices", ятакже рекомендуем вам позаботиться о том, чтобы не записывать за пределы буфера назначения.Как правило, это означает, что нужно избегать strcat и strcpy (если вы уже не проверили, что входные строки не переполняют место назначения) и использовать более безопасные версии (например, strncat. Обратите внимание, что strncpy имеет свои собственные подводные камни, поэтомуэто плохая замена. Существуют также более безопасные версии, которые нестандартны, такие как strlcpy / strlcat и strcpy_s / strcat_s.)

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

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