Борис Пахомов Санкт Петербург бхв петербург 2013 удк 004. 4 Ббк 32. 973. 26018. 2 П
Скачать 17.38 Mb.
|
символьными строками Для работы с символьными строками в языке С существует ряд функций. Чтобы ими воспользоваться, надо в основную программу (функцию main() ) включить файл string.h. Основные стандартные строковые функции Функция sprintf() sprintf (s, Эта функция родственна функции printf() , которую мы уже рассматривали. Она работает точно также, как и printf() , нов отличие от функции printf() , которая выводит результат своей работы на стандартное выводное устройство (по умолчанию экран, функция sprintf() результат своей работы выводит в строку s . Это очень полезная функция с ее помощью мы можем собрать в одну строку совершенно разнотипные данные, расположенные в переменных arg1 , arg2 ,..., argN , да еще и вставлять между ними необходимый текст, который может находиться между форматами расположенных в управляющей строке Control данных. Функция Эта функция выполняет тоже, что и функция copy() , рассмотренная нами в разд. Головная программа для проверки функций getline(), substr(), copy()" главы 4: она копирует содержимое строки в строку s1 . Признак конца строки — символ 'тоже копируется. Напомним, что строка в языке С представляет собой массив символов (описывается как char s[] ), и что имя массива является адресом его первого элемента s[0] . Это нам пригодится в дальнейшем, когда в качестве аргументов будут выступать не имена массивов, а имена переменных, типы которых мы будем изучать позже. 80 Часть I. Изучение языка С/С++ Функция Эта функция сравнивает две строки (те. содержимое переменных s1 и s2 ) и выдает результат сравнения в виде числового значения. Если s1=s2 , то функция возвращает ноль;если s1 — возвращает положительное число. Это происходит оттого, что функция сравнивает коды символов. Мы знаем, что коды символов в таблице кодирования символов ASCII, на основе которой кодируются символы в языке С, для английского алфавита расположены по возрастанию. Они занимают первую половину (первые 128 значений) таблицы. Вторая половина таблицы (остальные 128 позиций) отдана под национальные кодировки, которые, в общем случае, не упорядочены, это касается и кириллицы тоже. Данный момент надо учитывать при сравнении символьных строк с помощью В теле функции коды строки s1 посимвольно сравниваются с кодами строки s2 посредством вычитания, как обычные числа (а коды и есть числа. Такая обработка символов происходит до первого несовпадения, и результат вычитания выводится в качестве результата работы функции. Повторим, что такой подход возможен, потому что символы английского алфавита в таблице ASCII упорядочены по возрастанию код символа a меньше кода символа b , ив этом смысле строка " a " меньше строки " b ". Поэтому если все символы, расположенные в строках на одинаковых местах, равны, то строки считаются равными, в противном случае одна строка либо меньше, либо больше другой. Таким образом, строки, содержащие текст на кириллице, сравнивать с помощью этой функции нельзя.Следует отметить, что одинаковые символы, введенные в разных регистрах (те. большие и маленькие буквы, различаются. Это и понятно у них в таблице ASCII разные коды. Функция Эта функция работает также, как и strcmp() , но регистров не различает (для нее, например, символ a совпадает с символом A ). Функция Это функция сцепления (как говорят, конкатенации) двух строк. Содержимое строки дописывается вконец строки s1 , и результат пересылается в Функция Эта функция возвращает (возвращает, значит, можно писать, например, int y=strlen(s) ) длину строки s (те. количество символов в строке) без учета символа '\0 ' — признака конца строки. Глава 5. Функции для работы с символьными строками Пример программы проверки функций Напишем программу, на примере которой проследим, как работают рассмотренные функции (листинг 5.1). Листинг 5.1 // 5.1_2011.cpp #include "stdafx.h" #include #include #include #include #include #define eof -1 //Ctrl+z #define maxline 1000 /* Функция getline(s,lim) вводит с клавиатуры строку в s и возвращает длину введенной строки с учетом cимвола '\0'; lim — максимальное количество символов, которое можно ввести в строку s*/ int getline(char s[],int lim) { int c,i; for(i=0; i //------------------------------------------- int main() { Программы работы со строками в С использование sprintf()------------ int x; float y; char s1[maxline]; char c,c1,ot[5],v1[maxline]; do { printf("Enter int n for sprintf()...>"); getline(ot,5); int x=atoi(ot); 82 Часть I. Изучение языка С/С++ printf("Enter float m for sprintf() >"); getline(ot,5); float y=atof(ot); printf("Enter string for sprintf(). >"); getline(s1,maxline); sprintf(v1,"%d %f %s",x,y,s1); printf("v=%s\n",v1); printf("continue } while((c1=getchar()) != eof) ; использование strcpy()-------------- char s2[maxline],v2[maxline]; while((c=getchar()) != eof) { printf("\n\nEnter string for strcpy() >\n"); getline(s2,maxline); strcpy(v2,s2); printf("Copied string=%s\n",v2); printf("Continue } _getch(); использование strcmp(), strlen()----------- char s3[maxline],v3[maxline]; while((c=getchar()) != eof) { printf("\n\nEnter string1 for strcmp() >"); getline(s3,maxline); printf("Enter string2 for strcmp() >"); getline(v3,maxline); int i=strcmp(s3,v3); printf("strcmp's value=%d\nstring1's length=%d\n ",i,strlen(s3)); if(i==0) printf("string1 = string2\n"); if(i>0) printf("string1 > string2\n"); if(i<0) printf("string1 < string2\n"); printf("continue } _getch(); Глава 5. Функции для работы с символьными строками использование strcat()-------------- char s4[maxline],v4[maxline]; while((c=getchar()) != eof) { printf("\n\nEnter string1 for strcat() >"); getline(s4,maxline); printf("Enter string2 for strcat() >"); getline(v4,maxline); printf("strcat's value=%s\n",strcat(s4,v4)); printf("continue } _getch(); } Для ввода данных мы использовали ранее рассмотренную нами функцию getline() , которая вводит строку символов. Но для наших целей нам требуется вводить числа. Поэтому мы их вводим с помощью getline() как текста потом преобразуем в формат int с помощью функции atoi(s) , которая преобразует строковые данные в целое число и возвращает это число. Если в строке не числовые данные, то функция возвратит ноль. Другая функция, использованная нами при переводе строковых данных в формат float , — это функция atof(s) . Она возвращает число в формате float , преобразуя свою входную строку. И также возвращает ноль, если в строке не числовые данные. Для ввода чисел мы использовали массив char ot[5] , поскольку число, вводимое в примере, не превзойдет пяти цифр (ввести больше не позволит функция getline() : так мы ее сформировали. В этой программе мы встретились с новым оператором do...while . Он работает, как оператор while , нос тем отличием, что оператор while проверяет условие продолжения окончания цикла в своей заголовочной части, а оператор do...while — в конце. Если нарушается условие продолжения оператора while , то его тело пропускается. Нов жизни бывают случаи, когда требуется, чтобы цикл while выполнился хотя бы один раз. Для этого и используют пару do...while , в которой условие продолжения проверяется в конце цикла, поэтому тело оператора будет выполнено хотя бы один раз. Формат оператора таков do { тело оператора (условие цикла) ; Точка с запятой обязательна. Для ввода нескольких вариантов данных в этой проверочной программе потребовалось внедрить так называемое зацикливание поставить оператор while , который обеспечивает зацикливание за счет запроса ввода символа либо для продолжения ввода другого варианта данных, либо для выхода из участка проверки. Нона первом участке удобно проводить проверку на продолже- 84 Часть I. Изучение языка С/С++ ние ввода вариантов данных не вначале участка, а в конце, чтобы первый вариант данных вводился без проверки. Иначе пошел бы запрос на ввод символа для проверки на продолжение ввода программа ожидала бы ввода, на экране бы мигал один курсор, и пользователю было бы непонятно, что же надо дальше делать. Поясним немного, что сделала функция Для ее проверки мы ввели два числа одно в формате int , другое в формате float и строку (в формате s ), чтобы показать, что sprintf() их обработает по форматам в управляющей строке функции мы задали эти форматы) и соберет в единую строку, включив в нее и символы, которые находились между полями, задающими форматы (те. между полями, начинающимися со знака % и оканчивающимися одним из символов форматирования d , f , s , ...). Для функции strcmp() мы вывели значение, которое она возвращает, чтобы читатель мог удостовериться, что это есть разность между первыми несравнившимися кодами символов. Попробуйте определить, какие символы первыми не сравнились, найдите их коды это можно сделать, воспользовавшись стандартной функцией имя символа) ,которая возвращает код символа, указанного у нее в аргументе например, int a=char('a') ). Результат работы проверочной программы приведен на рис. 5.1. Рис 5.1. Результат работы программы листинга 5.1 ГЛАВА Дополнительные сведения о типах данных, операциях, выражениях и элементах управления Новые типы переменных Мы уже знаем, что в языке Снаряду с рассмотренными типами переменных ( int , char , float ) существуют и другие типы данных, сведения о которых необходимо уточнить double — указывает, что данные имеют тип "с плавающей точкой" двойной точности указывает, что данные имеют тип "целое со знаком, но по сравнению сданными типа int занимают в 2 раза больше памяти short int — короткое целое со знаком, занимает в 2 раза меньше памяти, чем int ; long double — длинное удвоенное с плавающей точкой unsigned char — если в переменной, объявленной с таким типом данных, будет находиться число (которое, естественно, будет изображено кодами цифр, то знаковый бит такого числа будет подавлен, те. не будет учитываться как знак числа, а только как элемент числа. Это исказит размер отрицательных чисел. Знаковый бит всегда располагается в старшем (находящемся слева) разряде. Биты в записи числа нумеруются справа налево 0, 1, 2,... Это означает, что если под число отведено 4 байта (те бита, то самый старший бит 31 - й. Попробуйте выполнить следующую программу, используя проверку содержимого переменных с помощью точек останова, образованных отладчиком main() { int i=-10; unsigned int j=i; } 86 Часть I. Изучение языка С/С++ Вы убедитесь, что число j огромно. И все из-за того, что знаковый разряд, в котором была единица (т. к. число в переменной i отрицательное, стал участвовать в величине числа — стал его неотъемлемой частью (в этом и смысл квали- фикатора unsigned : он подавляет знак в числе, те. число становится числом без знака. Но, как известно, знак числа находится в старшем разряде ячейки, в которой находится число, и значение этого знакового разряда физически никуда не девается, когда число получает квалификатор unsigned . Поэтому, если число положительное, те. в его знаковом разряде стоит ноль, то квалификатор unsigned не меняет значения числа, т. к. этот ноль не повлияет на величину числа (вспомните, что любое число разлагается по степеням, например, двойки, и если знаковый разряд находится в разряде n, то слагаемое разложения будет иметь вид 0 * 2 n–1 = 0). Если же число отрицательное, тов его знаковом разряде будет стоять единица, а величина слагаемого в разложении числа по степеням двойки станет уже равной 1 * 2 n–1 = 2 n–1 . Вот на эту величину и изменится отрицательное число, если к нему применить атрибут unsigned ; unsigned int — аналогичен типу unsigned char ; unsigned long — длинное целое без знака. Последствия того, что переменная имеет тип "без знака, для отрицательных чисел уже рассмотрены. Кроме того, существуют также следующие типы данных enum ; bool enum — так называемый перечислимый тип данных. Он позволяет задавать мнемонические значения для множеств целых значений (те. обозначать данные в соответствии сих смыслом. Допустим, нам надо работать в программе с наименованиями дней недели (например, проверять, что текущий день — понедельник. Пока не было типа данных enum , надо было как-то задавать дни недели числами и работать с этими числами. Для дней недели это и не особенно сложно каждый помнит, что седьмой день это воскресенье, а первый — понедельник (хотя в английском варианте первым днем считается как раз воскресенье. Но бывают множества, как говорят, и "покруче", чем дни недели, элементы которых не упомнишь. Поэтому с помощью типа enum можно добиться большей наглядности и лучшего понимания программы. Приведем пример программы с использованием типа листинг. Листинг 6.1 int main() { enum days {sun, mon, tues, wed, thur, fri, sat}anyday; enum sex {man, wom}pol; anyday=sun; pol=wom; if(anyday==0 && pol == wom) можно писать либо anyday==sun, либо anyday==0; */ } Глава 6. Дополнительные сведения о типах данных, операциях, выражениях Запись enum days {sun, mon, tues, wed, thur, fri, sat} anyday — пример объявления переменной days перечислимого типа. Все, что в фигурных скобках, — это заданные заранее значения переменной days . Сама переменная days задает как бы шаблон, который самостоятельно применять нельзя это обычный тип данных. Следует объявить дополнительно другую переменную этого типа ее имя можно записать сразу при объявлении шаблона между последней фигурной скобкой и двоеточием (у нас это переменная anyday ). Можно было бы записать объявление так enum days {sun, mon, tues, wed, thur, fri, sat}; ввели тип days anyday,otherdays; объявили 2 переменные данного типа В любом случае после этого переменной anyday (или, как во втором случае, и переменной) можно присваивать значения из определенного в переменной enum списка Если список приведен в виде enum days {sun, mon, tues, wed, thur, fri, sat} , то подразумевается, что его элементы имеют последовательные целые числовые значения. (те. вместо anyday==sun можно писать anyday==0 ). Список, указанный в enum , можно при объявлении инициализировать другими целыми значениями. Например, имеем перечислимый тип "кувшины (объемы в литрах enum {Vol1=5, Vol2=7, Vol3=9,Vol4,Vol5,Vol6} У первого, второго и третьего элементов этого множества числовые значения соответственно равны 5, 7 и 9. Остальным элементам, неопределенным нами, компилятор присвоит последовательные (через единицу) значения, начиная со значения последнего определенного элемента тес получит значение те. В приведенной выше программе заданы две переменные перечислимого типа дни) и sex (пол. Это пока шаблоны (типы. А на их основе определены собственно "рабочие" перечислимые переменные тете, с которыми и надо работать. Затем определенным таким образом переменным можно присваивать значения элементов перечислимых множеств и сравнивать их. bool — этот тип заимствован из языка С+ (расширения С. Переменные этого типа могут принимать только два значения false (ложь) и true (истина. Они используются для проверки логических выражений. Числовые значения false (ложь) и истина) заранее предопределены значение false численно равно нулю, а значение true — единице. Эти так называемые литералы (постоянные символьные значения) сами выступают в роли переменных — им можно присваивать значения (соответственно, нуль и единицу. Вы можете преобразовать некоторую переменную типа bool в некоторую переменную типа int . Такое числовое преобразование устанавливает значение false равным нулю, а значение true — единице. Вы можете преобразовать перечислимые и арифметические типы в переменные типа bool : нулевое значение преобразовывается в false , а ненулевое — в true 88 Часть I. Изучение языка С/С++ Существует одноименная функция bool() , которая преобразует арифметические типы в булевы. В листинге 6.2 приведен пример программы с использованием булевых переменных и булевой функции. Поставьте с помощью отладчика точку останова на первом операторе и двигайтесь по программе пошагово, каждый раз нажимая клавишу int main() { проверка преобразований типов int, float в bool int i=2; bool b=i; float j=2.2; bool a=bool(j); j=0.0; a=bool(j); } Константы В языке С поддерживаются следующие типы констант (постоянных величин целые, вещественные, перечислимые и символьные. Вещественные константы — это, в общем случае, нецелые числа, которые представлены в виде чисел с плавающей точкой. Перечислимые константы — это значения элементов перечислимого множества значения такого типа постоянны. Целые константы. Например int i = Плавающая точка может определяться так float или Обе записи равноценны. Целые числа, помимо десятичных, могут быть также восьмеричными и шестнадцатеричными. Первые пишутся как int i = 0-1; (записано число –1 в 8-ричной системе, тес нулем впереди, а вторые как int i = 0xa; тес впереди. Среди констант символьного типа различают собственно символьные константы и строковые константы. Первые обозначаются символами в одинарных кавычках апострофах, вторые — в двойных. Отличие строковых констант от символьных Глава 6. Дополнительные сведения о типах данных, операциях, выражениях в том, что у строковых констант в конце всегда стоит признак конца строки — символ (так записывается нуль как символ. Этот признак формирует компилятор, когда встречает выражение вида char или вида char *s="asdf"; (здесь для иллюстрации применена конструкция "указатель, око- торой речь пойдет в следующих главах. Символьные константы имеют вид char или с c='\n'; char В первом случае так задаются константы для символов, которые отображаются на экране (это все символы таблицы ASCII с кодами от 32 и далее. Во втором случае — для символов, которые не имеют экранного отображения и используются как управляющие (это символы с кодами 0—31 в таблице ASCII). Второй вид записи — это так называемые последовательности. Сих помощью можно записывать не только управляющие, но и любые символы. В этом случаев качестве элементов последовательности выступят сами коды символов. Например, код символа 0 по таблице ASCII равен 48. В виде последовательности его можно записать как char v='\060'; (48 = 060). Новые операции Поговорим об операциях, с которыми еще не встречались в рассмотренных ранее примерах. Операции отношения == равно != неравно больше или равно > (больше <= меньше или равно < меньше. Если две переменные сравниваются с помощью операций отношения, то результат сравнения всегда бывает булевого типа, те. либо ложен, либо истинен. Поэтому мы можем, например, писать int i=34; int j=i * 25; bool В данном примере, кстати, ат. к. i 90 Часть I. Изучение языка С/С++ Операции отношения бывают очень полезны. Допустим, в вашей программе цикл выражение) {...} работает не так, как вам бы того хотелось. Вы никак немо- жете разобраться, в чем дело, поскольку выражение в операторе while настолько громоздко, что вы не можете его вычислить в момент отладки программы. Тогда это выражение можно присвоить некоторой (объявляемой вами) булевой переменной. Теперь вы сможете в режиме отладчика увидеть значение этого выражения и принять соответствующие меры. Надо учитывать, что операции отношения по очередности (приоритету) их выполнения младшеарифметических операций. Если вы напишете if(i < j-1) , то при вычислении выражения в if() сначала будет вычислено j-1 , а затем результат будет сравниваться с содержимым переменной Логические операции && (и || (или. Тип выражения, в котором участвуют эти операции, будет булевым, как и для операций отношения. Выражение a && b будет истинным только тогда, когда истинны оба операнда (операнды имеют булевы значения. Выражение a || b будет истинным только тогда, когда хоть один из операндов истинен. Следует иметь ввиду, что компилятор так строит алгоритм вычисления выражений, связанных этими операциями, что выражения вычисляются слева направо, и вычисление прекращается сразу, как только становится ясно, будет ли результат истинен или ложен. Поэтому при формировании выражений подобного рода следует их части, которые могут оказать влияние на полный результат первыми, располагать первыми, что экономит время. Унарная (те. воздействующая только на один операнд) операция ! (не. Этологическая операция отрицания. Она преобразует операнд, значение которого неравно нулю или истине, в нуль, а нулевой или ложный — в единицу. Результат этой операции представляет собой булево значение. Иногда ее используют в записи вида if(!a) вместо Действительно, по определению оператора условие в его скобках всегда должно быть истинным, поэтому получается, что !a=1 истина. Тогда ат. е. в записи по правилам С это будет выглядеть как К унарным относятся также операции "одиночный плюс" и "одиночный минус, явно устанавливающие знаки чисел или значений переменных. Например int x = -1; float z=12.5; float y = -z; Глава 6. Дополнительные сведения о типах данных, операциях, выражениях Преобразование типов данных Современные компиляторы многое берут на себя, а неопытный программист этого не замечает и потому должным образом не оценивает происходящее. Но все жена- до иметь представление о преобразованиях типов данных, потому что тот жене- опытный программист часто заходит в тупик в очевидных ситуациях и недоуменно разводит руками "Чего это оно не идет Не понимаю. Ане понимает, потому что избалован возможностями современных компиляторов, при которых он родился и вырос. Но они его (если программист не очень грамотный) иногда подводят, и бывает очень сильно. Итак, при составлении программ все-таки надо знать, что в выражениях обычно участвуют данные разных типов, как ив операциях присвоения, при которых левая часть имеет один типа правая другой. И чтобы как-то свести концы с концами, установлены соответствующие правила преобразований данных разных типов. Преобразования осуществляются для тех типов данных, для которых это имеет смысл. При вычислении выражений, в которые входят данные разных типов, компилятор строит программу так, что все данные разных типов преобразуются к общему типу последующим правилам типы int и char могут свободно смешиваться в арифметических выражениях, т. к. перед вычислением переменная типа char автоматически преобразуется в int (конечно, если оба типа относятся к числу. Поэтому когда мы видим, что символ может быть отрицательным числом (например, –1), то его лучше помещать в переменную, объявленную как int ; к каждой арифметической операции применяются следующие правила низший тип всегда преобразуется в высший в int , float в double , int в long и т. д при присвоении тип значения правой части всегда преобразуется в тип левой части. Отсюда надо учитывать, что если переменная, расположенная справа от знака присвоения, имеет типа переменная, расположенная слева — int , то произойдет преобразование в тип int , и дробная часть значения переменной типа float будет отброшена если справа расположена переменная типа double , а слева переменная типа float , то произойдет преобразование в тип float с округлением если справа расположена переменная типа long , а слева переменная типа int , то произойдет преобразование в тип int , при этому значения переменной справа будут отброшены старшие биты (вот это может быть погрешность любое выражение может быть приведено к желаемому типуне автоматически при преобразованиях, а принудительно с помощью конструкции имя типа) выражение. Такие преобразования называют кастингом. 92 Часть I. Изучение языка С/С++ Например, существует стандартная функция число, которая выделяет указанное в ее аргументе количество байтов памяти и которая возвращает адрес выделенного участка. Но функция возвращает адрес "неопределенного типа" (ее тип — void ), те. непонятно, данные какого типа мы можем размещать в выделенной области. Чтобы настроиться на выделенную область, в которой хотим, например, размещать данные типа char , мы должны неопределенный выход функции привести к нашему типу с помощью кастинга число (об адресах будем говорить в следующих главах. Побитовые логические операции Эти операции выполняются над соответствующими битами чисел, имеющими целый тип. Если операнд — нецелое число, то оно автоматически преобразуется в целое. Побитовые операции таковы & — поразрядное умножение (конъюнкция. Формат int a, b=3, c=4; a=b & c; | — поразрядное сложение (дизъюнкция) или включающее "или. Формат int a, b=3, c=4; a=b | c; ^ — поразрядное исключающее "или. Формат int a, b=3, c=4; a=b ^ c; — операция дополнения. Формат int a, b=3, c=4; a=b c; >> — сдвиг разрядов вправо. Формат int a, b=3, c=4; a=b >> c; << — сдвиг разрядов влево. Формат int a, b=3, c=4; a=b << Побитовые логические операции выполняются по правилам, приведенным в табл. 6.1. Таблица Правила выполнения побитовых логических операций |