Примеры решения задач с использованием строк



 

Задача 1. Дана строка символов, состоящая из слов, слова разделены между собой пробелами. Удалить из строки все слова, начинающиеся с цифры

Для решения этой задачи сформируем массив слов исходной строки, а затем перенесём в строку из этого массива только слова, удовлетворяющие поставленному условию. Этот прием можно использовать для решения большинства задач, в которых нужно удалять или добавлять слова в исходную строку.

 

#include <stdio.h>

#include <string.h>

void main()

{

char s[250], //исходная строка

w[25],   //слово

mas[10][25]; //массив слов

puts(”\nвведите строку”);

gets(s);

int k=0,t=0,i,len,j;

len=strlen(s);

while(t<len)

{

for(j=0,i=t;s[i]!= ’ ’;i++,j++)

w[j]=s[i]; //формируем слово до пробела

w[j]=’/0’; //формируем конец строки

strcpy(mas[k],w); //копируем слово в массив

k++; //увеличиваем счетчик слов

//переходим к следующему слову в исходной строке

t=i+1;  

}

strcpy(s,””);//очищаем исходную строку

for(t=0;t<k;t++)

if(mas[t][0]<’0’ && mas[t][0]>’9’)

//если первый символ не цифра

{

strcat(s,mas[t]);//копируем в строку слово

strcat(s,” ”);//копируем в строку пробел

}

puts(s);//выводим результат

}

 

Задача 2. Сформировать динамический массив строк. Удалить из него строку с заданным номером.

В динамическом массиве строк каждая строка имеет свою длину, поэтому при формировании такого массива нужно выделять память под каждую строку отдельно.

 

#include <iostream.h>

#include <string.h>

void main()

{

int n;

cout<<”\nN=?”;

cin>>n;

 

//вспомогательная переменная для ввода строки

char s[100];

    

char** mas=new char*[n];

for(int i=0;i<n;i++)

{

    cout<<”\nS=?”;

    cin>>s;       //вводим строку

/*выделяем столько памяти, сколько символов в строке + один байт для ’\0’ */

mas[i]=new char[strlen(s)+1];

 

//копируем строку в массив

strcpy(mas[i],s);

}

 

for(i=0;i<n;i++)   //вывод массива

{

    cout<<mas[i];

    cout<<"\n";

}

 

int k;

cout<<"\nK=?";

cin>>k;  //ввести номер удаляемой строки

if(k>=n)

{

cout<<"There is not such string\n";

return;

}

 

/*выделяем память для размещения нового массива строк (без строки с номером k)*/

char** temp=new char*[n-1];

int j=0;

 

//переписываем в новый массив все строки, кроме k-ой

 

for(i=0;i<n;i++)

if(i!=k)

    {

//выделяем память под строку

temp[j]=new char[strlen(mas[i])];

 

//копируем строку

    strcpy(temp[j],mas[i]);

    j++;

    }

n--;//уменьшаем размер массива

 

for(i=0;i<n;i++)   //вывод нового массива

{

    cout<<temp[i];

    cout<<"\n";

}

}

Эту же задачу можно решить не выделяя память под элементы нового массива, а меняя указатели: temp[j]=mas[i];

 

/*выделяем память для размещения нового массива строк (без строки с номером k)*/

char** temp=new char*[n-1];

int j=0;

 

//переписываем в новый массив все строки, кроме k-ой

 

for(i=0;i<n;i++)

if(i!=k)

    {

temp[j]=mas[i];// меняем указатели

 

    j++;

    }

n--; //уменьшаем размер массива

 

delete []mas;//удаляем старый массив

mas=temp;//ставим указатель на измененный массив

for(i=0;i<n;i++)       

{

    cout<<mas[i]; //вывод массива

    cout<<"\n";

}

 

    

}

17. Функции в C++

С увеличением объема программы становится невозможно удерживать в памяти все детали. Чтобы уменьшить сложность программы, ее разбивают на части. В C++ задача может быть разделена на более простые подзадачи с помощью функций. Разделение задачи на функции также позволяет избежать избыточности кода, т. к. функцию записывают один раз, а вызывают многократно. Программу, которая содержит функции, легче отлаживать.

Часто используемые функции можно помещать в библиотеки. Таким образом, создаются более простые в отладке и сопровождении программы.

Объявление и определение функций

 

Функция – это именованная последовательность описаний и операторов, выполняющая законченное действие, например, формирование массива, печать массива и т. д.

Функция, во-первых, является одним из производных типов C++, а, во-вторых, минимальным исполняемым модулем программы.

Рис. 8. Функция как минимальный исполняемый модуль программы

 

Любая функция должна быть объявлена и определена.

Объявление функции (прототип, заголовок) задает имя функции, тип возвращаемого значения и список передаваемых параметров.

Определение функции содержит, кроме объявления, тело функции, которое представляет собой последовательность описаний и операторов.

 

тип имя_функции([список_формальных_параметров])

{

тело_функции

}

 

Тело_функции – это блок или составной оператор. Внутри функции нельзя определить другую функцию.

В теле функции должен быть оператор, который возвращает полученное значение функции в точку вызова. Он может иметь две формы:

1) return выражение;

2) return;

Первая форма используется для возврата результата, поэтому выражение должно иметь тот же тип, что и тип функции в определении. Вторая форма используется, если функция не возвращает значения, т. е. имеет тип void. Программист может не использовать этот оператор в теле функции явно, компилятор добавит его автоматически в конец функции перед }.

Тип возвращаемого значения может быть любым, кроме массива и функции, но может быть указателем на массив или функцию.

Список формальных параметров – это те величины, которые требуется передать в функцию. Элементы списка разделяются запятыми. Для каждого параметра указывается тип и имя. В объявлении имена можно не указывать.

Для того, чтобы выполнялись операторы, записанные в теле функции, функцию необходимо вызвать. При вызове указываются: имя функции и фактические параметры. Фактические параметры заменяют формальные параметры при выполнении операторов тела функции. Фактические и формальные параметры должны совпадать по количеству и типу.

Объявление функции должно находиться в тексте раньше вызова функции, чтобы компилятор мог осуществить проверку правильности вызова. Если функция имеет тип не void, то ее вызов может быть операндом выражения.

 

/*Заданы координаты сторон треугольника, если такой треугольник существует, то найти его площадь. */

#include <iostream.h>

#include <math.h>

 

/*функция возвращает длину отрезка, заданного координатами x1,y1 и x2,y2*/

double line(double x1,double y1,double x2,double y2)

{

return sqrt(pow(x1-x2,2)+pow(y1-y2,2));

}

 

/*функция возвращает площадь треугольника, заданного длинами сторон а,b,c*/

double square(double a, double b, double c)

{

double s, p=(a+b+c)/2;

return s=sqrt(p*(p-a)*(p-b)*(p-c));//формула Герона

}

 

//возвращает true, если треугольник существует

bool triangle(double a, double b, double c)

{

if(a+b>c&&a+c>b&&c+b>a) return true;

else return false;

}

 

void main()

{

double x1=1,y1,x2,y2,x3,y3;

double point1_2,point1_3,point2_3;

do

{

cout<<"\nEnter koordinats of triangle:";

cin>>x1>>y1>>x2>>y2>>x3>>y3;

point1_2=line(x1,y1,x2,y2);

point1_3=line(x1,y1,x3,y3);

point2_3=line(x2,y2,x3,y3);

if(triangle(point1_2,point1_3,point2_3)==true)

 

cout<<"S="<<square(point1_2,point2_3,point1_3)<<"\n";

 

else cout<<"\nTriagle doesnt exist";

}

while(!(x1==0&&y1==0&&x2==0&&y2==0&&x3==0&&y3==0));

}

 

Прототип функции

 

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

 

double line(double x1,double y1,double x2,double y2);

double square(double a, double b, double c);

bool triangle(double a, double b, double c);

double line(double ,double ,double ,double);

double square(double , double , double );

bool triangle(double , double , double );

 

При наличии прототипов вызываемые функции не обязаны размещаться в одном файле с вызывающей функцией, а могут оформляться в виде отдельных модулей и храниться в откомпилированном виде в библиотеке объектных модулей. Это относится и к функциям из стандартных модулей. В этом случае определения библиотечных функций уже оттранслированные и оформленные в виде объектных модулей, находятся в библиотеке компилятора, а описания функций необходимо включать в программу дополнительно. Это делают с помощью препроцессорных команд include<имя_файла>.

Имя_файла – определяет заголовочный файл, содержащий прототипы группы стандартных для данного компилятора функций. Например, почти во всех программах мы использовали команду #include <iostream.h> для описания объектов потокового ввода-вывода и соответствующие им операции.

