
- •Алгоритм
- •3. Этапы решения задач на эвм.
- •4. Структу́рное программи́рование
- •6. Типизированные указатели.
- •7.Ссылочный тип.
- •8.Перечислимый тип.
- •10.Процедуры ввода-вывода. Потоковый ввод-вывод.
- •13.Условный оператор. Оператор выбора.
- •14.Операторы безусловного перехода.
- •16.Циклические программы. Вложенные циклы.
- •17.Глобальные и локальные переменные.
- •18.Функции. Механизм передачи параметров.
- •19.Вложенные функции. Рекурсия.
- •20.Область видимости и время жизни переменной.
- •21.Фактические и формальные параметры.
- •22.Перегрузка функций.
- •23.Шаблоны функций.
- •24.Понятие модуля. Преимущества модульного программирования. Структура модуля.
- •25.Пример модуля. Способ использования модуля.
- •27.Символьный тип данных. Строковые массивы. Способы обработки.
- •28.Основные операции над строками. Функции для работы со строками.
- •29.Структуры. Работа со структурами. Примеры.
- •30.Массивы структур. Особенности обработки. Примеры.
- •31.Файлы. Виды файлов. Файловая переменная. Общая схема работы с файлами.
- •33.Текстовые файлы. Функции обработки для текстовых файлов.
- •34.Бинарные файлы. Функции для работы с бинарными файлами.
- •35.Статическая и динамическая память.
- •37.Алгоритмы и методы сортировки: оценка эффективности алгоритма.
- •38.Сортировка выбором.
- •43.Алгоритмы и методы поиска в отсортированном массиве данных.
27.Символьный тип данных. Строковые массивы. Способы обработки.
Строки как массивы символов
Строка представляет собой массив символов, заканчивающийся нуль-символом. Нуль-символ — это символ с кодом, равным О, что записывается в виде управляющей последовательности ‘\0′. По положению нуль-символа определяется фактическая длина строки. Строку можно инициализировать строковым литералом.
char str[10] = "Vasia";
// выделено 10 элементов с номерами от 0 до 9
// первые элементы - 'V', 'а', 's', 'i', 'а', '\0'
В этом примере под строку выделяется 10 байт, 5 из которых занято под символы строки, а шестой — под нуль-символ. Если строка при определении инициализируется, ее размерность можно опускать (компилятор сам выделит соответствующее количество байт):
char str [] = "Vasia"; // выделено и заполнено 6 байт
Оператор
char *str = "Vasia"
создает не строковую переменную, а указатель на строковую константу, изменить которую невозможно (к примеру, оператор str[l]=’o’ не допускается). Знак равенства перед строковым литералом означает инициализацию, а не присваивание. Операция присваивания одной строки другой не определена (поскольку строка является массивом) и может выполняться с помощью цикла или функций стандартной библиотеки. Библиотека предоставляет возможности копирования, сравнения, объединения строк, поиска подстроки, определения длины строки и т. д., а также содержит специальные функции ввода строк и отдельных символов с клавиатуры и из файла.
При работе со строками часто используются указатели.
Внимание
Распространенные ошибки при работе со строками — отсутствие нуль-символа и выход указателя при просмотре строки за ее пределы.
Существует несколько способов работы со строками (через массивы или указатели) и они имеют свои плюсы и минусы, но в общем случае лучше не изобретать велосипед, а пользоваться функциями библиотеки или определенным в стандартной библиотеке C++ классом string, который обеспечивает индексацию, присваивание, сравнение, добавление, объединение строк и поиск подстрок, а также преобразование из С-строк, то есть массивов типа char, в string, и наоборот.
28.Основные операции над строками. Функции для работы со строками.
Создание строк
Проиллюстрирую создание строк на фрагментах кода с комментариями.char str1[10]; // Строка - массив из 10 символов. Начальное значение символов не определено.
char str2[10]="Hello";
/* Используется инициализация (не присваивание!). В первые 5 символов записывается “Hello”, в 6 – нуль-терминатор, значение трех последних не определено.*/
char str3[10]={'H', 'e', 'l', 'l', 'o', '\0'}; //Эквивалентно предыдущему.
char str4[10]="Very long line";
//Ошибка. Массив из 10 элементов нельзя инициировать более длинной последовательностью.
char str5[]="Very long line";
/*Компилятор автоматически определяет длину массива (в нашем случае 15) и инициализирует его последовательностью символов. */
char* str6;
/*Строка - указатель на символ. В большинстве случаев для ее использования
потребуется выделить память.*/
str6=(char*) malloc(10);
free(str6);
Присваивание строк
Первый и самый очевидный способ присваивания строк – присваивание отдельных символов. Например,str1[0]=’H’;
str1[1]=’e’;
str1[2]=’l’;
str1[3]=’l’;
str1[4]=’o’;
str1[5]=’\0’;
Однако, это совершенно неудобно. Не зная о правильных способах, начинающие программисты часто «выдумывают» свои способы присваивания строк, конечно, неправильные. Приведу несколько примеров:char str1[10], str2[10];
str1="Hello";
str2=str1;
//Одна и та же ошибка в обоих операторах =.
//Имя массива нельзя использовать в левой части оператора присваивания.
Эта ошибка относительно безопасна, так как приводит к сбою на этапе компиляции. Есть и гораздо более опасная ошибка. char str1[10]= "Hello";
char* str2;
str2=str1;
str2[1]='u';
Этот код откомпилируется, но, возможно, содержит «идеологическую» ошибку. Неправильно полагать, что в str2 теперь содержится копия str1. На самом деле этот указатель указывает не на копию, а на ту же самую строку. При любом изменении содержимого str2 изменяется str1. Однако, если именно это и требуется, то все в порядке.
Еще один вариант присваивания указателей – присваивание их строковым литералам. Как вы помните, тип строкового литерала – const char*, а значит такой код работает:const char* str;
str="Hello";
Опять же следует помнить, что str указывает на строковый литерал, а не на его копию. Но, к сожалению, такой код тоже сработает:char* str; // Нет const
str="Hello";
Здесь мы имеем дело с наследством C, в котором отсутствовал const. Поэтому стандарт С++ разрешает такое присваивание. Что может иметь неприятные последствия:char* str; // Нет const
str="Hello";
str[1]=’u’;
Результат работы такой программы непредсказуем. Компилятор может разместить константы в памяти только для чтения, и попытка их изменить приведет к сбою. Поэтому всегда объявляйте указатели, в которые вы собираетесь записывать адреса строковых литералов как const char*. В этом случае компилятор не позволит модифицировать данные и диагностирует ошибку, что поможет вам исправить логику программы.const char* str; // const есть
str="Hello";
str[1]=’u’; //error: l-value specifies const object
Вопросы неправильного и рискованного присваивания строк мы рассмотрели. Пришла пора обсудить правильное присваивание или копирование строк.
Для копирования строк существуют несколько библиотечных функций, наиболее общеупотребительной из которых является функция char* strcpy(char* dest, const char* src)
Функция посимвольно копирует содержимое строки, на которую указывает src в строку, на которую указывает dest и возвращает dest. Так как массив может быть преобразован в указатель, такой вызов функции абсолютно легален:char str1[10], str2[10];
strcpy(str1, "Hello");
strcpy(str2, str1);
При использовании этой функции следует соблюдать осторожность. Опасность заключается в том, что даже если исходная строка окажется больше, чем память, выделенная для второй строки (программистом через malloc или компилятором при использовании массивов), функция strcpy никак про это узнать не сможет и продолжит копирование в невыделенную память. Разумеется, последствия будут катастрофическими.
Снизить риск такого развития событий способна функция char* strncpy(char* dest, const char* src, size_t count)
Последний параметр – максимальное количество копируемых символов. Таким образом, передавая туда размер приемника, вы гарантируете, что функция никогда не выйдет за пределы выделенной памяти. Однако помните, что если исходная строка будет скопирована не полностью, нуль-терминатор не появится в результирующей строке. Его придется записать самостоятельно.ПРЕДУПРЕЖДЕНИЕ
Никогда не забывайте контролировать используемую память!
Сравнение строк
Для лексикографического сравнения строк используются функции strcmp и stricmp. Первая сравнивает строки с учетом регистра, вторая – без. Однако, все это относится только к латинице. Если вы хотите сравнивать без учета регистра кириллические строки, придется разобраться с локалями.
Прототипы этих функций таковы:int stricmp(const char *string1, const char *string2);
int strcmp(const char *string1, const char *string2);
Обе функции возвращают число меньшее 0, если первая строка меньше второй, большее нуля если первая строка больше второй и 0, если строки лексикографически равны.
Полагаю, вам не придет в голову сравнивать строки, используя операции ‘<’ и ‘>’.
Длина строки
Для вычисления длины строки используется функция size_t strlen(const char *string);
Функция возвращает длину строки, не включая нуль-терминатор. Как всегда, следите за тем, чтобы в выделенной под string памяти все же нашелся такой символ. В противном случае функция выйдет за пределы выделенной памяти и все будет плохо. Напомню, что для определения длины строки функции придется последовательно обратиться ко всем ее символам до нуль-терминатора, а значит, потенциально эта операция довольно дорогая. Поэтому, не следует использовать эту функцию в циклах, то есть вместоfor (i=0;i<strlen(str);i++) {
// работа со строкой, не изменяющая ее длину
}
больше подойдет примерно такой код :char len;
len=strlen(str);
for (i=0;i<len;i++) {
// работа со строкой, не изменяющая ее длину
}
Преобразования строк
Зачастую требуется преобразовать число в строку и наоборот. Есть несколько способов сделать это.
Во-первых, такие преобразования совсем несложно делать самостоятельно. Оставляю это в качестве домашнего задания.
Во-вторых, можно использовать функции sprintf и sscanf. Например, так:char str[50];
int i=15;
int j;
sprintf(str, "%d", i); // Записать в str строковое представление i
sscanf(str, "%d", &j); // Записать в j число, содержащееся в строке str
sprintf(str, "i=%d and j=%d", i, j);
// содержимое str: "i=15 and j=15"
Эти функции очень похожи на printf и scanf, за исключением того, что они работают не с консолью, а со строковым буфером. Для дополнительной информации об этих функциях см. документацию.
Хотя sprintf и sscanf довольно удобны, у них есть несколько недостатков. Во-первых, они не всегда быстро работают, во-вторых не типобезопасны. Например, если в строке формата вы укажите, что передаете два целых, а вместо этого передадите два double, ошибка обнаружится только при выполнении программы и найти ее причину будет не так-то просто.
В-третьих, доступно целое семейство функций atof, atoi, atol и itoa, ltoa. Все они очень похоже между собой. Функции из первой группы преобразуют строку в число (float, int или long) в зависимости от окончания. Функции из второй группы выполняют обратное преобразование. Должен заметить, что функции из второй группы (равно как и ранее упомянутая stricmp) не входят в стандарт С, однако они весьма удобны и доступны на некоторых платформах.
Прототипы функций из первой группы:double atof(const char* string);
int atoi(const char* string);
long atol(const char* string);
Вторая группа:char* itoa(int value, char* string, int radix);
char* ltoa(long value, char* string, int radix);
Функции из второй группы могут создавать строковое представление чисел в любой системе (по любому основанию) от 2 до 36. Основание передается в третьем параметре. Чтобы получить строковое представление числа в десятичной системе, передайте 10. Функции возвращают указатель на строку.ПРИМЕЧАНИЕ
При использовании этих функций не забывайте выделять память, достаточную для предоставления строкового предоставления числа. Например, максимальная длина десятичного строкового представления четырехбайтного беззнакового целого числа - 11 байт, включая нуль-терминатор ("4294967295").
Пример:char str1[5];
char str2[5];
char str3[5];
itoa(12, str1, 10); //str1=”12”
itoa(12, str1, 16); //str1=”C”
itoa(12, str1, 2); //str1=”1100”
Конкатенация (объединение) строк
Сначала простой вопрос – каков результат выполнения следующего кода:char str1[10]="Hello";
char str2[10]="World!";
char* str3;
str3=str1+str2;
Если ответ – ошибка на этапе компиляции, материал изложенный в статье вы усвоили (или знали это раньше). Если же вы полагаете, что в str3 будет хранится строка "Hello world!", то вероятно, мои предыдущих объяснений оказалось недостаточно. Нельзя складывать указатели (и имена массивов).
Для конкатенации следует использовать функции.
Есть две специальные функции:char* strcat(char* dest, const char* source)
char* strncat(char* dest, const char* source, size_t size)
Эти функции добавляют к строке, на которую указывает dest, символы из строки source. Первая версия добавляет все символы до нуль-терминатора, вторая – максимум size символов. Результирующая строка завершается нуль-терминатором.
Кроме того, можно воспользоваться общей функцией sprintf так:char str1[]="Hello ";
char str2[]="world";
char str3[]="!";
char str4[13];
sprintf(str3, "%s%s%s", str1, str2, str3);
Этот вариант удобнее, если нужно объединить более двух строк. Однако к его недостаткам относится типонебезопасность.
функции
memset (void*, int, size_t) - устанавливает n первых байтов в указанное значение;
strcat(char*, const char*) - добавляет одну строку к другой;
strchr(const char*, int) - ищет первое вхождение указанного символа в строке;
strcmp(const char*, const char*) - сравнивает две строки (0 - равны строки, отрц. - первая строка меньше второй, полож. первая строка больше второй);
strcpy(char*, const char*) - копирует одну строку в другую;
strcspn(const char*, const char*) - ищет первое вхождение одного из символов одной строки в другой (возвращается позиция);
strlen(const char*) - возвращает длину строки (без нулевого символа конца строки);
strncat(char*, const char*, size_t) - добавляет n символов одной строки к другой.
strncmp(const char*, const char*, size_t) - сравнивает n первых символов;
strncpy(char*, const char*, size_t) - копировать n первых символов одной строки в другую;
strpbrk(const char*, const char*) - ищет первое вхождение одного из символов одной строки в другой;
strrchr(const char*, int) - поиск символа с конца;
strspn(const char*, const char*) - поиск первого символа не входящего в указанную строку (возвращается номер первого символа);
strstr(const char*, const char*) - поиск первого вхождения подстроки;
strtok(char*, const char*) - возвращает следующий токен (элемент разбора).