Перейти к содержимому

Как ввести аргументы функций

  • автор:

main аргументы функции и командной строки

Все программы C++ должны иметь функцию main . При попытке скомпилировать программу C++ без main функции компилятор вызывает ошибку. (Библиотеки и static библиотеки динамической main компоновки не имеют функции.) Функция main заключается в том, где начинается выполнение исходного кода, но перед вводом main программы в функцию все static члены класса без явных инициализаторов заданы равным нулю. В Microsoft C++глобальные static объекты также инициализированы перед записью main . Несколько ограничений main применяются к функции, которая не применяется к другим функциям C++. Функция main :

  • Невозможно перегружать (см . перегрузку функции).
  • Невозможно объявить как inline .
  • Невозможно объявить как static .
  • Не удается принять свой адрес.
  • Не удается вызвать из программы.

Сигнатура main функции

Функция main не имеет объявления, так как она встроена в язык. Если это сделать, синтаксис main объявления будет выглядеть следующим образом:

int main(); int main(int argc, char *argv[]); 

Если возвращаемое значение не указано, main компилятор предоставляет возвращаемое значение нуля.

Стандартные аргументы командной строки

Аргументы для main обеспечения удобного анализа аргументов командной строки. Типы для параметров argc и argv определяются языком. Имена argc и argv являются традиционными, но вы можете назвать их независимо от того, что вы хотите.

Используются следующие определения аргументов.

argc
Целое число, содержащее количество аргументов, которые следуют в argv. Параметр argc всегда больше или равен 1.

argv
Массив завершающихся null строк, представляющих введенные пользователем программы аргументы командной строки. По соглашению — это команда, argv[0] с помощью которой вызывается программа. argv[1] — первый аргумент командной строки. Последний аргумент из командной строки имеет argv[argc — 1] argv[argc] значение NULL.

Сведения о том, как отключить обработку командной строки, см. в разделе «Настройка обработки командной строки C++».

По соглашению argv[0] — это имя файла программы. Однако в Windows можно создать процесс с помощью CreateProcess . Если вы используете оба первых и второго аргумента ( lpApplicationName и lpCommandLine ), argv[0] может не быть исполняемым именем. Можно использовать GetModuleFileName для получения исполняемого имени и полного пути.

Расширения, относящиеся к Корпорации Майкрософт

В следующих разделах описано поведение майкрософт.

wmain Функция и _tmain макрос

Если вы разрабатываете исходный код для использования широких charактеров Юникода, вы можете использовать точку входа, определенную wmain корпорацией Майкрософт, которая является широкойchar версией main . Ниже приведен синтаксис эффективного объявления для wmain :

int wmain(); int wmain(int argc, wchar_t *argv[]); 

Вы также можете использовать определенный корпорацией Майкрософт _tmain макрос препроцессора, определенный в tchar.h . _tmain разрешает, main если _UNICODE не определено. В противном случае функция _tmain разрешается в функцию wmain . Макросы _tmain и другие макросы, которые начинаются с _t , полезны для кода, который должен создавать отдельные версии для узких и широких charнаборов актировщиков. Дополнительные сведения см. в разделе «Использование универсальных текстовых сопоставлений».

void Возврат изmain

Как расширение Майкрософт, main wmain функции могут быть объявлены как возвращаемые void (без возвращаемого значения). Это расширение также доступно в некоторых других компиляторах, но его использование не рекомендуется. Он доступен для симметрии, если main не возвращает значение.

При объявлении main или возврате void кода в родительский процесс или wmain операционную систему невозможно вернуть exit с помощью инструкции return . Чтобы вернуть exit код, когда main или wmain как void объявлен, необходимо использовать функцию exit .

envp Аргумент командной строки

wmain Сигнатуры main позволяют дополнительному расширению майкрософт для доступа к переменным среды. Это расширение также распространено в других компиляторах для систем Windows и UNIX. Имя envp традиционное, но можно присвоить параметру среды любое имя. Ниже приведены действующие объявления для списков аргументов, включающих параметр среды:

int main(int argc, char* argv[], char* envp[]); int wmain(int argc, wchar_t* argv[], wchar_t* envp[]); 

envp
Необязательный envp параметр — это массив строк, представляющий переменные, заданные в среде пользователя. Этот массив завершается записью NULL. Его можно объявить как массив указателей char на ( char *envp[] ) или как указатель на указатели char на ( char **envp ). Если программа использует wmain вместо main этого, используйте wchar_t вместо него тип char данных.

Блок среды, переданный main и wmain является «замороженным» копией текущей среды. Если вы позже измените среду, выполнив вызов putenv или _wputenv , текущая среда (возвращаемая getenv или _environ _wgetenv _wenviron переменная) изменится, но блок, на который указывает, envp не изменится. Дополнительные сведения о подавлении обработки среды см. в разделе «Настройка обработки командной строки C++». Аргумент envp совместим со стандартом C89, но не со стандартами C++.

Примеры аргументов main

В следующем примере показано, как использовать argc аргументы и envp argv аргументы main для:

// argument_definitions.cpp // compile with: /EHsc #include #include using namespace std; int main( int argc, char *argv[], char *envp[] ) < bool numberLines = false; // Default is no line numbers. // If /n is passed to the .exe, display numbered listing // of environment variables. if ( (argc == 2) && _stricmp( argv[1], "/n" ) == 0 ) numberLines = true; // Walk through list of strings until a NULL is encountered. for ( int i = 0; envp[i] != NULL; ++i ) < if ( numberLines ) cout > 

Анализ аргументов командной строки C++

Правила синтаксического анализа командной строки, используемые кодом Microsoft C/C++, относятся к корпорации Майкрософт. Код запуска среды выполнения использует эти правила при интерпретации аргументов, заданных в командной строке операционной системы:

  • Аргументы разделяются пробелами (пробел или табуляция).
  • Первый аргумент ( argv[0] ) обрабатывается особым образом. Он представляет имя программы. Это должен быть допустимый путь, поэтому разрешены части, заключенные в двойные кавычки ( « ). Эти знаки двойных кавычек не включаются в выходные данные argv[0] . Части, окруженные двойными кавычками, предотвращают интерпретацию пробела или действия табуляции charв качестве конца аргумента. Последующие правила в этом списке не применяются.
  • Строка, окруженная двойными кавычками, интерпретируется как один аргумент, который может содержать актировщики пробелов char. Строку в кавычках можно встроить в аргумент. Курсор ( ^ ) не распознается как экранный charактер или разделитель. Внутри заключенной в кавычки строки пара двойных кавычек интерпретируется как одна экранированная двойная кавычка. Если командная строка заканчивается до того, как будет найдена закрывающая двойная кавычка, все charвыступающие, прочитанные до сих пор, будут выводиться в качестве последнего аргумента.
  • Символ двойной кавычки после обратной косой черты ( \» ) интерпретируется как литеральный символ двойной кавычки ( « ).
  • Символы обратной косой черты считаются литералами, если сразу за ними не стоит двойная кавычка.
  • Если двойная кавычка стоит после четного числа символов обратной косой черты, в массив argv помещается по одному символу обратной косой черты ( \ ) для каждой пары символов обратной косой черты ( \\ ), а сама двойная кавычка ( « ) интерпретируется как разделитель строк.
  • Если двойная кавычка стоит после нечетного числа символов обратной косой черты, в массив argv помещается по одному символу обратной косой черты ( \ ) для каждой пары символов обратной косой черты ( \\ ). Двойная кавычка интерпретируется как escape-последовательность повторнойmainобратной косой черты, что приводит к тому, что двойный кавычки литералов ( « ) помещается в argv .

Пример синтаксического анализа аргументов командной строки

В следующем примере программы показана передача аргументов командной строки:

// command_line_arguments.cpp // compile with: /EHsc #include using namespace std; int main( int argc, // Number of strings in array argv char *argv[], // Array of command-line argument strings char *envp[] ) // Array of environment variable strings < int count; // Display each command-line argument. cout

Результаты анализа командных строк

В следующей таблице показаны примеры входных данных и ожидаемые выходные данные, иллюстрирующие применение правил из приведенного выше списка.

Входные данные в командной строке argv[1] argv[2] argv[3]
"abc" d e abc d e
a\\b d"e f"g h a\\b de fg h
a\\\"b c d a\"b c d
a\\\\"b c" d e a\\b c d e
a"b"" c d ab" c d

Развертывание подстановочных знаков

Компилятор Майкрософт при необходимости позволяет использовать дикие картаchar актеры, вопросительный знак () и звездочку ( ? * ), чтобы указать имя файла и аргументы пути в командной строке.

