Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЗФ_ОАиП / Лекции ГГУ Скорины - Программирование.doc
Скачиваний:
179
Добавлен:
21.03.2016
Размер:
2.27 Mб
Скачать

16. Строки

В языке С нет отдельного типа для строк. Работа со строками реализована через массивы. Хотя в других языках программирования имеется такой тип данных как string – строки.

Строка в Паскале – специальный тип данных string. Тип string – это, по существу, массив array [0..255] of char. Первый его элемент (байт с номером 0) задает динамическую длину строки (реальную длину строки), которая может принимать значения от 0 до 255 символов. Символы, составляющие строку, занимают места от 1 до 255.

var

s : string;

begin

s:= '1234567';

writeln(Ord(s[0])); // увидим длину строки 7

end.

В других средах программирования на Паскале могут быть типы строковых данных с другими максимальными длинами (например, тип AnsiString). Переменная типа AnsiString – это динамически распределяемые массивы символов, максимальная длина которых ограничивается только наличием памяти. При определении таких переменных память для них не выделяется, а выделяется память только под указатель, который хранит адрес самой строки. Под строку память будет выделена при присваивании или процедурой SetLength.

В языке С символьная строка – это одномерный массив типа char, заканчивающийся нулем (символом с кодом 0, ноль-символом, символьной константой '\0').

Если объявить строку s в виде char s[10], то для хранения этой строки будет отведено 10 байт памяти. К каждому символу строки можно обращаться, как к элементу массива: s[0], s[1] и т.д. При этом реальная длина строки может быть и меньше 10 символов. Признаком окончания строки является символ с нулевым ASCII-кодом. Если в строке s записано слово "С++", то s[0]='C', s[1]='+', s[2]='+', s[3]=0, а во всех остальных элементах массива s могут быть записаны произвольные символы.

C

+

+

\0

-

-

-

Длина строки (т.е. число символов, предшествующих '\0') нигде явно не хранится. Длина строки ограничена лишь размером массива, в котором сохранена строка, и может изменяться в процессе работы программы в пределах от 0 до длины массива-1. Таким образом, максимальная длина строки, объявленной как char s[n] может быть n-1 символ, поскольку один символ требуется для хранения нулевого символа, завершающего строку. При работе со строками надо быть аккуратными и не вылезать за границы массива-строки, поскольку язык C не контролирует подобные ошибки. Ошибки, связанные с переполнением строк, наряду с ошибками при работе с указателями, являются основными причинами затирания памяти.

Язык С допускает строковые константы. Строковая константа – это набор символов в двойных кавычках. В конце строковой константы не нужно ставить символ '\0'. Это сделает компилятор автоматически. Например, константа "Borland C++" будет выглядеть в памяти как вот такой массив символов:

B

o

r

l

a

n

d

C

+

+

\0

Значение строковой константы – это адрес ее первого символа.

Нельзя путать строковые константы с символьными константами. Так "а" – это строковая константа, содержащая одну букву, в то время как 'а' – символьная константа, или просто символ. Отличие "а" от 'а' в том, что строка "а" содержит еще один символ '\0' в конце строки и, таким образом, занимает в памяти 2 байта, в то время как 'а' – только 1 байт.

Пустая строка – это строка с нулевой длиной. Чтобы получить пустую строку, надо в нулевой символ строки занести 0. Пустая строка – константа это константа “” (не надо писать так “\0”).пустую строку, н0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

char s[80];

s[0] = 0; // или *s = 0 или *s = ‘\0’

Ввод и вывод строк осуществляется так же, как и ввод-вывод других типов данных: с помощью scanf() и printf():

char s[80];

scanf(“%s”,s); // & перед s писать не надо, т.к. s и есть адрес строки,

// как указатель-константа на начало массива

printf(“%s”,s);

Есть еще специальные функции для ввода и вывода только строк:

gets(s); // ввод строки

puts(s); // вывод строки

Между printf() и puts() отличий нет, кроме того, что в printf() помимо строки можно еще вывести какой-нибудь поясняющий текст, например. Отличие между scanf() и gets(): scanf() вводит строку или до конца строки (\n), или до первого пробела; gets() – вводит всю набранную строку до конца. Если ввести, например, строку «мама мыла» и нажать Enter, то scanf() введет только «87Ама», а gets() – введет всю строку «мама мыла».

При выводе на экран строки выводятся все символы строки вплоть до завершающего нулевого символа. Нулевой символ на экран не выводится.

