Главная страница

Электронный конспект по программированию часть 1. Программа на языке Си, так же как и на большинстве современных языков программирования, создается в два этапа


Скачать 32.35 Mb.
НазваниеПрограмма на языке Си, так же как и на большинстве современных языков программирования, создается в два этапа
АнкорЭлектронный конспект по программированию часть 1.doc
Дата04.09.2018
Размер32.35 Mb.
Формат файлаdoc
Имя файлаЭлектронный конспект по программированию часть 1.doc
ТипПрограмма
#24059
страница8 из 12
1   ...   4   5   6   7   8   9   10   11   12

&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 ++ ) // цикл по столбцам строки

{
1   ...   4   5   6   7   8   9   10   11   12


написать администратору сайта