if (strcmp(s1,s2)==0) { /* хранят ли строки s1 и s2 одни и те же
символы? */
}
Функция strcmp
может дать три разных ответа. При заданных выше значениях s1
и s2
функция strcmp(s1,s2)
вернет нуль, что означает полное совпадение. Если строка s1
предшествует строке s2
в соответствии с лексикографическим порядком, то она вернет отрицательное число, и если строка s1
следует за строкой s2
в лексикографическом порядке, то она вернет положительное число. Термин
strcmp("dog","dog")==0
strcmp("ape","dodo")<0 /* "ape" предшествует "dodo" в словаре */
strcmp("pig","cow")>0 /* "pig" следует после "cow" в словаре */
Результат сравнения указателей s1==s2
не обязательно равен 0 (false
). Механизм реализации языка может использовать для хранения всех строковых литералов одну и ту же область памяти, поэтому можем получить ответ 1 (true
). Обычно функция strcmp
хорошо справляется со сравнением С-строк.
Длину С-строки можно найти с помощью функции strlen
.
int lgt = strlen(s1);
Обратите внимание на то, что функция strlen
подсчитывает символы, не учитывая завершающий нуль. В данном случае strlen(s1)==4
, а строка "asdf
" занимает в памяти пять байтов. Эта небольшая разница является источником многих ошибок при подсчетах.
Мы можем копировать одну С-строку (включая завершающий нуль) в другую.
strcpy(s1,s2); /* копируем символы из s2 в s1 */
Программист должен сам гарантировать, что целевая строка (массив) имеет достаточный размер, чтобы в ней поместились символы исходной строки.
Функции strncpy
, strncat
и strncmp
являются версиями функций strcpy
, strcat
и strcmp
, учитывающими не больше n
символов, где параметр n
задается как третий аргумент. Обратите внимание на то, что если в исходной строке больше n символов, то функция strncpy
не будет копировать завершающий нуль, поэтому результат копирования не будет корректной С-строкой. Функции strchr
и strstr
находят свой второй аргумент в строке, являющейся их первым аргументом, и возвращают указатель на первый символ совпадения. Как и функция find
, они выполняют поиск символа в строке слева направо. Удивительно, как много можно сделать с этими простыми функциями и как легко при этом допустить незаметные ошибки. Рассмотрим простую задачу: конкатенировать имя пользователя с его адресом, поместив между ними символ @. С помощью класса std::string
это можно сделать так:
string s = id + '@' + addr;
С помощью стандартных функций для работы с С-строками этот код можно написать следующим образом:
char* cat(const char* id, const char* addr)
{
int sz = strlen(id)+strlen(addr)+2;
char* res = (char*) malloc(sz);
strcpy(res,id);
res[strlen(id)+1] = '@';
strcpy(res+strlen(id)+2,addr);
res[sz–1]=0;
return res;
}
Правильный ли ответ мы получили? Кто вызовет функцию free
для строки, которую вернула функция cat
?
ПОПРОБУЙТЕ
Протестируйте функцию cat
. Почему в первой инструкции мы добавляем число 2? Мы сделали глупую ошибку в функции cat
, найдите и устраните ее. Мы “забыли” прокомментировать код. Добавьте соответствующие комментарии, предполагая, что читатель знает стандартные функции для работы с С-строками.
27.5.1. Строки в стиле языка С и ключевое слово const
Рассмотрим следующий пример:
char* p = "asdf";
p[2] = 'x';
[2]='x'
(который пытается превратить исходную строку в строку "asxf") является недопустимым. К сожалению, некоторые компиляторы пропускают присваивание указателю p
, что приводит к проблемам. Если вам повезет, то произойдет ошибка на этапе выполнения программы, но рассчитывать на это не стоит. Вместо этого следует писать так:
const char* p = "asdf"; // теперь вы не сможете записать символ
// в строку "asdf" с помощью указателя p
Эта рекомендация относится как к языку C, так и к языку C++.
Функция strchr
из языка C порождает аналогичную, но более трудноуловимую проблему. Рассмотрим пример.
char* strchr(const char* s,int c); /* найти c в константной строке s
(
не C++) */