При вводе строки считываются все введенные символы (для scanf() или до конца или до пробела, причем пробел не считывается). При этом строка автоматически будет дополнена завершающим нулевым символом.

При описании строки можно инициализировать.

char s1[] = “123456”; // строго 7 символов

char s2[10] = “123456”; // последние 4 символа будут 0

char s3[] = {‘1’,’2’,’3’,’4’,’5’,’6’,’\0’};

char s4[10] = {'1','2','3','4','5','6'}; // последние символы

// будут 0

char s5[] = {'1','2','3','4','5','6'}; // это не строка!!!

char s6[6] = “”; // это пустая строка

1

2

3

4

5

6

\0

1

2

3

4

5

6

\0

\0

\0

\0

1

2

3

4

5

6

\0

1

2

3

4

5

6

\0

\0

\0

\0

1

2

3

4

5

6

\0

\0

\0

\0

\0

\0

В языке С не предусмотрены какие-либо операции для обработки всей строки символов целиком. Есть функции для работы со строками, их мы рассмотрим позже. Но так как строка, это массив символов, то мы можем обрабатывать строки посимвольно, обращаясь к отдельным элементам строки через их индексы.

Указатели в сочетании с операцией инкремента естественным образом используются для сканирования строк. В таком контексте динамически меняющийся указатель на строку часто называют просто строкой, хотя реально это не так.

Задача. Подсчитать длину строки.

void main(){

char s[80];

int n = 0;

gets(s);

while(s[n] != 0) // while(s[n]) while(s[n]!=‘\0’)

n++;

printf("n = %d\n", n);

}

Задача. Подсчитать длину строки, используя указатель.

void main(){

char s[80], *ps;

int n = 0;

gets(s);

ps = s;

while(*ps) {

n++;

ps++;

}

printf("n = %d\n", n);

}

======= второй вариант ============================

void main(){

char s[80], *ps;

gets(s);

ps = s;

while(*ps)

ps++;

printf("n = %d\n", ps-s);

}

Задача. Сколько цифр в слове?

void main(){

char s[80];

int i = 0, n = 0;

gets(s);

while(s[i] != ‘\0’) {

if (s[i] >= ‘0’ && s[i] <= ‘9’) // символ цифра?

n++;

i++;

}

printf(“В слове %s цифр %d\n”, s, n);

}

Задача. К строке 1 присоединить строку 2.

void main(){

char s1[80],s2[80];

int i = 0,j = 0;

gets(s1);

gets(s2);

while(s1[i]) // становимся в конец строки s1

i++;

while(s2[j]) { // добавляем к s1 строку s2

s1[i]=s2[j]; // s1[i++]=s2[j++];

i++; //

j++; //

}

s1[i]=0; // не забывать ставить символ конца строки!!!

puts(s1);

}

======= второй вариант ============================

void main(){

char s1[80],s2[80];

int i,j;

gets(s1);

gets(s2);

for (i=0; s1[i]; i++);

for (j=0; s2[j]; i++, j++)

s1[i]=s2[j];

s1[i]=0;

puts(s1);

}

Задача. К строке 1 присоединить строку 2, используя указатели.

void main(){

char s1[80],s2[80];

char *ps1,*ps2;

gets(s1);

gets(s2);

ps1 = s1;

ps2 = s2;

while(*ps1)

ps1++;

while(*ps2) {

*ps1 = *ps2;

ps1++;

ps2++;

}

*ps1 = 0;

puts(s1);

}

Задача. Удалить из строки все вхождения заданного символа.

void main() {

char s[80], ss[80], c;

int i = 0, j = 0;

gets(s);

c = 'a';

while (s[i]) {

if (s[i] != с) {

ss[j]=s[i]; // создаем новую строку

j++;

}

i++;

}

ss[j]=0;

puts(ss);

}

======= второй вариант ============================

void main() {

char s[80], c;

int i = 0, j;

gets(s);

c = 'a';

while (s[i]) {

if (s[i] == с) {

j = i;

while (s[j]) // смещаем хвост

s[j++] = s[j+1];

}

else i++;

}

puts(s);

}

======= третий вариант ============================

void main() {

char s[80], c;

char *ptr, *ptr_rez;

ptr = ptr_rez = s;

gets(s);

c = 'a';

while (*ptr) {

if (*ptr!=c) {

*ptr_rez=*ptr; // используем два указателя

ptr_rez++;

}

ptr++;

}

*ptr_rez=0;

puts(s);

}