Аргументы командной строки обрабатываются внутренней подпрограммой в коде запуска среды выполнения, который по умолчанию не расширяет дикие элементы карта в отдельные строки в массиве argv строк. Вы можете включить расширение wild карта, включив setargv.obj файл ( wsetargv.obj файл для wmain ) в /link параметры компилятора или LINK в командной строке.

Дополнительные сведения о параметрах компоновщика для запуска среды выполнения см. в статье Параметры ссылок.

Настройка обработки командной строки C++

Если программа не принимает аргументы командной строки, можно сохранить небольшой объем пространства, подавив подпрограмму обработки командной строки. Для этого включите файл noarg.obj (для main и wmain ) в параметры компилятора /link или командную строку LINK .

Аналогичным образом, если вы никогда не использовали таблицу среды для доступа к аргументу envp , можно подавить внутреннюю подпрограмму обработки среды. Для этого включите файл noenv.obj (для main и wmain ) в параметры компилятора /link или командную строку LINK .

Программа может вызывать семейство подпрограмм spawn или exec в библиотеке среды выполнения C. В этом случае не следует подавлять подпрограмму обработки среды, так как она используется для передачи данных о среде из родительского процесса в дочерний.

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

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

Параметры представляют собой локальные переменные, которым присваиваются значения в момент вызова функции. Конкретные значения, которые передаются в функцию при ее вызове, будем называть аргументами. Следует иметь в виду, что встречается иная терминология. Например, формальные параметры и фактические параметры. В Python же обычно все называют аргументами.

Рассмотрим схему и поясняющий ее пример:

Когда функция вызывается, то ей передаются аргументы. В примере указаны глобальные переменные num1 и num2 . Однако на самом деле передаются не эти переменные, а их значения. В данном случае числа 100 и 12. Другими словами, мы могли бы писать mathem(100, 12) . Разницы не было бы.

Когда интерпретатор переходит к функции, чтобы начать ее исполнение, он присваивает переменным-параметрам переданные в функцию значения-аргументы. В примере переменной a будет присвоено 100, b будет присвоено 12.

Изменение значений a и b в теле функции никак не скажется на значениях переменных num1 и num2 . Они останутся прежними. В Python такое поведение характерно для неизменяемых типов данных, к которым относятся, например, числа и строки. Говорят, что в функцию данные передаются по значению. Можно сказать, когда a присваивалось число 100, то это было уже другое число, не то, на которое ссылается переменная num1 . Число 100 было скопировано и помещено в отдельную ячейку памяти для переменной a .

На самом деле переменная a в момент присваивания значения может указывать на то же число 100, что и переменная num1 . Однако, когда a в результате вычислений в теле функции получает новое значение, то связывается с другой ячейкой памяти, потому что числа относятся к неизменяемым типам данных, то есть нельзя переписать значение содержащей их ячейки. При этом переменная num1 остается связанной со старым значением.

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

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

Произвольное количество аргументов

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

Однако в Python у функций бывают параметры, которым уже присвоено значение по-умолчанию. В таком случае, при вызове можно не передавать соответствующие этим параметрам аргументы. Хотя можно и передать. Тогда значение по умолчанию заменится на переданное.

def cylinder(h, r = 1): side = 2 * 3.14 * r * h circle = 3.14 * r**2 full = side + 2 * circle return full figure1 = cylinder(4, 3) figure2 = cylinder(5) print(figure1) print(figure2)
131.88 37.68

При втором вызове cylinder() мы указываем только один аргумент. Он будет присвоен переменной-параметру h . Переменная r будет равна 1.

Согласно правилам синтаксиса Python при определении функции параметры, которым присваивается значение по-умолчанию должны следовать (находиться сзади) за параметрами, не имеющими значений по умолчанию.

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

… figure3 = cylinder(10, 2) figure4 = cylinder(r=2, h=10) print(figure3) print(figure4)

В данном случае оба вызова – это вызовы с одними и теми же аргументами-значениями. Просто в первом случае сопоставление параметрам-переменным идет в порядке следования. Во-втором случае – по ключам, которыми выступают имена параметров.

В Python определения и вызовы функций имеют и другие нюансы, рассмотрение которых мы пока опустим, так как они требуют более глубоких знаний, чем у нас есть на данный момент. Скажем лишь, что функции может быть определена так, что в нее можно передать хоть ни одного аргумента, хоть множество:

def few_or_many(*a): print(a) few_or_many(1) few_or_many('1', 1, 2, 'abc') few_or_many()
(1,) ('1', 1, 2, 'abc') ()

Опять же, судя по скобкам, здесь возникает упомянутый в прошлом уроке кортеж.

Практическая работа

Напишите программу, в которой определена функция int_test , имеющая один параметр. Функция проверяет, можно ли переданное ей значение преобразовать к целому числу. Если можно, возвращает логическое True . Если нельзя – False .

В основной ветке программы присвойте переменной s то, что пользователь вводит с клавиатуры. Вызовите функцию int_test() , передав ей значение s . Если функция возвращает истину, преобразуйте строку s в число n и выведите на экран значение n + 10 .

Примеры решения и дополнительные уроки в pdf-версии курса

X Скрыть Наверх

Python. Введение в программирование

Объект arguments

Объект arguments — это подобный массиву объект, который содержит аргументы, переданные в функцию.

Примечание: Если вы пишете ES6-совместимый код, то лучше использовать остаточные параметры.

Примечание: "Подобный массиву" означает, что arguments имеет свойство length , а элементы индексируются начиная с нуля. Но при этом он не может обращаться к встроенным методам Array , таким как forEach() или map() . Подробнее об этом в §Описании.

Интерактивный пример

Синтаксис

arguments

Описание

Объект arguments - это локальная переменная, доступная внутри любой (нестрелочной) функции. Объект arguments позволяет ссылаться на аргументы функции внутри неё. Он состоит из переданных в функцию аргументов, индексация начинается с 0. Например, если в функцию было передано 3 аргумента, обратиться к ним можно следующим образом:

[0]; arguments[1]; arguments[2]; 

Аргументам может быть присвоено значение:

[1] = "new value"; 

Объект arguments не является Array . Он похож на массив, но не обладает ни одним из его свойств, кроме length . Например, у него нет метода pop . Однако, он может быть преобразован в обычный массив:

var args = Array.prototype.slice.call(arguments); var args = [].slice.call(arguments); // ES2015 const args = Array.from(arguments); const args = [. arguments]; 

Предупреждение: Использование slice на объекте arguments не позволяет сделать оптимизации в некоторых JavaScript движках (например, V8 — подробнее). Если они важны, можно попробовать вместо этого создать новый массив с аналогичной длиной и заполнить его элементами объекта arguments. Альтернативный вариант — использовать конструктор Array как функцию:

var args = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments); 

Объект arguments можно использовать при вызове функции с бо́льшим количеством аргументов, чем было предусмотрено в её объявлении. Такой способ удобен для функций, в которые допустимо передавать переменное количество аргументов. Можно воспользоваться arguments.length , чтобы определить количество переданных в функцию аргументов, а затем обработать каждый из них с помощью объекта arguments . Чтобы определить количество параметров функции, описанных в её сигнатуре (en-US) , можно использовать свойство Function.length .

Использование typeof с объектом arguments

Применение оператора typeof к arguments вернёт 'object'.

console.log(typeof arguments); // 'object'

Определение типов аргументов может быть выполнено применением оператора typeof и индексацией.

// выведет тип первого аргумента console.log(typeof arguments[0]);

Использование оператора расширения для объекта arguments

Как и с обычными массива-подобными объектами, для преобразования объекта arguments в обычный массив можно использовать метод Array.from() или оператор расширения:

var args = Array.from(arguments); var args = [. arguments]; 

Свойства

Ссылка на функцию, которая выполняется в текущий момент.

Ссылка на функцию, которая вызвала функцию, выполняющуюся в текущий момент.

Количество переданных в функцию аргументов.

Возвращает новый объект Array Iterator , содержащий значения для каждого индекса в массиве.

Примеры

Создание функции, соединяющей несколько строк

Данный пример описывает функцию, которая соединяет несколько строк. Для этой функции объявлен только один аргумент, определяющий символ-разделитель соединяемых элементов. Функция определена следующим образом:

function myConcat(separator)  var args = Array.prototype.slice.call(arguments, 1); return args.join(separator); > 

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

// возвращает "red, orange, blue" myConcat(", ", "red", "orange", "blue"); // получает "elephant; giraffe; lion; cheetah" myConcat("; ", "elephant", "giraffe", "lion", "cheetah"); // выводит "sage. basil. oregano. pepper. parsley" myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley"); 

Функция, создающая HTML списки

В данном примере приведена функция, которая создаёт строку с HTML-разметкой для списка. Единственный её аргумент — строка, определяющая вид списка: если его значение равно "u", формируется неупорядоченный (маркированный) список, а если "o" — то упорядоченный (нумерованный):

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

Остаточные, деструктурированные и параметры по умолчанию

function foo(. args)  return arguments; > foo(1, 2, 3); // 

Тем не менее, в нестрогих функциях соответствие между их аргументами и объектом arguments существует только в том случае, если функция не содержит никаких остаточных параметров, параметров по умолчанию или деструктурированных параметров. Например, в функции, приведённой ниже, используется параметр по умолчанию, и в данном случае возвращаемый результат будет равен 10, а не 100:

function bar(a = 1)  arguments[0] = 100; return a; > bar(10); // 10 
function zoo(a)  arguments[0] = 100; return a; > zoo(10); // 100 

На самом деле, если остаточные параметры, параметры по умолчанию или деструктурированные параметры не используются, формальные аргументы будут ссылаться на последние значения объекта arguments , при считывании значений формальных аргументов будут считаны последние данные из arguments , а при изменении значений формальных аргументов будет обновлён и объект arguments . Пример приведён в коде ниже:

function func(a, b)  arguments[0] = 90; arguments[1] = 99; console.log(a + " " + b); > func(1, 2); //90, 99 
function func(a, b)  a = 9; b = 99; console.log(arguments[0] + " " + arguments[1]); > func(3, 4); //9, 99 

Но в случае, когда применяются остаточные параметры, параметры по умолчанию или деструктурированные параметры, будет обработано нормальное поведение, как в случае параметров по умолчанию:

function func(a, b, c = 9)  arguments[0] = 99; arguments[1] = 98; console.log(a + " " + b); > func(3, 4); //3, 4 

Спецификации

Specification
ECMAScript Language Specification
# sec-arguments-exotic-objects

Совместимость с браузерами

BCD tables only load in the browser

Смотрите также

Found a content problem with this page?

  • Edit the page on GitHub.
  • Report the content issue.
  • View the source on GitHub.

This page was last modified on 6 янв. 2024 г. by MDN contributors.

Your blueprint for a better internet.

MDN

Support

  • Product help
  • Report an issue

Our communities

Developers

  • Web Technologies
  • Learn Web Development
  • MDN Plus
  • Hacks Blog
  • Website Privacy Notice
  • Cookies
  • Legal
  • Community Participation Guidelines

Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2024 by individual mozilla.org contributors. Content available under a Creative Commons license.

C++: Параметры и аргументы функции

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

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

Что такое параметры и аргументы функции

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

Параметры функции определяются в объявлении функции — они указываются в скобках после имени функции. При этом несколько параметров разделяются запятыми.

Посмотрим на код:

#include #include std::string GetFullName(std::string first_name, std::string last_name) < return first_name + " " + last_name; >int main() < std::string result < GetFullName("John", "Wik") >; std::cout

Здесь у нас есть функция GetFullName , которая принимает две строки в качестве параметров. Эти параметры также называются формальными параметрами или аргументами функции. Так наша функция предоставляет интерфейс, ожидая две строки.

В функции main происходит вызов функции GetFullName с передачей фактических аргументов. Количество и типы передаваемых аргументов должны соответствовать интерфейсу функции, иначе программа не будет скомпилирована.

Когда функция вызывается, все параметры функции создаются как переменные. При этом значение каждого из аргументов копируется в соответствующий параметр. Этот процесс называется передачей по значению.

Что такое передача по значению

Рассмотрим следующий пример:

#include int AddOne(int num) < return num + 1; >int main() < int number = 1; std::cout ; std::cout

Эта программа создает следующий вывод:

number = 1 result = 2

Обратим внимание на следующее:

  • Мы передаем переменную в вызов функции AddOne . При компиляции программы вместо переменной подставляется ее значение
  • Значение переменной number не изменяется. Это происходит из-за того, что параметр передается по значению. Также при вызове функции внутри нее создается локальная переменная num , в которую копируется значение переменной number . Так функция работает с копией данных, а не с их оригиналом

Этот процесс и называется передачей параметров в функцию по значению.

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

Задание

Реализуйте функцию Remainder() , которая принимает число типа int и возвращает остаток от деления на два. Остаток от деления можно взять с помощью оператора % .

Упражнение не проходит проверку — что делать? ��

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

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

В моей среде код работает, а здесь нет ��

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя ��

Это нормально ��, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.

В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Прочитал урок — ничего не понятно ��

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.

Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *