Строки и указатели
Краткие итоги
Ключевые термины
Конкатенация строк – это результат последовательного соединения строк.
Лексикографический порядок – правило сравнения символов, основанное на величине кода внутреннего представления каждого символа.
Пустая строка – это строка единичной длины, содержащая только символ конца строки.
Сравнение строк – это результат проверки выполнения отношения "больше", "меньше" или "равно" над строками.
Стандартные функции по работе со строками – это функции обработки строк, прототипы которых входят в стандартные библиотеки С++.
1. Для работы со строками в языке С++ предусмотрены стандартные функции, прототипы которых включены в стандартные библиотеки stdlib.h и string.h.
2. При обращении к функциям для работы со строками следует учитывать, что изменение значений элементов строк сохраняются после завершения работы функции.
3. Перед использованием строки в программном коде ее необходимо проинициализировать. Неинициализированные строки могут привести к некорректной работе программы.
4. В некоторых стандартных функциях по работе со строками следует проводить контроль длин параметров.
5. Результат работы некоторых функций требует принудительного добавления к строке символа конца строки.
6. Значения элементов строк зависят от регистра.
7. Изменение регистра символов кириллицы в программе может выполняться некорректно.
Поскольку строка всегда заканчивается нулевым символом, можно увеличивать указатель на 1, пока очередным символом не станет нуль. Например:
while ( *st++ ) { ... }
st разыменовывается, и получившееся значение проверяется на истинность. Любое отличное от нуля значение считается истинным, и, следовательно, цикл заканчивается, когда будет достигнут символ с кодом 0. Операция инкремента ++ прибавляет 1 к указателю st и таким образом сдвигает его к следующему символу.
Поскольку указатель может содержать нулевое значение (ни на что не указывать), перед операцией разыменования его следует проверять. Вот как может выглядеть вычисление длины строки.
void main()
{
const char *st= "123456";
int cnt = 0;
if ( st )
while ( *st++ )
++cnt;
cout<<cnt;
getch(); // ждать нажатия любой клавиши
}
Строка встроенного типа может считаться пустой в двух случаях: если указатель на строку имеет нулевое значение (тогда у нас вообще нет никакой строки) или указывает на массив, состоящий из одного нулевого символа (то есть на строку, не содержащую ни одного значимого символа).
char *pc1 = 0; // pc1 не адресует никакого массива символов
const char *pc2 = ""; // pc2 адресует нулевой символ
При разработке функций для работы со строками в большинстве случаев целесообразно применять указатели. Приведем примеры фрагментов программ:
/*Пример пользовательской функции копирования строки s2 в s1*/
void main()
{
char s1[] = "123456"; char s2[]= "789";
char *p1= s1, *p2= s2; //указатели инициализированы на начало строк
while ((*p1++ = *p2++) != 0);
// вывод строки s1
p1 = s1; //указатель инициализирован на начало строки s1
while ( *p1 != '\0') {cout<<*p1; p1++;}
getch(); // ждать нажатия любой клавиши
}
Следующий пример демонстрирует, что использование нулевого ограничителя упрощает различные операции над строками.
/*Пример пользовательской функции конкатенации*/
void main()
{
char s1[] = "123456"; char s2[]= "789";
char *p1= s1, *p2= s2;
while ( *p1 != '\0') p1++; //найти конец 1-ой строки.
//или while ( *p1) p1++;
while ((*p1 = *p2) != 0)
{
/*копировать строку р2, пока не будет скопирован нулевой ограничитель*/
p1++;
p2++; //Передвинуть указатели к следующему байту
} //или while (( *p1++ = *p2++) != 0);
// вывод строки s1
p1 = s1; //указатель инициализирован на начало строки s1
while ( *p1 != '\0') {cout<<*p1; p1++;}
getch(); // ждать нажатия любой клавиши
}
Пример 1.
/*Демонстрация работы с указателями и с функциями для обработки строк*/
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <string.h>
using namespace std;
int main()
{SetConsoleOutputCP(1251);
char string[100], temp[100], *result, simvol;
int numresult, res;
/*создает строку "computer program C++ " посредством
использования strcpy и strcat*/
strcpy(string, "computer");
result = strcat(string," program C++");
printf("1) создали строку\n%s\n", result);
/*находит строку, в которой первый раз обнаружено 'a'*/
simvol='a';
result = strchr(string,simvol);
printf("2) находим в строке первое вхождение символа \'%c\'\n %s\n",simvol,result);
/* создает копию строки */
result = strcpy(temp,string);
printf("3) создали копию строки\n%s\n",result);
/* находит "a","b","c" в строке */
strcpy(string,"xyzabbc");
res = strcspn(string,"abc"); /* определяет длину первого сегмента строки string, содержащего символы, не входящие во множество символов строки "abc"*/
printf("4) определяем длину заданного сегмента \n%d\n",res);
/*создает новый указатель на строку для дублирования
строки*/
result = strdup(string); /*выделяет память и переносит в нее копию строки string*/
printf("5) создали новый указатель на строку \n%s\n",result);
system("pause");
return 0;
}
Например, программа выводит на экран часть введенной строки после первого пробела:
Пример 2.
/*Вывести на экран часть строки после первого пробела*/
#include "stdafx.h"
#include <windows.h>
using namespace std;
int main()
{SetConsoleOutputCP(1251);
char s[80], *p;
int i;
printf("ввести строку: ");
gets(s);
/*найти первый пробел или конец строки*/
for(i=0; s[i] && s[i]!=' '; i++);
p = &s[i];
printf(p);
system("pause");
return 0;
}
В этой программе p будет указывать либо на пробел, если он есть, либо на ноль, если в строке нет пробелов. Если p указывает на пробел, то программа выведет на экран его и затем остаток строки. Если p укажет на ноль, то на экран ничего не выводится.
Пример 3:
//Выводит каждое отдельное слово и подсчитывает его длину
#include "stdafx.h"
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
SetConsoleOutputCP(1251);
char text[100],*p, *razd=" .,";
int dlina;
puts ("Введите текст ");
gets(text);
p=strtok(text,razd); // Выделение первого слова текста
while (p) // Пока можно выделить слово
{
dlina=strlen(p); // Определение длины слова
cout << "\n слово "<< p << " длина = " << dlina <<"\n";
p=strtok(NULL,razd); //Выделение второго, третьего, и т.д. слов
}
system("pause");
return 0;
}
При использовании строк или указателей на строки в качестве параметров функций следует учитывать некоторые особенности.
· При передаче строки как параметра функции не указывается длина, так как ограничителем является символ конца строки.
· Строки передаются в функции в качестве параметров как массивы символов или как указатели типа char.
· При побайтовом копировании строки или ее подстроки без использования стандартных функций формируемую строку следует завершить, дописав символ конца строки. В противном случае строка не воспринимается как единое целое, а при выходе за ее границы доступными становятся байты, содержащие "мусор", то есть непредсказуемую информацию.
· Обращение к строкам через указатели позволяет вносить и сохранять изменения, записанные в адресуемой области памяти. Для недопущения изменений в строке указатель на константу можно объявить с лексемой const следующим образом: const char *p;.
· В силу специфики представления строк в виде символьного массива сами строки, строковые константы, заключенные в кавычки, и указатели на строки обрабатываются эквивалентно. При этом каждый такой элемент адресует область памяти и передается в функции как адрес.
· При копировании строки или подстроки с использованием указателя не создается физической копии значений элементов. Объявленный новый указатель адресует то место в памяти, с которого начинается копируемая строка или подстрока.
Например:
char text[50]="Язык программирования";
char *p=text, *pp; //объявление и инициализация указателя р адресом строки text
pp=p; //указатель рр адресует ту же строку text