КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Использование встроенного отладчика. 4 страница
fputs(crecord,stdout); fclose(pfinfile);
StopWait(); /* Wait a little */ return (0); }
Поскольку максимальный размер записи равен 40, то необходимо зарезервировать 41 ячейку в памяти; дополнительная ячейка должна хранить null-символ \0. Программа не использует при печати записей дополнительный символ перевода строки; расчет делается на символ перевода строки, считанный в символьный массив функцией fgets(). Функция fputs() печатает содержимое символьного массива crecord в файл stdout, на который указывает указатель файла. Если программа обращается к файлу в каталоге, отличном от того, в котором располагается компилятор, то в имени файла нужно указать полный путь. Обратите на это внимание в приведенном примере; две обратные наклонные черты (\\) синтаксически необходимы для указания подкаталога. Напоминаем, что одна наклонная черта (\) обычно указывает на то, что далее следует управляющая последовательность или продолжение строки. Хотя функции gets() и fgets() используются аналогичным образом, функции puts() и fputs() работают по-разному. Функция fputs() записывает в файл и имеет два параметра: адрес символьной строки, заканчивающейся null-символом, и указатель на файл; fputs() просто копирует строку в указанный файл. Символ перевода строки в конце строки не добавляется. Однако макрос puts() не требует указателя на файл, поскольку вывод автоматически назначается на stdout; в конце выводимой строки автоматически добавляется символ перевода строки. Функция считывания строки gets() очень удобна для диалоговых систем. Она получает строку от стандартного устройства ввода вашей системы, которым, как мы предполагаем, является клавиатура. Поскольку строка не имеет заранее заданной длины, функция gets() должна знать, когда ей прекратить работу. Функция читает символы до тех пор, пока ей не встретится символ новой строки (' \n'), который вы создаете, нажимая клавишу [ввод]. Функция берет все символы до (но не включая) символа новой строки, присоединяет к ним нуль-символ ('\0') и передает строку вызывающей программе. Вот простой способ использования функции:
/* получение имени1 */ #include <stdio.h> void main () { char name[81]; /* выделение памяти */ printf("Privet, kak vas zovyt?\n"); gets(name); /* размещение введенного имени в строку "name" */ printf("Horoshee imya, %s.\n", name); } Функция примет любое имя (включая пробелы) длиной до 80 символов. (Не забудьте запасти один символ для ' \0'.) Отметим, что мы хотели при помощи функции gets() воздействовать на нечто (name) в вызывающей программе. Значит, нужно использовать указатель в качестве аргумента; а имя массива, конечно, является его указателем. Функция gets () обладает большими возможностями, чем показано в последнем примере. Взгляните на эту программу:
/* получение имени2 */ #include <stdio.h> void main () { char name [80]; char *ptr, *gets (char *string); printf(" Привет, как вас зовут?\n"); ptr = gets(name); printf(" %s? Ax! %s!\n", name, ptr); } Получился диалог:
Привет, как вас зовут? Тони де Туна Тони де Туна? Ах! Тони де Туна!
Функция gets () предоставляет вам два способа ввода строки! · Использует метод указателей для передачи строки в name. · Использует ключевое слово return для возврата строки в ptr. Напомним, что ptr является указателем на тип char. Это означает, что gets() должна вернуть значение, которое является указателем на тип char. И в приведенном выше изложении вы можете увидеть, что мы так и описали gets ().
Описание вида
char *gets ();
говорит о том, что gets () является функцией (отсюда круглые скобки) типа «указатель на тип char» (поэтому * и char). В примере получение имени1 мы обходились без этого описания, потому что мы никогда не пытались использовать возвращенное значение функции gets (). Между прочим, вы можете также описать указатель на функцию. Это выглядело бы следующим образом:
char (*foop) ();
и foop был бы указателем на функцию типа char. Мы расскажем немного подробнее о таких причудливых описаниях позднее. Структура функции gets () выглядела бы примерно так:
char *gets(s); char *s; { char *p; ... return (p); }
На самом деле структура немного сложнее, и для gets () есть две возможности возврата. Если все идет хорошо, она возвращает считанную строку, как мы уже сказали. Если что-то неправильно или если gets() встречает символ EOF, она возвращает NULL, или нулевой адрес. Таким образом gets () включает разряд проверки ошибки. Поэтому данная функция удобна для использования в конструкциях, подобных
while (gets(name)!= NULL)
где NULL определен в файле stdio.h как 0. При помощи указателя массиву name присваивается значение. Наличие возврата позволяет присваивать значение всей gets(name) и выполнять проверку на EOF. Этот двоякий подход более компактен, чем использование функции getchar (), которая имеет возврат без аргумента.
while ((ch = getchar ())! = EOF)
Мы должны полагаться на библиотечные функции, которые могут немного изменяться от системы к системе. Функции puts() и printf() — две рабочие лошадки, используемые при выводе строк. Это очень простая функция; у нее есть только один аргумент, являющийся указателем строки. Нижеследующий пример иллюстрирует некоторые из многих способов ее применения.
/* простые выдачи */ #include <stdio.h> #define DEF "Я строка #define." void main () { static char str1[] = "Массив инициализирован мной."; static char *str2 = "Указатель инициализирован мной."; puts(" Я аргумент функции puts ()."); puts(DEF); puts(str1); puts(str2); puts(&str1[4]); puts(str2+4); }
В результате работы программы получаем
Я аргумент функции puts (). Я строка #define. Массив инициализирован мной Указатель инициализирован мной. ив инициализирован мной. атель инициализирован мной.
Этот пример напоминает нам, что фразы в кавычках и имена строк символьных массивов являются указателями. Обратите внимание на два последних оператора. Указатель &str1[4] ссылается на пятый элемент массива str1. Этот элемент содержит символ 'и', и функция puts () использует его в качестве начальной точки. Аналогично str2 + 4 ссылается на ячейку памяти, содержащую 'а' в «указателе», и с нее начинается вывод строки. Как puts () узнает, когда остановиться? Она прекращает работу, если встречает нуль-символ, поэтому лучше, чтобы он был. Не пытайтесь делать так!
/* нет строки1 */ main () { static char dont[] = ('H', 'I', '!','!'); puts(dont); /* dont не является строкой */ }
Поскольку в dont отсутствует завершающий нуль-символ, она не является строкой. Так как нуль-символ отсутствует, puts () не знает, когда ей останавливаться. Она будет просто перебирать ячейки памяти, следующие за dont до тех пор, пока не найдет где-нибудь нуль-символ. Если повезет, она, может быть, найдет его в ближайшей ячейке, но может и не повезти. Обратите внимание, что любая строка, вводимая функцией puts(), начинается с новой строки. Если puts() в конце концов находит завершающий нуль-символ, она заменяет его символом «новой строки» и затем выводит строку.
7.26. Функции, работающие со строками. Большинство библиотек языка Си снабжено функциями, работающими со строками. Рассмотрим четыре наиболее полезных и распространенных: strlen (), strcat (), strcmp () и strcpy(). Мы уже применяли функцию strlen (), которая находит длину строки. Используем ее в нижеследующем примере функции, укорачивающей длинные строки.
7.26.1. Функция strlen().
/* Функция Прокруста */ fit(string, size) char *string; int size; { if (strlen(string) > size) *(string + size) = ' \0'; }
Проверьте ее в «деле» в этой тестовой программе:
/* тест */ main () { static char mesg[] = "Ну, теперь держитесь, компьютероманы."; puts(mesg); fit(mesg, 10); puts(mesg); }
Программа выдает:
Ну, теперь держитесь, компьютероманы. Ну, теперь Текст программы имеет вид:
#include<stdio.h> #include <string.h> void fit(char *string, int size); void main () { static char mesg[] = "Ну, теперь держитесь, компьютероманы."; puts(mesg); fit(mesg, 10); puts(mesg); } void fit(char *string,int size) { if (strlen(string) > size) *(string + size) = ' \0'; }
Наша функция помешает символ '\0' в одиннадцатый элемент массива, заменяя символ пробела. Остаток массива остается на старом месте, но puts () прекращает работу на первом нуль-символе и игнорирует остаток массива.
7.26.2. Функция strcat(). Вот что умеет делать функция strcat ():
/* объединение двух строк */ #include <stdio.h> #include<string.h> void main () { static char flower [80]; static char addon[] = "ы пахнут старыми ботинками."; puts(" Назовите ваш любимый цветок."); gets(flower); strcat(flower, addon); puts(flower); } Получаем на экране:
Назовите ваш любимый цветок. Ирис Ирисы пахнут старыми ботинками.
Очевидно, что strcat () (string concatenation) использует в качестве аргументов две строки. Копия второй строки присоединяется к концу первой, и это объединение становится новой первой строкой. Вторая строка не изменяется. Внимание! Эта функция не проверяет, умещается ли вторая строка в первом массиве. Если вы ошиблись при выделении памяти для первого массива, то у вас возникнут проблемы. Конечно, можно использовать strlen () для определения размера строки до объединения.
/* Объединение двух строк, проверка размера первой */ #include< stdio.h> #include<string.h> #define SIZE 80 void main () { static char flower [SIZE]; static char addon[] = "ы пахнут старыми ботинками."; puts("Назовите ваш любимый цветок."); gets(flower); if ((strlen(addon) + strlen(flower) + 1) < SIZE) strcat(flower, addon); puts(flower); } Мы добавляем 1 к объединенной длине для размещения нуль-символа.
7.26.3. Функция strcmp(). Предположим, что вы хотите сравнить чей-то ответ со строкой, находящейся в памяти:
/* Будет ли это работать? */ #include <stdio.h> #define ANSWER "Grant" main () { char chance [40]; puts("Who was buried in the Grant's grave?"); gets(chance); while(chance!= ANSWER) { puts(" No, incorrectly. Try again, please."); gets(chance); } puts (" You all right!"); }
Хотя эта программа и смотрится неплохо, она не будет работать правильно,chance и ANSWER на самом деле являются указателями, поэтому сравнение (chance!= ANSWER) спрашивает не о том, одинаковы ли эти две строки, а одинаковы ли два адреса, на которые ссылаются chance и ANSWER. Так как ANSWER и chance запоминаются в разных ячейках, эти два указателя никогда не могут быть одним и тем же, и пользователю всегда сообщается, что программа неверна. Такие программы обескураживают людей. Нам нужна функция, которая сравнивает содержимое строк, а не их адреса. Можно было бы придумать ее, но это уже сделала за нас функция strcmp () (string comparision). Теперь исправим нашу программу:
/* это будет работать */ #include <stdio.h> #include <string.h> #define ANSWER "Grant" void main () { char chance [40]; puts("Who was buried in the Grant's grave?"); gets(chance); while (strcmp(chance, ANSWER)!= 0) { puts(" No, incorrectly. Try again, please."); gets(chance); } puts(" You all right!"); }
Так как ненулевые значения интерпретируются всегда как «true», мы можем сократить оператор while до while (strcmp(chance, ANSWER)). Из этого примера можно сделать вывод, что strcmp () использует два указателя строк в качестве аргументов и возвращает значение 0, если эти две строки одинаковы. Прекрасно, если вы придете к такому выводу. Хорошо, что strcmp() сравнивает строки, а не массивы. Поэтому, хотя массив chance занимает 40 ячеек памяти, а " Grant" — только 6 (не забывайте, что одна нужна для нуль-символа), сравнение выполняется только с частью try, до его первого нуль-символа. Такую функцию Strcmp () можно использовать для сравнения строк, находящихся в массивах разной длины. А что если пользователь ответил " GRANT" или "grant" или "Улиссес С. Грант"? Хорошо, если пользователю сказали, что он ошибся? Чтобы сделать программу гибкой, вы должны предусмотреть несколько допустимых правильных ответов. Здесь есть некоторые тонкости. Вы могли бы в операторе #define определить в качестве ответа "GRANT" и написать функцию, которая превращает любой ответ только в это слово. Это устраняет проблему накопления, но остаются другие поводы для беспокойства. Между прочим, какое значение возвращает strcmp(), если строки не одинаковы? Вот пример:
/* возвраты функции strcmp */ #include <stdio.h> #include<string.h> void main () { printf("%d\n", strcmp("A", "A")); printf("%d\n", strcmp("A", "B")); printf("%d\n", strcmp("B", "A")); printf(" %d\n", strcmp("C", "A")); printf(" %d\n", strcmp("apples", "apple")); } В результате получаем
-1
Как мы и предполагали, сравнение "А" с самим собой возвращает 0. Сравнение "А" с "В" дает -1, а "В" с "А" дает 1. Это наводит на мысль, что strcmp() возвращает отрицательное число, если первая строка предшествует второй в алфавитном порядке, или положительное число, если порядок иной. Кроме того, сравнение "С" с "А" дает 2 вместо 1. Картина проясняется: функция возвращает разницу между двумя символами в коде ASCII. В более общем смысле strcmp() передвигается вдоль строк до тех пор, пока не находит первую пару несовпадающих символов; затем она возвращает разницу в кодах ASCII. Например, в самом последнем примере "apples" и "apple" совпадают, кроме последнего символа 's', в первой строке. Он сопоставляется с шестым символом в "apple", который является нуль-символом (0 в ASCII). Возвращается значение
's' - '\0' = 115 - 0 = 115,
где 115 является кодом буквы 's' в ASCII. Обычно вам не нужно точно знать возвращаемое значение. Чаще всего вы только хотите знать, нуль это или нет, т. е. было ли совпадение. Или, может быть, вы пытаетесь отсортировать строки в алфавитном порядке и хотите узнать, в каком случае сравнение дает положительный, отрицательный или нулевой результат. Можно использовать эту функцию, чтобы проверить, остановится ли программа, читая вводимую информацию:
/* Начало какой-то программы */ #include < stdio.h> #define SIZE 81 #define LIM 100 #define STOP " "/* нулевая строка */ main () { static char input [LIM] [SIZE]; int ct = 0; while(gets(input[ct])!= NUL && strcmp(input[ct], STOP)!= 0 && ct++ < LIM) ... }
Программа прекращает чтение вводимой строки, если встречает символ EOF [в этом случае gets () возвращает NULL], или если вы нажимаете клавишу [ввод] в начале строки (т. е. введете пустую строку), или если вы достигли предела LIM. Чтение пустой строки дает пользователю простой способ прекращения ввода. Давайте перейдем к последней из обсуждаемых нами функций, работающих со строками.
7.26.4. Функция strcpy(). Мы уже говорили, что если pts1 и pts2 являются указателями строк, то выражение
pts2 = pts1;
копирует только адрес строки, а не саму строку. Предположим, что вы все же хотите скопировать строку. В этом случае можно использовать функцию strcpy (). Она работает примерно так:
/* демонстрация strcpy () */ #include <stdio.h> #include <string.h> #define WORDS "Проверьте, пожалуйста, вашу последнюю запись. " void main () { static char *orig = WORDS; static char copy [40]; puts(orig); puts(copy); strcpy(copy, orig); puts(orig); puts(copy); } Вот результат:
Проверьте, пожалуйста, вашу последнюю запись.
Проверьте, пожалуйста, вашу последнюю запись. Проверьте, пожалуйста, вашу последнюю запись.
Очевидно, что строка, на которую указывает второй аргумент (orig) функции strcpy (), скопирована в массив, на который указывает первый аргумент (сору). Порядок аргументов функции такой же, как в операторе присваивания: строка, получающая значение, стоит слева. (Пустая строка является результатом печати массива сору до копирования, и она говорит о том, что статические массивы инициализируются нулями, т. е. нуль-символами в символьном виде.) Нужно обеспечить, чтобы размер массива, принимающего строку, был достаточен для ее размещения. Поэтому мы используем описание
static char copy [40];
а не
static char *copy; /* не выделяет память для строки */
Короче говоря, strcpy() требует два указателя строк в качестве аргументов. Второй указатель, ссылающийся на исходную строку, может быть объявленным указателем, именем массива или строковой константой. А первый указатель, ссылающийся на копию, должен ссылаться на массив или часть массива, имеющего размер, достаточный для размещения строки. Теперь, когда мы описали несколько функций, работающих со строками, рассмотрим целую программу, работающую со строками. Пример: сортировка строк. Возьмем реальную задачу сортировки строк в алфавитном порядке. Эта задача может возникнуть при подготовке списка фамилий, при создании алфавитного указателя и во многих других ситуациях. В такой программе одним из главных инструментов является функция strcmp(), так как ее можно использовать для определения старшинства двух строк. Последовательность наших действий будет состоять из считывания массива строк, их сортировки и последующего вывода. Совсем недавно мы показали последовательность действий для считывания строк, и сейчас мы начнем программу таким же образом.
/* считывает строки и сортирует их */ #include <stdio.h> #define SIZE 81 /* предельная длина строки, включая\0 */ #define LIM 20 /* максимальное количество считываемых строк */ #define HALT " " /* нулевая строка для прекращения ввода */ main () { static char input [LIM] [SIZE]; /* массив для запоминания вводимых строк */ char *ptstr[LIM]; /* массив переменных типа указатель */ int ct = 0; /* счетчик вводимых строк */ int k; /* счетчик выводимых строк */ printf(" Введите до %d строк и я их отсортирую.\n", LIM); printf(" Для прекращения ввода нажмите клавишу [ввод] в начале строки\n"); while((gets(input[ct])!= NULL) && strcmp(input[ct], HALT)!= 0&& ct++ < LIM) ptstr[ct - 1] = input [ct - 1]; /указывает на еще не отсортированный ввод */ stsrt(ptstr, ct); /* сортировка строк */ puts(* \nВот отсортированный список строк:\n"); for (k = 0; k < ct; k++) puts(ptstr[k]); /* указатели на отсортированные строки */ }
/* функция сортировки строк с использованием указателей */ stsrt(strings, num) char *strings[]; int num; { char *temp; int top, seek; for (top = 0; top < num - 1; top++) for (seek = top + 1; seek < num; seek++) if (strcmp(strings[top], strings[seek]) > 0) { temp = strings [top]; strings [top] = strings [seek]; strings [seek] = temp; } }
Исходный текст программы будет иметь вид: /* считывает строки и сортирует их */ #include <stdio.h> #include <string.h> #define SIZE 81 /* предельная длина строки, включая\0 */ #define LIM 20 /* максимальное количество считываемых строк */ #define HALT " " /* нулевая строка для прекращения ввода */ void stsrt(char *strings[], int num) { char *temp; int top, seek; for (top = 0; top < num - 1; top++) for (seek = top + 1; seek < num; seek++) if (strcmp(strings[top], strings[seek]) > 0) { temp = strings [top]; strings [top] = strings [seek]; strings [seek] = temp; } }
void main () { static char input [LIM] [SIZE]; /* массив для запоминания вводимых строк */ char *ptstr[LIM]; /* массив переменных типа указатель */ int ct = 0; /* счетчик вводимых строк */ int k; /* счетчик выводимых строк */ printf(" Введите до %d строк и я их отсортирую.\n", LIM); printf(" Для прекращения ввода нажмите клавишу [ввод] в начале строки\n"); while((gets(input[ct])!= NULL) && strcmp(input[ct], HALT)!= 0&& ct++ < LIM) ptstr[ct - 1] = input [ct - 1]; /*указывает на еще не отсортированный ввод */ stsrt(ptstr, ct); /* сортировка строк */ puts(" \nВот отсортированный список строк:\n"); for (k = 0; k < ct; k++) puts(ptstr[k]); /* указатели на отсортированные строки */ } Вывод строк на печать не составляет проблемы, а для сортировки можно взять тот же алгоритм, который использовался раньше для чисел. Сейчас мы применим один хитрый трюк: посмотрим, сможете ли вы его заметить. Для проверки возьмем детский стишок.
Введите до %d строк и я их отсортирую. Для прекращения ввода нажмите клавишу [ввод] в начале строки Жил на свете человек Скрюченные ножки И гулял он целый век По скрюченной дорожке
Вот отсортированный список строк Жил на свете человек И гулял он целый век По скрюченной дорожке Скрюченные ножки
Детские стишки не кажутся слишком искаженными после сортировки их по алфавиту. Трюк состоит в том что вместо перегруппировки самих строк мы перегруппировали их указатели. Разберемся в этом. В начале ptrst[0] ссылается на input[0] и т. д. Каждый input[] является массивом из 81 элемента, а каждый элемент ptrst[] является отдельной переменной. Процедура сортировки перегруппировывает ptrst, не трогая input. Если, например, input[1] стоит перед input[0] по алфавиту, то программа переключает указатели ptrst, в результате чего ptrst[0] ссылается на input[1], a ptrst[1] на input[0]. Это гораздо легче, чем, используя strcpy (), менять местами две введенные строки. Просмотрите еще раз этот процесс на рисунке. И наконец, давайте попытаемся заполнить пробелы, оставшиеся в нашем описании, а именно «пустоту» между скобками в функции main().
7.27. Создание собственных функций ввода/вывода. Не ограничивайте себя при вводе и выводе только этими библиотечными функциями. Если у вас нет нужной функции, или она вам не нравится, можно создавать свои собственные версии, используя для этого getchar() и putchar(). Предположим, у вас нет функции puts (). Вот один из путей ее создания:
/* put1---- печатает строку */ put1 (string); char *string; { while(*string!= ' \0') putchar(*string++); putchar(' \n'); }
Символьный указатель string вначале ссылается на первый элемент вызванного аргумента. После печати его содержимого указатель увеличивается и ссылается уже на следующий элемент. Это продолжается до тех пор, пока указатель не дойдет до элемента, содержащего нуль-символ. Затем в конце строки будет поставлен символ новой строки. Предположим, у вас есть puts (), но вам нужна функция, которая, кроме того, сообщает, сколько напечатано символов. Эту возможность легко добавить:
/* put2-----печатает строку и считывает символы */ put2 (string); char *string; { int count = 0; while (*string!= ' \0') { putchar(*string ++); count ++; } putchar(' \n'); return(count); }
Вызов
put2("пицца");
печатает строку пицца, в то время как оператор
num = puts("пицца");
передаст, кроме того, количество символов в num; в данном случае это число 5. Вот несколько более сложный вариант, показывающий вложенные функции:
/* вложенные функции */ #include <stdio.h> main () { put1 ("Если бы я имел столько денег, сколько могу потратить,"); рrintf("Я считаю %d символа\n", put2 ("Я никогда бы не жаловался, что приходится чинить старые стулья.")); }
Исходный текст программы будет иметь следующий вид:
/* вложенные функции */ #include <stdio.h> /* put1---- печатает строку */ void put1 (char *string) { while(*string!= '\0') putchar(*string++); putchar(' \n'); } /* put2-----печатает строку и считывает символы */ int put2 (char *string) { int count = 0; while (*string!= '\0') { putchar(*string ++); count ++; } putchar(' \n'); return(count); }
void main () { put1 ("If I had so much money, how much I could spent,"); printf("I count %d simbols\n", put2 ("I would never complain about needs to repair old chairs.")); } (Мы включили в программу при помощи директивы #include файл Stdio.h, потому что в нашей системе в нем определена функция putchar (), а она используется в нашей новой функции.) Да-а, мы используем функцию printf () для печати значения put2 (), но в процессе нахождения значения put2 () компьютер должен сначала заставить ее поработать — напечатать строку. Вот что получается при этом:
Если бы я имел столько денег, сколько могу потратить, Я никогда бы не жаловался, что приходится чинить старые стулья.
Дата добавления: 2015-01-03; Просмотров: 510; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |