Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

192

Программирование на языке Си

4.3. С и м вольн ая инф орм ация и строки

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

Символьные константы были рассмотрены в главе 1. Для символьных данных введен базовый тип char. Описание сим­ вольных переменных имеет вид:

char список_именпеременных;

Например:

char a,z;

Ввод-вывод символьных данных. Для ввода и вывода сим­ вольных значений в форматных строках библиотечных функций printf() и scanf() используется спецификация преобразования %с. Некоторые особенности работы с символьными данными уже рассматривались выше. Смотрите, например, разные спосо­ бы "инвертирования" массива символов в §4.2. Продолжая эту тему, рассмотрим следующую задачу.

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

Текст программы:

/* Удаление повторяющихся пробелов */ #include <stdio.h>

void main( )

{

char

z, s;

/* z — текущий вводимый символ */

printf("\n Напишите предложение с точкой VI

for

"в конце:\п");

s=z)

(z=s='

z!='.';

{ /* s - предыдущий символ */

scanf("%c",&z);

') continue;

if

(z==1 ' && s ='

Глава 4. Указатели, массивы, строки

193

printf("%c",z) ;

}/* Конец цикла обработки */

}/* Конец программы */

Впрограмме две символьные переменные: z - для чтения очередного символа и s - для хранения предыдущего. В заго­ ловке цикла переменные z и s получают значения "пробел". Очередной символ вводится как значение переменной z, и пара z, s анализируется. Если хотя бы один из символов значений па­ ры отличен от пробела, то значение z печатается. В заголовке цикла z сравнивается с символом "точка" и при несовпадении запоминается как значение s. Далее цикл повторяется до появ­ ления на входе точки, причем появление двух пробелов (z и s) приводит к пропуску оператора печати.

Пример работы программы:

Напишите предложение с точкой в конце:

YYYYYууууу hhhhh ttttt. YYYYY ууууу hhhhh ttttt.

Помимо scanf() и printf() для ввода и вывода символов в библиотеке предусмотрены специальные функции обмена:

getchar() - функция без параметров. Позволяет читать из входного потока (обычно с клавиатуры) по одному символу за обращение. Как и при использовании функции scanf(), чтение вводимых данных начинается после нажатия клавиши <Enter>. Это дает возможность исправлять вводимую информацию (стирая последние введенные символы с помощью клавиши <Backspace>) до начала ее чтения программой;

putchar(X) - выводит символьное значение X в стандартный выходной поток (обычно на экран дисплея).

Проиллюстрируем применение этих функций на следующей задаче: "Ввести предложение, в конце которого стоит точка, и подсчитать общее количество символов, отличных от пробела (не считая точки)".

/* Подсчет числа отличных от пробелов символов*/ #include <stdio.h>

void main( )

{

1 3 -3 1 2 4

194

Программирование на языке Си

char z ; /* z — вводимый

символ */

int к; /* к - количество значащих символов */ printf("Напишите предложение с точкой в "

 

"конце:\п");

 

for (k=0; (z-getchar(

)

if

(z ?— 1 ') k++;

символов=%<1" ,k) ;

printf ("\n Количество

} /*

Конец программы

*/

В заголовке цикла for выражение z=getchar() заключено в скобки, так как операция присваивания (см. табл. 1.4 на с. 34) имеет более низкий ранг, чем операции сравнения. Если скобки опустить, то последовательность операций будет такой: функ­ ция getchar() введет значение символа и выполнит его сравне­ ние с символом Результат сравнения присвоится переменной z. По смыслу же необходимо введенный символ присвоить пе­ ременной z и сравнить его с точкой.

Результат выполнения программы:

Напишите предложение с точкой в конце:

1

2

3

4

5

6

7

8

9

0.

Количество символов=10

Внутренние коды и упорядоченность символов. В языке принято соглашение, что везде, где синтаксис позволяет исполь­ зовать целые числа, можно использовать и символы, т.е. данные типа char, которые при этом представляются числовыми значе­ ниями своих внутренних кодов. Такое соглашение позволяет сравнительно просто упорядочивать символы, обращаясь с ни­ ми как с целочисленными величинами. Например, внутренние коды десятичных цифр в таблицах кодов ASCII упорядочены по числовым значениям, поэтому несложно перебирать символы десятичных цифр в нужном порядке.

Следующая программа печатает цифры от 0 до 9 и шестна­ дцатеричные представления их внутренних кодов.

/* Печать десятичных цифр: */ #include <stdio.h>

void main( )

Глааа 4. Указатели, массивы, строки

195

{

char z;

for (z='0'; z<='9'; z++)

{

if (z=*'0’ || г=*='5') printf ("\n") ; printf(" %c-%x ", z, z) ;

>

} /* Конец программы */

Результат выполнения программы:

0-30

1-31

2-32

3-33

4-34

5-35

6-36

7-37

8-38

9-39

Обратите внимание на то, что символьная переменная z явля­ ется операндом арифметической операции выполняемой над числовым представлением ее внутреннего кода. Для изо­ бражения значения символов в форматной строке функции printf() используется спецификация %с. Шестнадцатеричные коды выводятся с помощью спецификации %х. В Приложении 1 можно посмотреть, что именно такими являются шестнадцате­ ричные коды символов. Для '0' - код 30, для Т - код 31 и т.д.

Воспользовавшись упорядоченностью внутренних кодов букв латинского алфавита, очень просто его напечатать:

/* Печать латинского алфавита: */ #include <stdio.h>

void main( )

{

char z;

for (z=»A'; z<='Z '; z++)

printf("%c",z);

*/

} /* Конец•программы

Результат выполнения программы:

ABCDEFGHIJKIWNOPQRSTUVWXYZ

Строки, или строковые константы. В программе строки, или строковые константы, представляются последовательно­ стью изображений символов, заключенной в кавычки (не в апо­ строфы), например "любые_символы". Среди символов строки

13*

196

Программирование на языке Си

могут быть эскейп-последовательности, соответствующие ко­ дам неизображаемых (специальных) символьных констант.

Примеры строк:

"1234567890"

"\t Состав президиума"

" Начало строки \п и конец строки".

Строки уже многократно встречались в функциях printf() и scanf().

Однако у строк есть некоторые особенности. Транслятор от­ водит каждой строке отдельное место в памяти ЭВМ даже в тех случаях, когда несколько строк полностью совпадают (стандарт языка Си предполагает, что в конкретных реализациях это пра­ вило может не выполняться). Размещая строку в памяти, транс­ лятор автоматически добавляет в ее конце символ '\0', т.е. нулевой байт. В записи строки может быть и один символ: "А", однако в отличие от символьной константы 'А' (использованы апострофы) длина строки "А" равна двум байтам. В отличие от других языков (например, от Паскаля) в языке Си нет отдельно­ го типа для строк. Принято, что строка - это массив символов, т.е. она всегда имеет тип char[ ]. Таким образом, строка счита­ ется значением типа "массив символов". Количество элементов в таком массиве на 1 больше, чем в изображении соответст­ вующей строковой константы, так как в конец строки добав­ лен нулевой байт '\0'.

Присвоить значение массиву символов (т.е. строке) с помо­ щью обычного оператора присваивания нельзя. Поместить строку в массив можно либо с помощью инициализации (при определении символьного массива), либо с помощью функций ввода. В функции scanf() или printf() для символьных строк используется спецификация преобразования %s.

При определении массива типа char с одновременной ини­ циализацией можно не указывать пределы изменения индекса. Сказанное иллюстрирует следующая программа:

/* Печать символьной строки */ #include <stdio.h>

void main( )

Г/шва 4. Указатели, массивы, строки

197

. {

char В[ ]="Сезам, откройся!"; printf("%s",В);

> /* Конец программы */

Результат выполнения программы:

Сезам, откройся!

В программе длина массива В - 17 элементов, т.е. длина строки, помещаемой в массив (16 символов), плюс нулевой байт окончания строки. Именно 17 байтов выделяется при инициали­ зации массива в приведенном примере. Инициализация массива символов с помощью строковой константы представляет собой сокращенный вариант инициализации массива и введена в язык для упрощения. Можно воспользоваться обычной инициализа­ цией, поместив начальные значения элементов массива в фи­ гурные скобки и не забыв при этом поместить в конце списка начальных значений специальный символ окончания строки '\0'. Таким образом, в программе была бы допустима такая инициа­ лизация массива В:

char В [ ]-{' С ,'е','з','а','м',',',' ,,,о','т',

•к','р','о','й','с','я',•!',’\0• >;

Соглашение о признаке окончания строки нужно соблюдать, формируя в программах строки из отдельных символов. В каче­ стве примера рассмотрим следующую задачу: "Ввести предло­ жение, заканчивающееся точкой, слова в котором отделены пробелами. Напечатать последнее слово предложения".

Анализ условия задачи позволяет выявить следующие оши­ бочные ситуации и особые случаи: отсутствие точки в конце предложения; пробел или пробелы перед первым словом пред­ ложения; несколько пробелов между словами; пробелы перед завершающей предложение точкой; отсутствие слов в предло­ жении (только точка). Чтобы не усложнять решение задачи, примем соглашение о том, что вводимое предложение всегда размещается на одной строке дисплея, т.е. длина его не пре­ вышает 80 символов. Это позволит легко выявлять отсутствие точки в конце предложения и ограничивает длину любого слова

198

Программирование на языке Си

предложения. Чтобы учесть особые ситуации с пробелами, не­ обходимо при анализе очередного введенного символа (переменная s) рассматривать и предыдущий символ (переменная ss). Для выявления отсутствия слов в предложении будем вычислять длину к каждого очередного вводимого слова. Если к = 0 , а вводится символ то это признак пустого пред­ ложения.

Текст программы:

/* Напечатать последнее слово в предложении* */ #include <stdio.h>

void ma±n( )

{

char

s,ss;

/* s

- вводимый символ */

/* ss — предыдущий введенный символ */

char А [80];

/* Массив для слова */

int

i,k;

/* k

- длина слова */

printf("Напишите

предложение с точкой в "

for

"конце:\п");

(i=0,s=' ',k=0; i<=79; i++)

{

ss=s; s=getchar( ); if (s=' ') continue; if (s=='.') break;

if (ss=' ') k=0; A[k]=s; k++;

}

/♦Выход no точке или по окончании ввода строки*/ if (i==80 || k=0)

printf(" Неверное предложение \n"); else

{

A[k]='\0'; /* Конец строки */

printf("Последнее

слово: %s",A);

}

*/

} /* Конец программы

Чтение вводимых данных выполняется посимвольно в цикле с параметром i. Если вводится пробел, то оператор continue вы­ зывает переход к следующей итерации. Если введена точка, то цикл прерывается. При этом в первых к элементах массива А[]

Глава 4. Указатели, массиаы, строки

199

запоминается последнее слово предложения. Если введенный символ отличен от точки и пробела, то анализируется предыду­ щий символ. Если это пробел, то начинается ввод следующего слова и устанавливается нулевое значение к. В следующих опе­ раторах введенный символ записывается в к-й элемент массива А й к увеличивается на 1. Выход из цикла возможен при появ­ лении точки или после ввода 80 символов. Последнее выполня­ ется при отсутствии в предложении точки. В этом ошибочном случае i= 8 0 . Когда в предложении нет слов, к остается равным нулю. Если i<80 и к не равно 0, то в к-й элемент массива А за­ писывается признак конца строки (последнего слова предложе­ ния), и она как единое целое выводится на печать.

Результат выполнения программы:

Напишите предложение с точкой в конце: Орфографический словарь.

Последнее слово: словарь

Как еще раз иллюстрирует приведенная программа, работа с символьными строками - это работа с массивами типа char. При этом следует обращать внимание на обязательное присут­ ствие в конце строки (в последнем занятом элементе массива) символа '\0' и не допускать его случайного уничтожения при обработке.

В качестве следующей задачи рассмотрим проверку вводи­ мой цифровой информации: "Необходимо, введя строку, напе­ чатать порядковый номер (позицию) каждого символа, который отличен от пробела или цифры". Задачу решает следующая про­ грамма:

/* Проверка вводимых числовых данных: */ #include <std±o.h>

void ma±n( )

{

char

z [ ]="0123456789 ";

char

s;

int

i ,j ;

printf("Введите строку символов:\n ") ; for (i=l; (s=getchar( ))!='\n'; i++)

200 Программирование на языке Си

{

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

if (s = 2 [j]) break; if (j = 11)

printf("Ошибка в символе %c с номером " "%d\n", s,i);

) /* Конец цикла ввода */

}/* Конец программы */

В программе (в цикле по i) выполняется ввод отдельных символов до появления неизображаемого символа '\п' - перевод строки. Каждый введенный символ s сравнивается в цикле по j с элементами массива z, содержащего все допустимые символы. Если выявлено совпадение, то при выходе из цикла j не равно 11. При естественном завершении цикла (не найдено совпаде­ ния) j оказывается равным 11, и печатается номер ошибочного символа.

Пример результата выполнения программы:

Введите строку символов: 124Е-22 16 Ошибка в символе Е с номером 4 Ошибка в символе - с номером 5

Строки и указатели. Мы уже рассмотрели взаимосвязь ука­ зателей с массивами, так что возможность связать указатель с каждой строкой нас уже не удивит. Рассмотрим следующие два определения:

char Л [20];

/* Массив, в который можно записать строку */

char *В;

/* Указатель, с которым можно связать строку */

Массив А получает память автоматически при обработке оп­ ределения. Строке, с которой мы хотим связать указатель В, па­ мять при обработке определения указателя не выделяется. Поэтому если далее следуют операторы

scanf("%s",А); /* Оператор верен */

scanf("%s",В); /* Оператор не корректен */

Глава 4. Указатели, массивы, строки

201

то первый из них допустим, а второй приводит к ошибке во время выполнения программы - попытка ввода в неопределен­ ный участок памяти. До выполнения второго из этих операто­ ров ввода с указателем В нужно связать некоторый участок па­ мяти. Для этого существует несколько возможностей. Во-пер­ вых, переменной В можно присвоить адрес уже определенного символьного массива. Во-вторых, указатель В можно "на­ строить" на участок памяти, выделяемый с помощью средств динамического распределения памяти (см. табл. 4.1). Например, оператор

B=(char *) malloc(80);

выделяет 80 байт н связывает этот блок памяти с указателем В. Только теперь применение приведенного выше оператора ввода допустимо. Прототипы функции malloc() и других функций распределения памяти находятся в файле stdlib.h.

С помощью указателей типа char* удобно получать доступ к строкам в виде массивов символов. Типичная задача - обработ­ ка слов или предложений, каждое из которых представлено в массиве типа char в виде строки (т.е. в конце представления предложения или слова находится нуль-символ '\0'). Использо­ вание указателей, а не массивов с фиксированными размерами особенно целесообразно, когда предложения или слова должны быть разной длины. В следующей программе определен и при инициализации связан с набором строк одномерный массив point[ ] указателей типа char*. Функция printf() и специфика­ тор преобразования %s допускают использование в качестве параметра указатель на строку. При этом на дисплей (в выход­ ной поток) выводится не значение указателя point[i], а содержи­ мое адресуемой им строки.

Текст программы:

#±nclude <stdio.h> void main( )

{

char * point! ]={"thirteen","fourteen", "fifteen","sixteen","seventeen","eighteen", "ninteen"};

Соседние файлы в папке книги