Что такое код strcmp


Что такое strcmp (сравнение строк)?

Мой следующий код для тестирования strcmp выглядит следующим образом:

Поэтому я смущен тем, почему вывод равен 32. Что именно сравнивается с этим результатом? Я ценю ваше время и помощь.

Что бы он ни хотел. В этом случае, похоже, что значение, которое вы получаете, это ‘c’ — ‘C’ (разница между двумя символами в первой точке, где строки отличаются), что равно 32 на многих системах, но вы должны «Нет. Единственное, на что вы можете рассчитывать, это то, что возврат будет равен 0, если две строки равны, отрицательные, если s1 предшествует s2, и положительный, если s1 появляется после s2.

На страницах man указано, что результат будет больше 0 или меньше 0, если строки не совпадают. Он ничего не говорит о точном значении (если не 0).

При этом коды ASCII для c и C отличаются на 32. Вероятно, из этого результата. Однако вы не можете зависеть от того, что это поведение идентично в любых двух заданных реализациях.

Он не указан. Согласно стандарту:

Описание

Функция strcmp сравнивает строку, на которую указывает s1 с строкой, на которую указывает s2 .

Возвращает

Функция strcmp возвращает целое число больше, равное или меньше нуля, соответственно, поскольку строка, на которую указывает s1 , больше, равна или меньше строки, на которую указывает s2 .

Согласно стандарту C (N1570 7.24.4.2):

Функция strcmp возвращает целое число больше, равное или меньше нуля, соответственно, поскольку строка, на которую указывает s1 , больше, равна или меньше строки, на которую указывает s2 .

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

Сказав это, простая реализация strcmp скорее всего вернет числовую разницу в значениях первых символов, которые не совпадают. В вашем случае первыми несогласованными символами являются ‘c’ и ‘C’ , которые отличаются на 32 в ASCII.

Функция strcmp

Функция strcmp() сравнивает в лексикографическом порядке две строки и возвращает целое значение, зависящее следующим образом от результата сравнения.

Значение Результат сравнения строк
Меньше нуля str1 меньше str2
Нуль str1 равен str2
Больше нуля str1 больше str2

Пример

Следующую функцию можно использовать для проверки пароля. В случае неудачи она возвращает нуль, а при успешном выполнении — единицу.

Об одной неочевидной ошибке в реализации функции strcmp

Оказалось, что некоторые студенты при выполнении упражения по реализации библиотечной функции сравнения двух строк int strcmp ( const char *s1, const char *s2 ) забывают учесть машинное представление символьного типа в стандарте языка Си.

Итак, есть задача:

«Реализовать функцию int strcmp ( const char *s1, const char *s2 ) , которая сравнивает две строки и возвращает 0, если строки равны; значение больше нуля, если первая строка больше второй; меньше нуля — если, соответственно, меньше. Считать, что коды символов в используемой реализации Си-машины упорядочены по алфавиту».

А вот пример решения:

Здесь ошибка в случае, когда в используемой реализации поведение char как целого типа совпадает с signed char (по стандарту char обязан совпадать либо с signed char , либо с unsigned char — это определяется реализацией), и в операторе return *s1 − *s2; код символа *s1 меньше 128, а код символа *s2 больше 127. Тогда при целочисленном расширении малых знаковых целых до int (по стандарту во всех операциях малые целые расширяются до int или unsigned int ) получим вычитание из положительного числа отрицательного — результат будет положительный, что не удовлетворяет условию задачи.

Та же ошибка произойдет, если использовать фрагмент вида:

Поскольку при сравнении значений char также происходит целочисленное расширение до int (promotion), слева от > будет неотрицательное число, а справа — отрицательное.

Исправить ситуацию можно явным приведением типа:

Более того, оказывается, опубликованная в книге K&R реализация strcmp обладает таким же недостатком.

Функция strcmp(str1, str2).

После сравнения строк str1 и str2 данная функция возвратит целое значение:

Эта функция производит сравнение, различая прописные и строчные буквы

using namespace std;

using namespace std;

int k=strncmp(s1,s2, 2);

using namespace std;

int var = 123; // инициализация переменной var числом 123

int *ptrvar = &var; // указатель на переменную var (присвоили адрес переменной указателю)

Функция malloc выделяет (резервирует) из области “куча” (heap) память длиной байтов, где задается выражением типа unsigned. Функция возвращает адрес выделенной памяти в виде константы-указателя типа void.

Инициализацию указателя можно осуществить следующим образом:

int *x = (int *)malloc(sizeof(int));

Указатели и массивы. Методы ссылки на элементы массива. Привести примеры программ с использованием ссылок на языке С/C++.