Задача. Дана строка. Создать подстроку длиной n>0, начиная от символа с номером k>=0.

void main(){

char s[80], ss[80];

int k, n, i;

gets(s);

scanf("%d%d", &n, &k);

for(i = 0; i < k; i++)

if(!s[i]) break;

if (i < k)

*ss = 0; //ss[0] = 0;

else {

for(i = 0; i < n && s[i+k]; i++)

ss[i] = s[i+k];

ss[i]=0;

}

puts(ss);

}

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

void main(){

char s[80], c_end;

int i = 0, j, n;

gets(s);

scanf("%d", &n);

while(s[i])

i++;

c_end = s[i-1];

for (j = 0; j < n; j++)

s[i+j] = s[0]; // s[i+j] = *s

s[i+j] = 0;

j += i;

for (i = j; i >= 0; i--)

s[i+n] = s[i];

for (i = 0; i < n; i++)

s[i] = c_end;

puts(s);

}

======= второй вариант ============================

void main(){

char s[80], c_end;

int i = 0, j, n;

gets(s);

scanf("%d", &n);

while(*(s+i))

i++;

c_end = *(s+i-1);

for (j=0; j<n; j++)

*(s+i+j) = *s;

*(s+i+j) = 0;

j += i;

for (i = j; i >= 0; i--)

*(s+i+n) = *(s+i);

for (i = 0; i < n; i++)

*(s+i) = c_end;

puts(s);

}

======= третий вариант ============================

void main(){

char s[80], *ps, *p, c_end;

int n;

gets(s);

scanf("%d", &n);

ps = s;

while(*ps)

ps++;

c_end = *(ps-1);

p = ps + n;

for (; ps != p; ps++)

*ps = *s;

*ps = 0;

p = ps;

ps += n;

for (; p >= s; p--, ps--)

*ps = *p;

for (; ps >= s; ps--)

*ps = c_end;

puts(s);

}

Задача. Дано предложение, слова в котором разделены произвольным количеством пробелов. Найти количество слов в предложении.

void main(){

char s[80];

int n = 0, i = 0;

gets(s);

while (s[i]) {

while (s[i]==' ')

i++;

if (!s[i]) break;

n++;

while (s[i]!=' ' && s[i])

i++;

}

printf("n = %d\n", n);

}

======= второй вариант ============================

void main(){

char s[80], c = ' ';

int n = 0, i;

gets(s);

for (i=0; s[i]; i++) {

if (s[i]!=' ' && c==' ')

n++;

c = s[i]; // сохраняем предыдущий символ

}

printf("n = %d\n", n);

}

Задача. Дано предложение. Оставить от каждого слова только первую букву, поставив после нее длину слова (“заяц” => “з4”).

#include <stdlib.h> // для itoa()

void main(){

char s[80], ss[80], c = ' ', t[3];

int i, j=0, n, k;

gets(s);

for (i=0; s[i]!=0; i++) {

if (s[i]!=' ' && c==' ') {

ss[j] = s[i];

j++;

n = i;

while (s[i]!=' ' && s[i])

i++;

n = i - n;

itoa(n,t,10);

for (k=0; t[k]; ss[j++]=t[k++]);

ss[j++] = ' ';

i--;

}

c = s[i];

}

if (j > 0)

j--;

ss[j] = 0;

puts(ss);

}

Поскольку имя массива фактически является указателем на первый элемент массива, переменные типа строк могут также рассматриваться, как имеющие тип char *. Но это не значит, что, написав char *s, мы описываем строку. Мы описываем указатель на строку. В этот указатель можно занести адрес реальной строки (размещенной в памяти).

char s[7];

gets(s);

char *se;

gets(se);

по случайному адресу из seзапишется 6 байт

и затрется память !!!

char s[80]; // описали строку

char *ps; // описали указатель на строку

ps = s; // указатель ps стал указывать на строку s

gets(ps); // введенная строка сохранится в памяти,

// выделенной для переменной s char *p = “12345”; // описали указатель и в него занесли

// адрес константы “12345”

// Изменять такую строку нельзя!!!

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

//s = p; // ошибка!!!!!!!!!

ps = p; // в ps занесли адрес константы “12345”

// Это не копирование строки!!!

Как определить, что символ цифра: s[i]>=’0’ && s[i]<=’9’