При разработке программ, которые состоят из большого количества функций, размещенных в разных модулях, прототипы функций и описания внешних объектов (констант, переменных, массивов) помещают в отдельный файл, который включают в начало каждого из модулей программы с помощью директивы include ”имя_файла”.

 

Параметры функции

 

Основным способом обмена информацией между вызываемой и вызывающей функциями является механизм параметров. Существует два способа передачи параметров в функцию: по адресу и по значению.

При передаче по значению выполняются следующие действия:

· вычисляются значения выражений, стоящие на месте фактических параметров;

· в стеке выделяется память под формальные параметры функции;

· каждому фактическому параметру присваивается значение формального параметра, при этом проверяются соответствия типов и при необходимости выполняются их преобразования.

 

/*функция возвращает площадь треугольника, заданного длинами сторон а,b,c */

double square(double a, double b, double c)

{

double s, p=(a+b+c)/2;

return s=sqrt(p*(p-a)*(p-b)*(p-c));//формула Герона

}

 

Вызовы этой функции могут быть следующими:

1) double s1=square(2.5,2,1);

 

 

Будет сформирован стек:

A 2.5
B 2
C 1
S  
P  

где P и S – локальные переменные.

 

 

2) double a=2.5,b=2,c=1;

double s2=square(a,b,c);

 

3) double x1=1,y1=1,x2=3,y2=2,x3=3,y3=1;

 

double s3=square(

 

//расстояние между 1 и 2:

sqrt(pow(x1-x2,2)+pow(y1-y2,2)),

 

//расстояние между 1 и 3:

sqrt(pow(x1-x3,2)+pow(y1-y3,2))

 

//расстояние между 2 и3:

sqrt(pow(x3-x2,2)+pow(y3-y2,2))

);

 

Для 2) и 3) будет сформирован такой же стек, как и для 1), т. к. выражения, стоящие на месте фактических параметров сначала вычисляются, а потом передаются в стек. Таким образом,  в стек заносятся копии фактических параметров, и операторы функции работают с этими копиями. Доступа к самим фактическим параметрам у функции нет, следовательно, нет возможности их изменить.

       При передаче по адресу в стек заносятся копии адресов параметров, следовательно, у функции появляется доступ к ячейке памяти, в которой находится фактический параметр и она может его изменить.

 

void Change(int a,int b) //передача по значению

{

int r=a;

a=b;

b=r;

}

 

Для вызова функции

int x=1,y=5;

Change(x,y);

будет сформирован стек:

 

A 1 5
B 5 1
r   1

и при выполнении оператора

cout<<”x=”<<x<<” y=”<<y;

 в функции main выведется:

x=1 y=5

Таким образом, эта функция не решает поставленную задачу.

 

void Change(int* a, int* b) //передача по адресу

{

int r=*a;

*a=*b;

*b=r;

}

 

Для вызова функции

int x=1,y=5;

Change(&x,&y);

будет сформирован стек:

A &x 5
B &y 1
r   1

и при выполнении оператора

cout<<”x=”<<x<<” y=”<<y;

 в функции main выведется:

x=5 y=1,

т. е. выполнится обмен переменных.

 

Для передачи  данных в функцию также могут использоваться ссылки. При передаче по ссылке в функцию передается адрес указанного при вызове параметра, а внутри функции все обращения к параметру неявно разыменовываются.

 

void Change(int& a,int& b)

{

int r=a;

a=b;

b=r;

}

 

Для вызова функции

int x=1,y=5;

Change(x,y);

будет сформирован стек:

A &x 5
B &y 1
r   1

и при выполнении оператора

cout<<”x=”<<x<<” y=”<<y;

 в функции main выведется:

x=5 y=1, т. е. выполнится обмен переменных.

 

Использование ссылок вместо указателей улучшает читаемость программы, т. к. не надо применять операцию разыменовывания. Использование ссылок вместо передачи по значению также более эффективно, т. к. не требует копирования параметров. Если требуется запретить изменение параметра внутри функции, используется модификатор const. Рекомендуется ставить const перед всеми параметрами, изменение которых в функции не предусмотрено (по заголовку будет понятно, какие параметры в ней будут изменяться, а какие нет).

 


Дата добавления: 2018-04-04; просмотров: 196;