В языке СИ между указателями и массивами существует тесная связь. Например, когда объявляется массив в виде int array[25], то этим определяется не только выделение памяти для двадцати пяти элементов массива, но и для указателя с именем array, значение которого равно адресу первого по счету (нулевого) элемента массива, т.е. сам массив остается безымянным, а доступ к элементам массива осуществляется через указатель с именем array. Поскольку имя массива является указателем допустимо, например, такое присваивание:

Здесь указатель ptr устанавливается на адрес первого элемента масcива.

Для доступа к элементам массива существует два различных способа. Первый способ связан с использованием обычных индексных выражений в квадратных скобках, например, array[16]=3.

Второй способ доступа к элементам массива связан с использованием адресных выражений и операции разадресации в форме *(array+16)=3. При таком способе доступа адресное выражение равное адресу шестнадцатого элемента массива тоже может быть записано разными способами *(array+16) или *(16+array).

Для доступа к начальному элементу массива (т.е. к элементу с нулевым индексом) можно использовать просто значение указателя array или ptr. Любое из присваиваний присваивает начальному элементу массива значение 2.

Пример программы с использованием ссылок на языке С/C++

using namespace std;

int main(int argc, char* argv[])

int &reference = value; // объявление и инициализация ссылки значением переменной value

using namespace std;

double summa (double array[], int n)

