Электронный конспект по программированию часть 1. Программа на языке Си, так же как и на большинстве современных языков программирования, создается в два этапа
Скачать 32.35 Mb.
|
&s[i]) .Так как функциям strcpy и strcat надо передать адреса в памяти, куда и откуда переместить данные, мы можем использовать запись a+len вместо &a[len] и т.д. #include #include main() { char s[80], a[80] = "Привет, "; int n, len; printf("Введите фамилию и имя "); gets ( s ); n = 0; while ( (s[n] != ' ') // ищемпервыйпробел && (s[n] != '\0') ) // или конец строки n ++; if ( s[n] != ' ' ) { // если нет пробела, ... printf( "Неверная строка" ); return 1; // выход по ошибке, код ошибки 1 } strcat ( a, s + n + 1 ); // добавить имя strcat ( a, " "); // добавить пробел len = strlen ( a ); // найти длину строки strncpy ( a + len, s, n ); // добавитьфамилию strcpy ( a + len + n, "!" ); // добавить "!" puts ( a ); } Поиск в строках Когда говорят о поиске в строках, обычно рассматривают две задачи: найти первый заданный символ с начала (или с конца), или также найти заданную подстроку (если она есть). Первую задачу выполняют функции strchr (поиск с начала строки) и strrchr (поиск с кон- ца строки), а вторую – функция strstr. Все эти функции возвращают указатель на найденный символ (или на первый символ найденной подстроки). Это значит, что переменная, в которую записывается это значение, должна быть объявлена как указатель на символьную переменную. Мы уже встречались с ука- зателями, когда работали с файлами. Указатель – это ячейка в памяти, в которую можно запи- сывать адрес другой переменной. Структура вызова функций такая: на первом месте – где искать (строка), на втором – что искать (один символ для функций strchr и strrchr или строка для strstr). Чтобы полу- чить номер символа с начала строки, надо вычесть из полученного указателя адрес начала мас- сива. Если поиск завершился неудачно, функции возвращают NULL. #include #include main() { chars1[] = "Мамамылараму", s2[] = "Война и мир", *p; p = strchr(s1, 'а'); if ( p != NULL ) { printf("Первая буква а: номер %d", p - s1); p = strrchr(s1, 'а'); printf("\nПоследняя буква а: номер %d", p - s1); } p = strstr( s2, "мир"); if ( p != NULL ) printf("\nНашлимирв %s", s2); else printf("\nНет слова мир в %s", s2); } Вспомните, что при чтении строк из файла с помощью функции fgets на конце иногда остается символ перехода на новую строку '\n'. Чаще всего он совсем не нужен и надо его удалить– поставить на его месте нуль (признак конца строки). Делается это так: char s[80], *p; ... p = strrchr (s, '\n'); // ищемсимвол '\n' if ( p != NULL )// если нашли, ... *p = '\0'; // записываем в это место '\0' Рассмотрим теперь более сложную задачу. Пример. С клавиатуры вводится предложение и слово. Надо определить, сколько раз встречается это слово в предложении. Функция strstr может определить только первое вхождение слова в строку, поэтому в одну строчку эту задачу не решить.Попробуем использовать такую идею: если мы нашли адрес первого данного слова в строке и записали его в указатель p, то искать следующее слово нужно не сна чала строки, а с адреса p+длина_слова. Повторяем эту операцию в цикле, пока функция strstr может найти слово в оставшейся части строки. Поскольку начало области поиска постоянно смещается с #include #include main() { int len, count; char s[80], word[20], *p, // указатель на найденное слово *start; // указатель на начало зоны поиска puts ( "Введите предложение" ); gets ( s ); puts ( "Введите слово для поиска" ); gets ( word ); len = strlen ( word ); // находимдлинуслова count = 0; // счетчик найденных слов start = s; // в первый раз ищем с начала строки while ( 1 ) { p = strstr (start, word); // есть ли еще слова? if ( p == NULL ) break; // если нет, то выход count ++; // увеличить счетчик start = p + len; // сместили начало поиска } printf ( "В этом предложении %d слов %s", count, word ); } В конце работы цикла в переменной count, будет записано количество данных слов в предложении. Заметьте, что вместо переменной start можно везде использовать p, результат от этого не изменится.каждым новым найденным словом, адрес оставшейся части надо хранить в отдельной переменной типа «указатель на символ». Реализация может выглядеть так: #include #include main() { int len, count; char s[80], word[20], *p, // указатель на найденное слово *start; // указатель на начало зоны поиска puts ( "Введите предложение" ); gets ( s ); puts ( "Введите слово для поиска" ); gets ( word ); len = strlen ( word ); // находимдлинуслова count = 0; // счетчик найденных слов start = s; // в первый раз ищем с начала строки while ( 1 ) { p = strstr (start, word); // есть ли еще слова? if ( p == NULL ) break; // если нет, то выход count ++; // увеличить счетчик start = p + len; // сместили начало поиска } printf ( "В этом предложении %d слов %s", count, word ); } В конце работы цикла в переменной count, будет записано количество данных слов в предложении. Заметьте, что вместо переменной start можно везде использовать p, результат от этого не изменится. Форматирование строк В программах часто требуется перед выводом информации сформировать всю строку для вывода целиком, включив в нее все необходимые данные. Например, сообщение об ошибке выводится стандартной функцией, и в это сообщение надо включить числовые данные. Другой пример – вывод текста в графическом режиме, для которого нет аналога функции printf.В этих случаях необходимо использовать функцию sprintf, которая поддерживает те же форматы данных, что и printf, но записывает результат не на экран и не в файл, а в символьную строку (под нее надо заранее выделить память). Вот как выглядит вывод на экран значения переменных x и y в графическом режиме: #include #include #include main() { chars[80]; // вспомогательнаястрока intx, y; // здесь нужно открыть окно для графики x = 1; y = 5; sprintf (s, "X=%d, Y=%d", x, y); // выводвстроку s outtextxy ( 100, 100, s ); // выводстроки s наэкран getch(); closegraph(); } Не забудьте, что для использования функции outtextxy надо открыть окно для работы с графикой (с помощью функции initwindow). Чтение из строки Иногда, особенно при чтении данных из файлов, возникает обратная задача: есть сим- вольная строка, в которой записаны данные. Необходимо ввести их в соответствующие ячейки памяти.В этом случае используется функция sscanf, которая читает данные по указанному формату не с клавиатуры (как scanf) и не из файла (как fscanf), а из символьной строки. В приведенном ниже примере мы ищем в файле строчку, которая начинается с символа # и считываем из нее значения x и y.Сложность задачи заключается в том, что мы не знаем точно, какая по счету эта строчка в файле. Если не использовать функцию sscanf, то пришлось бы сначала найти номер нужной строки в файле, затем начать просмотр с начала, отсчитать нужное количество строк и использоватьfscanf. #include main() { char s[80]; // вспомогательнаястрока int x, y; FILE *fp; fp = fopen ( "input.dat", "r" ); while ( fgets ( s, 80, fp ) ) if ( s[0] == '#' ) { // если строка начинается с #, ... sscanf ( s+1, "%d%d", &x, &y); // читаем данные break; // выход из цикла } fclose ( fp ); printf ( "x = %d, y = %d", x, y ); } Строки в функциях и процедурах Стандартные и ваши собственные функции могут принимать строки в качестве парамет- ров. Здесь, однако, есть некоторые тонкости. Вы знаете, что если параметр функции объявлен как int a, то изменение переменной a в функции никак не влияет на ее значение в вызывающей программе – функция создает копию переменной и работает с ней (для того, чтобы функция могла это сделать, надо объявить параметр как int &a). Это относится ко всем простым типам.Когда вы передаете в функция или процедуру строку, вы в самом деле передаете адресначала строки, никаких копий строки не создается. Поэтому всегда надо помнить, что При изменении строки-параметра в функции или процедуре меняется и соответствующая строка в основной программе.Как же объявить строку-параметр в своей функции? В простейшем варианте – так же, как и массив символов: char s[]. А можно также и как указатель (все равно в функцию передается адрес строки char *s). Между этими двумя способами есть некоторая разница. В первом случае s – это имя массива символов, поэтому нельзя его изменять, а во втором случае – указатель на символ, который можно сдвигать, как хочется.__Все стандартные функции языка Си объявляют символьные строки как указатели – это дает большую свободу действий. Сравните, например, две реализации функции, которая копирует строки (аналогично strcpy). Первая выполнена с помощью параметра-массива, а вторая – с помощью указателя. Как видите, вторая функция получилась более компактной. Применение указателей позволило не вводить дополнительную переменную, хотя и сделала программу менее ясной. Итак, в условии цикла while стоит оператор присваивания. Если не обращать внимания на плюсы, он означает «взять символ по адресу s2 и записать его по адресу s1». Двойные плюсы ПОСЛЕ s1 и s2 означают, что ПОСЛЕ выполнения присваивания оба указателя надо увеличить на единицу, то есть перейти к следующему символу. Что же является условием цикла? Оказывается условие – это величина *s1, то есть код символа по адресу s1. Когда же происходит проверка? Это зависит от расположения знаков++.В данном случае они стоят ПОСЛЕ имен переменных, поэтому операция инкремента выполняется ПОСЛЕ проверки условия. Проверка выполняется так: скопировали очередной символ,посмотрели на него, и если он – ноль (признак конца строки), то вышли из цикла. После этого увеличили указатели s1 и s2. Обратите внимание, что после выхода из цикла увеличение указателей также происходит, и они будут указывать не на нули, завершающие строки, а на следующие байты в памяти. 3. Матрицы (двухмерные массивы) Что такое матрица? Вспомните, что из себя представляет адрес любого человека (или фирмы). Вы можете ска- зать: "Я живу на Невском проспекте в доме 20 в квартире 45", но никому в голову не придет сказать: "Я живу в 13678 квартире от начала Невского проспекта". Почему? Потому что неудобно. Первая часть адреса – улица, вторая – дом, третья – квартира. Часто обычный массив неудобен для хранения данных из-за своей линейной структуры. Например, пусть нам надо обрабатывать информацию о количестве выпавших осадков за несколько лет, причем известны осадки по месяцам. Если обрабатывать данные вручную, то удобнее всего использовать таблицу - по горизонтали откладывать года, а по вертикали - месяцы (или наоборот). В принципе в программе можно использовать и одномерный массив, но тогда требуется пересчитывать индексы – по известному году и месяцу получать смещение ячейки от начала линейного массива: Для такого пересчета можно было бы использовать формулу i = (год - 2008)*12 + месяц - 1;где i – индекс нужного элемента массива. Тем не менее, так не делают (почти никогда), потомучто для работы с табличными данными во всех современных языках программирования существуют двухмерные массивы или матрицы. Матрица – это прямоугольная таблица элементов (например, чисел или символов). В ин- форматике матрица представляется в виде двухмерного массива, то есть массива, все эле- менты которого имеют два индекса. Матрица, как и таблица, состоит из строк и столбцов. Два индекса элемента - это и есть номера строки и столбца, на пересечении которых этот элемент находится. В языке Си каждый индекс записывается отдельно в квадратных скобках. Каждую строку и каждый столбец матрицы можно рассматривать как обычный одномерный массив. Поэтому можно сказать, что матрица – это массив из массивов. Существует только два ограничения: 1) все элементы матрицы должны быть одинакового типа; 2) все строки матрицы должны быть одинаковой длины. Первый индекс элемента матрицы – это строка, второй – столбец. Поэтому когда говорят о «матрице 4 на 5», это означает, что матрица имеет 4 строки и 5 столбца. Матрицы, у которых число строк равно числу столбцов, называют квадратными. В квад- ратных матрицах можно выделить главную диагональ – это все элементы, у которых номер строки равен номеру столбца, то есть A[0][0], A[1][1], ..., A[N-1][N-1] для матрицы размером N на N. Объявление матриц Матрицы объявляются также, как и простые массивы, но у них не один индекс, а два. При объявлении в отдельных квадратных скобках указывается количество строк и количество столбцов. Например, оператор int Bmv[20][10]; выделит место в памяти под матрицу целых чисел, имеющую 20 строк и 10 столбцов (всего 200 элементов). Если матрица глобальная (объявляется выше всех процедур и функций), то она в самом начале заполняется нулями. Локальные матрицы (объявленные внутри процедуры или функции) содержат «мусор» – неизвестные значения. Начальные значения элементов При объявлении можно сразу задать все или часть ее элементов, например так float X[2][3] = {{1., 2., 3.},{4., 5., 6.}}; Как видно из примера, элементы каждой строки заключаются в отдельные фигурные скобки.Если задать не все элементы, то остальные заполнятся нулями: float X[2][3] = {{1., 3.},{6.}}; Здесь элементы X[1][2], X[2][1] и X[2][2] будут нулевыми. Расположение матриц в памяти Иногда бывает полезно знать, как матрицы располагаются в памяти ЭВМ. Оказывается во всех современных языках программирования (кроме Фортрана) элементы матрицы располагаются по строкам, то есть сначала изменяется последний индекс. Объявленная выше матрица X расположена так: X[0][0] X[0][1] X[0][2] X[1][0] X[1][1] X[1][2] Стандартный ввод и вывод Как и для одномерных массивов, матрицы могут быть введены с клавиатуры, из файла, и заполнены с помощью случайных чисел. Общий принцип – для каждого элемента функция чтения вызывается отдельно. Представим себе матрицу как массив строк равной длины. Для ввода одной строки требуется цикл, и таких строк несколько. Поэтому для работы с матрицами требуется двойной или вложенный цикл, то есть цикл в цикле. Ввод с клавиатуры Единственная проблема состоит в том, чтобы не перепутать переменные в двух циклах и пределы их изменения. #include const int M = 5; // числострок const int N = 4; // числостолбцов main() { int i, j, A[M][N]; for ( i = 0; i < M; i ++ ) // циклпострокам for ( j = 0; j < N; j ++ ) // цикл по столбцам строки { |