
- •Лекция 13
- •Основные понятия
- •Операции со строками
- •Инициализация строки
- •Возможные ошибки при работе со строками
- •Ввод-вывод строк
- •Преобразование значения базового типа в с-строку
- •Ввод ограниченного числа символов строки
- •Функция вывода строки
- •Особенности посимвольного вывода строки
- •Копирование строк
- •Оптимизация текста программы на примере функции копирования
- •Функции для работы с с-строками
- •Определение длины строки
- •Копирование строк
- •Объединение строк
- •Сравнение строк
- •Поиск символа в строке
- •Проверка принадлежности символа некоторому множеству
- •Поиск строки в строке
- •Преобразование и заполнение строк
- •Преобразование строки в число
- •Преобразование числа в строку
- •Преобразование строчных символов строки в прописные и наоборот
- •Реверс строки
- •Заполнение строки символом
- •Пример применения функций преобразования
- •Выделение лексем
- •Динамические строки
- •Копирование строки в динамическую строку
- •Передача строки в функцию в качестве параметра
- •Пример_1: в main()– строка-массив; в функции – строка-указатель
- •Пример_2: в main() – строка-указатель; в функции – строка-указатель
- •Передача в функцию массива строк
- •Примеры работы со строками Определение длины строки с помощью функции пользователя
- •Реверс строки (перегрузка функций)
- •Удаление начальных пробелов из строки
- •Проверка, является ли строка целым числом
- •Проверка, является ли строка 16-ичным целым числом
- •Проверка, является ли строка дробным числом без знака
- •Перевод двоичного числа в десятичное
- •Перевод шестнадцатеричного числа в десятичное
- •Перевод десятичного числа в двоичное
- •Перевод десятичного числа в с/с от 2-х до 10
- •Перевод десятичного числа в шестнадцатеричное
- •Определение количества слов в фразе
- •Выделение слов в тексте по нажатию произвольной клавиши
- •Сортировка вводимых с клавиатуры строк *
- •Сортировка строк в зависимости от признака, передаваемого функции main() через аргумент argv *
- •Инвертирование строки с помощью рекурсивной функции
Динамические строки
Память под строки, как и под другие массивы, может выделяться как компилятором, так и непосредственно в программе. Как мы видели, длина статической строки должна быть только константным выражением. Чаще всего она задается именованной константой (как частным случаем константного выражения). Длина динамической строки может задаваться выражением, вычисляемым в ходе выполнения программы.
Для размещения строки в динамической памяти описываем указатель на char и выделяем память с помощью new или malloc.
char *p = new char [m];
char *q = (char *) malloc(m*sizeof(char));
Динамические строки, как и другие динамические массивы, нельзя инициализировать при создании.
Копирование строки в динамическую строку
При копировании строки в динамическую строку необходимо правильно выделять для неё память:
#include <string.h>
int main()
{
char* str = "aaaa bbbbb ccc ddd eeeeeee fffffff";
size_t len = strlen(str); //получение длины строки
char* ptr= new char[len+1]; //выделение места под динамическую строку + '\0'
strcpy_s(ptr, len+1, str); //копирование строки размера len+1 из str в ptr
cout << "ptr=" << ptr << endl; //вывод строки на экран
delete[] ptr; //освобождение памяти
_getch();
return 0;
}
Передача строки в функцию в качестве параметра
Возможность выполнения в функции тех или иных действий со строкой зависит от описания аргумента, передаваемого в функцию (строка-указатель или строка-массив), и от спецификации формального параметра. Анализируйте описания строк в вызывающей функции и строк – формальных параметров и выбирайте последнее, в зависимости от действий, которые необходимо выполнить со строкой в функции.
Рассмотрим сочетания разных описаний строк, которые могут выступать в качестве фактических и формальных параметров.
Пример_1: в main()– строка-массив; в функции – строка-указатель
//пример_1_1: строка передается как неконстантный указатель на неконстантные данные
void convertToUppercase (char *);
int main()
{
char string [ ] = "aaaaaaa"; //string - константный указатель на неконстантные данные
cout << string <<endl; //string++ - ошибка!!!, string[i]=i; - ок!!!
convertToUppercase(string);
cout << string <<endl;
_getch();
return 0;
}
void convertToUppercase (char *sPtr)
{
while (*sPtr != '\0') { // sPtr - неконстантный указатель на неконстантные данные
*sPtr = toupper(*sPtr);
++sPtr;
}
}
//пример_1_2: строка передается как неконстантный указатель на константные данные
void convertToUppercase (const char *);
int main()
{
char string [ ] = "aaaaaaa"; //string - константный указатель на неконстантные данные
cout << string <<endl;
convertToUppercase (string);
cout << string <<endl;
_getch();
return 0;
}
void convertToUppercase (const char *sPtr)
// sPtr – неконстантный указатель на константные данные
//т.е. данные «только для чтения»
{
for ( ; *sPtr != '\0'; sPtr++) //модифицировать неконстантный указатель можно
{cout << *sPtr; //читать константное значение можно
// *sPtr = toupper(*sPtr); - в данном случае ошибка!!!
} //нельзя модифицировать константные данные
}
//пример_1_3: строка передается как константный указатель на неконстантные данные
void convertToUppercase (char *const);
int main()
{
char string [ ] = "aaaaaaa"; //string – константный указатель на неконстнатные данные
cout << string <<endl;
convertToUppercase(string);
cout << string <<endl;
_getch();
return 0;
}
void convertToUppercase (char *const sPtr)
{ while (*sPtr != '\0') { // sPtr – константный указатель на неконстантные данные
*sPtr = toupper(*sPtr); //неконстантные данные модифицировать можно
// ++sPtr; ошибка!!! константный указатель модифицировать нельзя
}
}
Константный указатель на неконстантные данные – это указатель, который всегда указывает на одну и ту же ячейку памяти, данные в которой можно модифицировать посредством указателя. Этот вариант реализуется по умолчанию для имени массива. Имя массива – это константный указатель на начало массива. Используя имя массива и индексы массива можно обращаться ко всем данным в массиве и изменять их.
Указатели, объявленные как const, должны получить начальные значения при своем объявлении (если указатель является параметром функции, он получает начальное значение, равное указателю, который передается в функцию). Обратите внимание, что при объявлении константного указателя, ключевое слово const пишется между символом ‘*’ и идентификатором указателя, например: int *const ptr . Постоянство передаваемого значения задает const слева от *, например: const int * ptr.
//пример_1_4: строка передается как константный указатель на константные данные
#include <ctype.h>
void convertToUppercase (const char *const, int);
int main()
{
const int n=8;
char string [n] = "aaaaaaa"; //string – константный указатель на неконстантные данные
cout << string <<endl;
convertToUppercase(string, n);
cout << string <<endl;
_getch();
return 0;
}
void convertToUppercase (const char *const sPtr, int n)
{
for ( int i=0; i<n; i++) // sPtr – константный указатель на константные данные
{//*sPtr = toupper(*sPtr); //нельзя модифицировать константные данные
cout << sPtr[i] << endl;
//sPtr++; //ошибка !!!нельзя модифицировать константный указатель
}
}
Константный указатель на константные данные имеет наименьший уровень привилегий. Такой указатель всегда указывает на одну и ту же ячейку памяти и данные в этой ячейке нельзя модифицировать. Это выглядит так, как если бы массив нужно было передать функции, которая его только просматривает, «читает», но не модифицирует.