for(int i=0;i

— здесь мы задаем размер массива (например [n] или [25]).

Пример создания и обработки статического массива:

using namespace std;

int Arr[3] = <1,2,3>; //Инициализация массива набором значений


using namespace std;

int *arr = new int [n];

for (int i = 0; i Поскольку обе представленные функции в качестве возвращаемого значения имеют указатель на пустой тип void, требуется явное приведение типа возвращаемого значения. Для определения размера массива в байтах, используемого в качестве аргумента функции malloc()требуется количество элементов умножить на размер одного элемента. Поскольку элементами массива могут быть как данные простых типов, так и составных типов (например, структуры), для точного определения размера элемента в общем случае рекомендуется использование функции int sizeof(тип);

// Выделение памяти int * arrayPtr = (int*)malloc(n*m * sizeof(int));

Пример с calloc

int * arrayPtr = (int*) calloc(size,sizeof(int)); // выделяем память под динамический массив целых чисел

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

Пример создания, инициализации и обработки динамического массива: см. вопрос 38.

Динамические матрицы. Ввести размеры матрицы и выделить для нее место в памяти во время работы программы. Решить задачу 2 способами: выделять отдельный блок памяти для каждой строки; выделить память сразу на всю матрицу.

int **ptrarray = new int* [size1];//динамическое создание двуменого массива

Функции обработки строк в Cи

В программе строки могут определяться следующим образом:

  • как строковые константы;
  • как массивы символов;
  • через указатель на символьный тип;
  • как массивы строк.

Кроме того, должно быть предусмотрено выделение памяти для хранения строки.

Любая последовательность символов, заключенная в двойные кавычки «» , рассматривается как строковая константа .

Для корректного вывода любая строка должна заканчиваться нуль-символом ‘\0’ , целочисленное значение которого равно 0. При объявлении строковой константы нуль-символ добавляется к ней автоматически. Так, последовательность символов, представляющая собой строковую константу, будет размещена в оперативной памяти компьютера, включая нулевой байт.

Под хранение строки выделяются последовательно идущие ячейки оперативной памяти. Таким образом, строка представляет собой массив символов. Для хранения кода каждого символа строки отводится 1 байт.

Для помещения в строковую константу некоторых служебных символов используются символьные комбинации. Так, если необходимо включить в строку символ двойной кавычки, ему должен предшествовать символ «обратный слеш»: ‘\»‘ .

Строковые константы размещаются в статической памяти. Начальный адрес последовательности символов в двойных кавычках трактуется как адрес строки. Строковые константы часто используются для осуществления диалога с пользователем в таких функциях, как printf() .

При определении массива символов необходимо сообщить компилятору требуемый размер памяти.

Компилятор также может самостоятельно определить размер массива символов, если инициализация массива задана при объявлении строковой константой:

В этом случае имена m2 и m3 являются указателями на первые элементы массивов:

  • m2 эквивалентно &m2[0]
  • m2[0] эквивалентно ‘Г’
  • m2[1] эквивалентно ‘o’
  • m3 эквивалентно &m3[0]
  • m3[2] эквивалентно ‘x’

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

Для задания строки можно использовать указатель на символьный тип .

В этом случае объявление массива переменной m4 может быть присвоен адрес массива:

Здесь m3 является константой-указателем. Нельзя изменить m3 , так как это означало бы изменение положения (адреса) массива в памяти, в отличие от m4 .

Для указателя можно использовать операцию увеличения (перемещения на следующий символ):

Массивы символьных строк

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

В этом случае poet является массивом, состоящим из четырех указателей на символьные строки. Каждая строка символов представляет собой символьный массив, поэтому имеется четыре указателя на массивы. Указатель poet[0] ссылается на первую строку:
*poet[0] эквивалентно ‘П’,
*poet[l] эквивалентно ‘-‘.

Инициализация выполняется по правилам, определенным для массивов.
Тексты в кавычках эквивалентны инициализации каждой строки в массиве. Запятая разделяет соседние
последовательности.
Кроме того, можно явно задавать размер строк символов, используя описание, подобное такому:

Разница заключается в том, что такая форма задает «прямоугольный» массив, в котором все строки имеют одинаковую длину.

Функция strcmp

Читайте также:

  1. Безопасная хэш-функция
  2. Волновая функция и ее статистический смысл
  3. Волновая функция и ее статистический смысл
  4. Воспроизводственная функция финансов
  5. Деньги, их функция, происхождение и развитие.
  6. Защита прав детей как основная функция На­циональной комиссии по правам ребёнка.
  7. ИНТЕГРАЛЬНАЯ ФУНКЦИЯ РАСПРЕДЕЛЕНИЯ
  8. Информационная функция
  9. Контроль как функция управления.
  10. Контроль, как функция управления.
  11. Контроля как функция менеджмента
  12. Корреляционная функция и ее измерение

Сравнение строк

Функция strncat

Пример

Функция strcat

Конкатенация строк означает их последовательное присоединение друг к другу. Прототип функции strcat таков:

char *strcat(char *target, const char *source) ;

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

char string[81] ; strcpy(string, «Turbo»); strcat (string, » C++»);

Переменная string содержит строку «Turbo C++».

Функция strncat добавляет к содержимому целевой строки указанное количество символов из строки-источника. Прототип функции strcat :

char *strncat(char *target, const char *source, sizet num);

Функция добавляет к содержимому целевой строки num символов из строки-источника и возвращает указатель на целевую строку. char strl[81] = «Hello I am «; char str2[41] = «Keith Thompson»; strncat(strl, str2, 5);

Переменная strl теперь содержит строку «Hello I am Keith».

Пример использования функций getline, strlen и strcat в файле List7_4.cpp

(исходный код программы STRING.CPP). Программа выполняет следующие

• Предлагает вам ввести строку; ввод не должен превышать 40 символов

• Предлагает вам ввести вторую строку; ввод не должен превышать 40 символов

• Выводит число символов, содержащихся в каждой строке

• Присоединяет вторую строку к первой

• Выводит результат конкатенации

• Выводит длину объединенной строки


• Предлагает вам ввести символ для поиска

• Предлагает вам ввести символ для замены

• Выводит содержимое объединенной строки после замены символа

Поскольку строки являются массивами символов, вы не можете применить операцию сравнения для проверки равенства двух строк. Библиотека функций STRING.H предлагает набор функций для сравнения строк. Эти функции сравнивают символы двух строк, используя для этого ASCII-коды символов. Это функции strcmp, stricmp, strncmp и strnicmp.

Вообще говоря, все функции сравнения работают одинаково: возвращают 0, если две строки совпали, отрицательную величину, если вторая строка больше по величине, и положительное значение, если большей оказалась первая строка.

Функция strcmp выполняет сравнение двух строк с учетом регистра символов. Прототип функции strcmp:

int strcmp(const char *strl, const char *str2);

Функция сравнивает строки strl и str2. Возвращает в качестве результата сравнения целую величину:

0 когда strl больше, чем str2.

char stringl[] = «Borland C++»; char string2[] = «BORLAND C++»; i = strcmp(string1, string2);

В последнем операторе переменной i присваивается положительное значение, так как string1 больше string2 (ASCII-коды символов в нижнем регистре больше ASCII-кодов символов в верхнем.)

Дата добавления: 2014-01-06 ; Просмотров: 338 ; Нарушение авторских прав? ;

Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет

Что такое код strcmp

if(*(maxStr + i) I originate
You must appreciate, all the others imitate
SCOOTER «GUEST LIST»

‘Pon the mic I’m the teacher!
Spread my words like a preacher!
Yiiihhaaaa.
SCOOTER «WEEKEND»

return *ps1 FasterHarder

очень насыщена операциями.
код очень емкии. и работает превосходно.

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

всем огромное спасибо за помощь в решении проблемы.

I originate
You must appreciate, all the others imitate
SCOOTER «GUEST LIST»

‘Pon the mic I’m the teacher!
Spread my words like a preacher!
Yiiihhaaaa.
SCOOTER «WEEKEND»

I originate
You must appreciate, all the others imitate
SCOOTER «GUEST LIST»

‘Pon the mic I’m the teacher!
Spread my words like a preacher!
Yiiihhaaaa.
SCOOTER «WEEKEND»

I originate
You must appreciate, all the others imitate
SCOOTER «GUEST LIST»

‘Pon the mic I’m the teacher!
Spread my words like a preacher!
Yiiihhaaaa.
SCOOTER «WEEKEND»

а почему не у Пети??

не понял, честно говоря мысль. поясни еще как нибудь если хочешь.

Добавлено 26.08.09, 20:20

I originate
You must appreciate, all the others imitate
SCOOTER «GUEST LIST»

‘Pon the mic I’m the teacher!
Spread my words like a preacher!
Yiiihhaaaa.
SCOOTER «WEEKEND»

как понял, вариант с условием:

for(; *ps1 == *ps2; ++ps1, ++ps2)

работает только для цельных слов, т е если будет пробел, то можно получить неверныи результат, например: ps1 = «hello», ps2 = «helloa»(в этом случае строки будут равны).

По сути — аналог кода DEADHUNT

Добавлено 26.08.09, 20:28

Цитата (mes @ Сегодня, 00:12)
у Васи только

а почему не у Пети??

«у Вас», «и» — очепятка

Проверяй код в бою, с отладчиком — сразу станет понятно

Добавлено 26.08.09, 20:30
А, уже подправил . Ну это тогда ничем не отличается от кода из моего первого поста, кроме количества строк.

I originate
You must appreciate, all the others imitate
SCOOTER «GUEST LIST»

‘Pon the mic I’m the teacher!
Spread my words like a preacher!
Yiiihhaaaa.
SCOOTER «WEEKEND»

мысль намбер уан)
чем ближе к аппаратуре — тем быстрее. т.е. пишите на азме.
мысль намбер ту)
гляньте для начала как реализована библиотечная. смотрели?
мысль намбер фтри)
если вы обрабатываете чисто чары (без юникода) — то почему сравнение по байтно? Посмотрите что делает компилятор в азме. Посчитайте сколько лишних операций очисток он будет делать. Используйте максимальные возможности оси. если x32 — по 4 байта в самый раз.
мысль намбер фо)
обычно универсальность кода и скорость — два конца одной палки. Хотите скорость — выжимайте под конкретную разрядность, азм и прочее. Хотите универсальность — терпите тормоза. Есть конечно же подстройка на этапе компиляции — подумайте про неё

ну енто то что пришло на ум.
удачи Вам
(круглый)

Забытые секреты кодинга №2: строковые операции в C

«…Я обнаружил, что понимание указателей в С — это не навык, а способность. При поступлении на факультет кибернетики набирается человек 200 вундеркиндов, писавших игрушки для Atari 800 на BASIC в возрасте 4 лет. Затем они весело проводят время, изучая Паскаль, но в один прекрасный день профессор заводит речь об указателях, и внезапно они не могут этого понять… 90% потока переходит на политехнический и становится отличниками, уверяя друзей, что на информатике мало девок. На самом же деле по неизвестной причине часть человечества просто рождается без той части мозга, которая понимает указатели».

Не знаю, есть ли в коре головного мозга зона, отвечающая за понимание указателей ;-). Но сегодня я расскажу тебе о том, как работают строки в Си, и о том, как ускорить их работу. Надеюсь, что будет понятно всем.

ВОПРОСЫ ПО СТАНДАРТНЫМ ФУНКЦИЯМ

Зачем нужны специальные функции для сравнения или копирования строк? Почему в Си не пользуются обычными операторами , +, =, как в других языках?
Да, Си работает со строками совсем по-другому. Строка в Си — это указатель на символ (char). В какой-то области памяти находятся символы строки, заканчивающиеся нулевым символом ‘\0’ — на рисунке это массив символов mom. Адрес этого массива можно передавать в строковые функции, можно извлекать из массива отдельные символы (например, mom[3] — четвертый символ) — словом, это обычная строка.

В другой части памяти может находиться указатель — переменная p, которая хранит адрес определенной части строки. Все операции со строками выполняются через указатели. Например, чтобы найти первую букву ‘А’, нужно вызвать функцию strchr(), которая вернет указатель на этот символ:

p = strchr(s, ‘А’); // p будет указывать на первый символ ‘А’ в строке s

Указатели можно увеличивать и сравнивать. Например, чтобы найти следующую за ‘А’ букву, прибавим к указателю (адресу!) единицу:

p = strchr(s, ‘А’) + 1;

Если нужно узнать, какая буква расположена ближе к началу строки, ‘А’ или ‘М’, сравним указатели:

if( strchr(s, ‘А’) char str[200] = «сто»;
strcat(str, «лица»);
if(str == «столица»)
printf(«Равны!»);

Ошибка заключается в том, что str и «столица» хотя и равны, но расположены в разных участках памяти. Поэтому указатели на них не равны. В этом примере строки будут расположены в памяти примерно так:

Чтобы сравнить «содержимое» строк, нужно вызвать функцию strcmp(). Обрати внимание, что эта функция возвращает 0, когда строки равны:

char str[200] = «сто»;
strcat(str, «лица»);
if(strcmp(str, «столица») == 0)
printf(«Равны!»);

Можно ли в C работать со строками как в Паскале или Бэйсике? То есть можно ли складывать строки плюсом, автоматически выделять под них память и все такое?
Да, можно. В MFC есть класс CString, кроме него можно назвать CStr из Snippets, и еще несколько похожих разработок. Но они всегда будут работать медленнее и занимать больше места в exe’шнике, чем обычные функции Си. Обобщенные функции выделения памяти страдают избыточностью, много времени уходит на создание промежуточных строк и бесполезное копирование между ними. Сишные функции strcmp, strchr и так далее не такие уж сложные, и если ты хорошо разберешься в них, твои программы станут быстрее и короче.


Как выделять память под строки и массивы? Прежде всего, память не выделяется автоматически, как в других языках программирования. Тебе придется подсчитать, сколько символов будет занимать строка, и создать массив нужного размера. Если строка выйдет за границы отведенного для нее массива, то возникнет баг, известный как «переполнение буфера». Взломщик может использовать эту ошибку, чтобы запустить свой вредительский exploit. Поэтому нужно проверять размер строк, полученных от юзера. Например, пользователь вводит текст в контрол, а нам нужно записать этот текст большими буквами. Это делается так:

char *s; int size;
size = SendMessage(hWndCtrl, WM_GETTEXTLENGTH, 0, 0) + 1; // Узнаем размер строки
s = (char*) malloc(size); // Выделяем size байт под строку
SendMessage(hWndCtrl, WM_GETTEXT, size, (LPARAM)s); // Копируем в этот буфер строку
CharUpper(s); // Меняем регистр
SendMessage(hWndCtrl, WM_SETTEXT, 0, (LPARAM)s); // Записываем в контрол
free(s); // Освобождаем память

Выделяя память, нужно помнить о последнем нулевом символе. Он также входит в массив, так что если длина строки — 10 символов, то нужно создавать массив длиной 11 байт. Вместо сишных функций malloc, free в C++ используют операторы new, delete. Разница только в синтаксисе, а по сути это одно и то же:

char *s; int size;
s = new char[size]; // Выделяем size байт под строку
. // Выполняем нужные операции
delete[] s; // Освобождаем память

Часто максимальный размер строки известен заранее. Например, путь к файлу в Windows не может быть длиннее MAX_PATH = 260 символов. Тогда проще ограничить длину вводимой строки (послать сообщение EM_SETLIMITTEXT), а затем использовать обычный статический массив:

Функции malloc/realloc/free и операторы new/delete обращаются к Windows, запрашивая у нее память. Если вместо них ты будешь пользоваться Windows’овскими функциями HeapAlloc, HeapRealloc, HeapFree, то сможешь выбросить из exe’шника стандартную библиотеку Си, и он станет короче примерно на 20 Кб. А код для выделения памяти будет очень похожим на
malloc/free:

HANDLE heap;
heap = GetProcessHeap(); // в начале программы
s = (char*) HeapAlloc(heap, 0, size); // Выделяем size байт под строку
HeapFree(heap, 0, s); // Освобождаем память

ПРОСТЫЕ ОПЕРАЦИИ СО СТРОКАМИ

Как выделить подстроку справа, например, имя файла? Просто найди символ, с которого начинается подстрока, и пользуйся указателем на него. На рисунке указатель filename показывает на строку «C:\WORK\FV.C», а указатель p — на часть этой строки «FV.C».

p = strrchr(filename, ‘\\’) + 1; // Символ, следующий за последней обратной косой чертой

Как выделить подстроку слева, например, путь к файлу? Подставь нулевой байт в конец подстроки:

p = strrchr(filename, ‘\\’);
*p = ‘\0’; // Теперь в filename записан путь к файлу

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

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

char *p = str; int n = 0;
while(p = strchr(p, ‘А’))
p++, n++; // По окончании цикла n == число вхождений символа ‘А’ в строку str

В более удобочитаемом виде та же программа записывается следующим образом:

Почему указатели быстрее, чем индексы массивов? Программеров, которые пришли из Паскаля или Бэйсика, может немного смутить код, приведенный выше. Для них привычнее обращаться к символу строки по индексу (его номеру в квадратных скобках):

Чем лучше указатели? Да тем, что процессору не нужно при каждом проходе цикла складывать адрес начала строки s и переменную i, чтобы вычислить адрес
s[i]:

xor ecx, ecx ; ecx — это i
xor edx, edx ; edx — это n
LOOP:
cmp DWORD PTR s[ecx], ‘A’
jne SHORT BYPASS
inc edx
BYPASS:
inc ecx
cmp ecx, eax
jb SHORT LOOP

Вместо этого хорошая программа просто увеличивает адрес, то есть указатель p, а не индекс i. Код тела цикла становится немного проще и быстрее:

mov ecx, offset s ; ecx — это p
xor edx, edx ; edx — это n
LOOP:
cmp BYTE PTR [ecx], ‘A’
jne SHORT BYPASS
inc edx
BYPASS:
inc ecx
cmp ecx, eax
jb SHORT LOOP

Здесь нужно сказать, что интеллектуальный компилятор может «додуматься» преобразовать код с индексом в код с указателем, но слишком надеяться на это не стоит. Компилятор все же не так умен, как использующий его программист :).

Как найти первую гласную или согласную букву в строке? Для этого случая лучше всего подходят функции strpbrk() и
strspn().

p = strpbrk(«стройка», «аеёиоуыэюя»); //
p – указатель на первую гласную
p = s + strspn(s, «аеёиоуыэюя»); // p – указатель на первую согласную

ПРОБЛЕМЫ С РУССКИМ ЯЗЫКОМ

Как преобразовать русские буквы в верхний или нижний регистр? Лучше всего использовать функции CharUpper() и CharLower() из библиотеки Windows (заголовочный файл windows.h). Во-первых, эти функции учитывают язык, установленный в Панели управления Windows, поэтому твоя программа будет работать правильно во всех странах и со всеми языками. Во-вторых, проще пользоваться этими функциями, чем пытаться настроить стандартные функции Си для работы с русским или изобретать что-то свое. Пример:

char *m=»министр», *p=»Президент»;
CharUpper(m);
CharLower(p);
printf(«%s %s», m, p); // Выведет «МИНИСТР президент»

Вместо указателя на строку можно передать отдельный символ или воспользоваться функциями с суффиксом -Buff, которые преобразуют указанное число символов строки:

char m[]=»министр»;
m[0] = (char)CharUpper((char*)(unsigned)(unsigned char)m[0]); // Министр
CharUpperBuff(m+2, 3); // МиНИСтр

Глядя на этот кусок кода, знатоки Си могут употребить немало интересных слов великого и могучего русского языка :). Но я пока не нашел другого работающего способа преобразовать тип (char) в (char*). Если кто-то найдет — пишите. Кстати, во многом это проблема программистов Microsoft. Им нужно было написать отдельное определение (макрос или inline-функцию) для CharUpper с аргументом типа
char.

В сравнении строк есть и еще одна тонкость, на которую обычно не обращают внимания. Дело в том, что коды символов не всегда соответствуют их порядку в алфавите. Например, русская буква «Ё» и специфические белорусские буквы «Ў» («у» краткое), «ї» («и» десятеричное) расположены в кодовой таблице Windows отдельно от всех остальных букв. И если ты сравниваешь слова «дерево» и «ёлка» с помощью strcmp (сравнение по кодам), то получишь, что «ёлка» меньше (ближе к началу алфавита), чем «дерево». Чтобы избежать этой ошибки, пользуйся функцией lstrcmp() из библиотеки Windows. Для примера приведу простейшую программу пузырьковой сортировки:

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
< char *a[]=<"кедр","дуб","желудь","дерево","ёлка","сосна">;
char *p, *x; unsigned i, j; char s[200];
#define N sizeof(a)/sizeof(a[0])
for(i=0; i Пузырьковая сортировка, strcmp
for(j=i+1; j 0 )
< x = a[i];
a[i] = a[j];
a[j] = x;
>
p = s;
p += sprintf(p, » strcmp:\n»);
for(i=0; i Пузырьковая сортировка, lstrcmp
for(j=i+1; j 0 )
< x = a[i];
a[i] = a[j];
a[j] = x;
>
p += sprintf(p, «\n lstrcmp:\n»);
for(i=0; i

Эта программа выведет (видно, что сортировка с использованием strcmp неверно обрабатывает слово «ёлка»):

strcmp:
ёлка
дерево
дуб
желудь
кедр
сосна

lstrcmp:
дерево
дуб
ёлка
желудь
кедр
сосна

УБРАТЬ И УДВОИТЬ

Как убрать из строки определенные символы, например, все пробелы и знаки препинания? Есть очень простой способ. Заведем два указателя: один (p) на ту часть строки, которую мы просматриваем, другой (p2) — на ту часть, в которую мы будем копировать символы, не являющиеся пробелами или знаками препинания. Если нам встретился знак препинания, пропускаем его, увеличивая только первый указатель. Таким образом, мы «собираем» все символы к началу строки (см. рисунок ниже).

char s[256], *p = s, *p2 = s;
gets(s);
while(*p) // Пока в строке есть символы
< if( !ispunct(*p) && !isspace(*p) )
*(p2++) = *p; // Если не знак препинания, копируем
p++; // Переходим к следующему символу
>
*p2 = ‘\0’;
puts(s);

Кстати, в Visual C++ функции ispunct, isspace довольно медленные, поэтому вместо них лучше написать условие типа

‘ ) //
примерно на 10% быстрее, чем !ispunct(p) &&
!isspace(*p)

Как убрать из строки все вхождения подстроки, например, убрать все переносы строк? Точно также, как и в случае с удалением символа, будем копировать нужные нам части строки в её начало. Можно искать подстроку с помощью strstr() или применить описанный в первой части статьи «Забытые секреты кодинга» макрос
toShort:

Сравнение строк: strcmp или посимвольно. Benchmark в черновиках

Я искал ответ на вопрос «что быстрее»

Задача

Проверить, не является ли строка «специальным маркером». Всего маркеров пять: «first», «last», «even», «odd», «exit», причём по «exit» программа завершается.

За те несколько дней что я изучаю C, я встречал два подхода к сравнению: функцией strcmp и побайтово (ака посимвольно). Мнения знакомых и коллег о том, какой подход работает быстрее, разделились. Значит нужен benchmark.

Исходники

Решение, использующее strcmp , я буду называть bench_str :

Решение, использующее побайтовое сравнение, я буду называть bench_char :

Компилятся и работают они одинаково корректно:

Входные данные

Используйте ваш любимый скриптовый язык для создания файла, содержащего изрядное число входных строк. Мне понадобилось 10M строк для того, чтобы время исполнения занимало несколько секунд. На меньших интервалах разница в скорости может быть не так заметна, и влияние прочих процессов, отжирающих ваш CPU, будет сказываться сильнее.

Я сделал make_input.php :

Обратите внимание, поскольку input читается бесконечно while (scanf(«%s», in)) , последней строкой в файле идёт «exit» — иначе программа зациклится.

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

Push the button, Max!

Выполняем! Я перенаправил вывод в /dev/null , чтобы не тратить ресурсы на вывод результата на экран: это тоже вносит погрешность и занимает приличное время. Если не собираетесь перенаправлять вывод, я рекомендую уменьшить число входных строк на порядок или два.

Итак, на сцене побайтовое сравнение:

I’d like to see the Great Leslie try THAT one! ©

На сцене strcmp :

Конечно, от запуска к запуску результаты немного варьируются, но в целом картина не меняется: strcmp примерно на полсекунды быстрее, а это около 20%! И я даже молчу о читаемости кода.

В качестве крайних кейсов можно оставить только корректные строки или только некорректные.


В случае использования только корректных строк, время выполнения обеих реализаций сокращается, и преимущество strcmp падает до 15%:

В случае использования только некорректных строк, время выполнения bench_char практически не меняется, а вот bench_str выполняется немного дольше.В целом преимущество strcmp падает примерно до 10%:

Картинка к этому делу:

Case #1: только правильные строки
Case #2: микс
Case #3: только неправильные

Если кто-то знает почему я не прав и в каком случае будет обратная картина — будет очень интересно расширить кругозор.
Но если кто-то подробно расскажет почему так, будет вообще суперски!

Спасибо за внимание!

комментарии ( 30 )

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

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

А почему вы скомпилировали код без оптимизаций? Добавьте -O3 и проверьте снова.

Еще существует функция memcmp для побайтного сравнения.

Вынес strlen в начало цикла, скомпилил с -O3, побайтовое стало шустрее.

Посыпаю буйну голову пеплом :)

Может из-за того что в случаи с «bench_char» используется strlen(), а потом еще от 3 до 5 сравнений символов? Давайте заглянем в реализацию этой функции, под рукой MSVC 10:

и на реализацию strcmp()

Думаю, очевидно что в случае с «bench_char» делается много лишней работы по сравнении с strcmp()

Насчёт «накладные расходы, связанные с начитыванием и выводом данных» — данные же идентичные. Вы имеете в виду, что считывание и вывод идут неравномерно?

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

Можно сделать что-то типа этого:

Всего маркеров пять: «first», «last», «even», «odd», «exit», причём по «exit» программа завершается.

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

Спасибо всем за наводки, кое-что уже встало на свои места (ошибка с многократным strlen, упущенная оптимизация), а кое что ещё буду курить и курить :)

Неплохо бы засечь время выполнения такого кода (только убрать все слова, начинающиеся на e, кроме последнего exit:

Чтобы понять, какое минимальное время может быть достигнуто в этом тесте.

Ну и скорость «проверки как int32» тоже интересно увидеть. Особенно на словах типа firsz и lasz.

Я искал ответ на вопрос «что быстрее»

Не стоит терять время. На производительность влияет целая куча факторов:
— При бенчмарке данные оседают в кэше, причём некоторые — в L1, а в реальной жизни операция strcmp выполняется редко и за каждой порцией данных нужно лезть в память (а порой и на диск).
— strcmp — библиотечная функция, но её производительность будет зависеть как от используемого компилятора, так и от рантайм библиотеки. То есть на связке Linux + gcc + glibc вы получите один результат, а на windows + icc или VS — совершенно другой! Ну и как тут можно что-то сравнивать?
— От инлайнов пухнет бинарник, из-за чего есть риск вылететь за пределы кэша. В частности, icc считает себя умнее разработчиков ОС и запихивает в бинарник «свою» жирную «оптимизированную» SSE-версию strcmp, которая мало того что медленнее реализации в glibc, так ещё и съедает драгоценный кэш инструкций.
— Причуды планировщика ОС также вносят свою лепту в бенчмарк, из-за чего погрешность может достигать 10% и выше. Тест, работающий секунду — не тест. Иногда программисты забывают, что в синтетических тестах компилятор способен выкинуть кое-какие инструкции, тут тоже легко отстрелить конечность.
— Разработчики процессоров оптимизируют и затачивают свои микрооптимизации под тот или другой вид бенчмарков. На разных процессорах и разных архитектурах вы будете получать очень неожиданные результаты. Например, SSE оптимизация выиграет на i7, но просядет на Atom. Примеры нагуглить несложно.

А потом спросите себя — зачем это нужно? До тех пор, пока этот кусок кода не всплывёт в профилировщике, пишите понятный код в ущерб производительности, а не наоборот.

Функции для работы со строками

Пожалуйста, приостановите работу AdBlock на этом сайте.

В языке Си предусмотрено множество разных функций, предназначенных для работы со строками. Для их использования необходимо подключить заголовочный файл string.h

Разберём самые простые из них:

  • strlen(str) – длина строки str ;
  • strcmp(str1, str2) – сравнение строк str1 и str2 ;
  • strcat(str1, str2) – конкатенация (склеивание) двух строк. К строке str1 в конце приклеивается строка str2 .
  • strcpy(str1, str2) – копирование строки str2 в строку str1 ;

Функция strlen

Данная функция возвращает целое число – длину строки, которая ей передана в качестве аргумента.

Обратите внимание. Длина строки – это не количество элементов символьного массива, а количество элементов в массиве до первого нуль-символа. Например, следующий код выведет на экран не 19 , а 7 .

Рис.1 Работа функции strlen.

Функция склеивания строк strcat.

Данная функция склеивает строки, которые передаются ей в качестве параметров. Функция strcat присоединяет к концу строки str1 строку str2 .

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

Результат работы этой программы ниже:

Рис.2 Работа функции strcat.

Функция сравнения строк strcmp

Данная функция сравнивает посимвольно строки, переданные ей в качестве аргументов. Функция strcmp вернёт нуль, если строки равны между собой, иначе какое-либо другое целое число (положительное или отрицательное). Общее правило таково: Если в функции strcmp() первая строка больше, чем вторая строка, то функция возвращает положительное число. Если меньше – отрицательное. Сравнение осуществляется по кодам символов в таблице ASCII

Посмотрите на пример.

Рис.3 Работа функции strcmp.

Первая и вторая строки одинаковы, поэтому результат их сравнения нуль. А вот первая и третья строки различаются седьмым символом. В первой строке это строчная w , а в третьей – прописная W . Т.к. код строчной буквы w больше, чем код прописной буквы W (119 > 87) , то в n13 помещается положительное число, т.к. первая строка, больше второй. Теперь если мы поменяем строки местами, то получим отрицательное число.

Функция копирования строк strcpy.

Данная функция принимает на вход две строки, а потом копирует вторую строку в первую. Простой примерчик.

Рис.4 Работа функции strcpy.

Как и в случае с strcat программисту нужно самому следить за тем, чтобы в первой строке хватило места для копирования в неё второй строки.

Есть и более сложные функции работы со строками, но о них в базовой части курса мы говорить не будем.

Практика

Решите предложенные задачи:

Для удобства работы сразу переходите в полноэкранный режим

Исследовательские задачи для хакеров

  1. (Листинг 2) Попробуйте самостоятельно, добавляя символов в строки str2 или str3 , добиться того, чтобы программа завершалась с ошибкой.
  2. (Листинг 4) Попробуйте скопировать первую строку во вторую, поменяв их местами в вызове функции strcpy .
Илон Маск рекомендует:  Шаблон для сайта по дизайну интерьера HTML, CSS
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL