Функции со списками аргументов переменных (C++)
Функции, в объявлениях которых в качестве последнего члена указано многоточие (. ), могут принимать переменное число аргументов. В таких случаях C++ обеспечивает проверку типа только для явно объявленных аргументов. Переменные списки аргументов можно использовать, если функция должна быть настолько универсальной, что могут изменяться даже количество и типы аргументов. Семейство функций — это пример функций, использующих списки аргументов переменной. printf argument-declaration-list
Функции с переменными аргументами
Чтобы получить доступ к аргументам после объявления, используйте макросы, содержащиеся в стандартном файле , как описано ниже.
Блок, относящийся только к системам Майкрософт
Microsoft C++ допускает указывать многоточие в качестве аргумента, если это последний аргумент и перед многоточием стоит запятая. Поэтому объявление int Func( int i, . ); допускается, а объявление int Func( int i . ); — нет.
Завершение блока, относящегося только к системам Майкрософт
В объявлении функции, которая принимает переменное число аргументов, требуется по крайней мере один аргумент-местозаполнитель, даже если он не используется. Если этот аргумент-местозаполнитель не указан, доступ к остальным аргументам невозможен.
Когда аргументы типа char передаются в качестве аргументов переменной, они преобразуются в тип int . Аналогичным образом, когда аргументы типа float передаются в качестве аргументов переменной, они преобразуются в тип double . Для аргументов остальных типов могут выполняться обычные восходящие приведения целочисленных типов и типов с плавающей запятой. Дополнительные сведения см. в разделе «Стандартные преобразования».
Функции, которым необходимы списки с переменным количеством аргументов, объявляются с многоточием (. ) в списках аргументов. Используйте типы и макросы, описанные в , чтобы получить доступ к аргументам, передаваемым списком переменных. Дополнительные сведения об этих макросах см. в va_arg , va_copy, va_end va_start. в документации по библиотеке времени выполнения C.
В следующем примере показано, как макросы работают вместе с типом (объявленным в ):
// variable_argument_lists.cpp #include #include // Declaration, but not definition, of ShowVar. void ShowVar( char *szTypes, . ); int main() < ShowVar( "fcsi", 32.4f, 'a', "Test string", 4 ); >// ShowVar takes a format string of the form // "ifcs", where each character specifies the // type of the argument in that position. // // i = int // f = float // c = char // s = string (char *) // // Following the format specification is a variable // list of arguments. Each argument corresponds to // a format character in the format string to which // the szTypes parameter points void ShowVar( char *szTypes, . ) < va_list vl; int i; // szTypes is the last argument specified; you must access // all others using the variable-argument macros. va_start( vl, szTypes ); // Step through the list. for( i = 0; szTypes[i] != '\0'; ++i ) < union Printable_t < int i; float f; char c; char *s; >Printable; switch( szTypes[i] ) < // Type to expect. case 'i': Printable.i = va_arg( vl, int ); printf_s( "%i\n", Printable.i ); break; case 'f': Printable.f = va_arg( vl, double ); printf_s( "%f\n", Printable.f ); break; case 'c': Printable.c = va_arg( vl, char ); printf_s( "%c\n", Printable.c ); break; case 's': Printable.s = va_arg( vl, char * ); printf_s( "%s\n", Printable.s ); break; default: break; >> va_end( vl ); > //Output: // 32.400002 // a // Test string
Приведенный выше пример иллюстрирует следующие важные правила:
- Перед тем как обращаться к аргументам из списка переменной длины, необходимо установить его маркер в качестве переменной типа va_list . В приведенном выше примере этот маркер имеет имя vl .
- К отдельным аргументам можно обращаться при помощи макроса va_arg . Макросу va_arg необходимо указать тип получаемых аргументов, чтобы он мог перенести из стека нужное количество байтов. Если вы ошибетесь и укажете другой тип, а его размер не будет совпадать с типом, который вызывающая программа передает макросу va_arg , это может привести к непредсказуемым результатам.
- Результат, полученный при помощи макроса va_arg , необходимо явным образом преобразовывать в нужный вам тип.
Для завершения обработки списка с переменным количеством аргументов необходимо вызвать макрос. va_end
Форматированный ввод данных в Си — функция scanf
В то время как функция printf() осуществляет форматированный вывод данных, функция scanf() осуществляет их форматированный ввод. Это значит, что поступающие на ввод данные преобразуются соответственно указанному формату(ам) и записываются по адресу(ам) указанной(ых) переменной(ых):
scanf(строка_формата, адреса_переменных);
Причина, по которой в scanf() передаются адреса, а не значения переменных, очевидна. Функция scanf() должна изменять значения переменных тех функций, из которых вызывается. Единственный способ — это получить адреса областей памяти.
Спецификации, допустимые в строке формата, для scanf() почти идентичны тем, что были описаны для функции printf() .
Ввод чисел, символов и строк
Пример ввода-вывода целого и вещественного чисел, символа и строки:
int a; float b; char ch, str[30]; scanf("%d%f%c%s", &a, &b, &ch, str); printf("%d %.3f %c %s\n", a, b, ch, str);
45 34.3456y hello 45 34.346 y hello
Здесь при выполнении программы все данные были введены в одну строку. Разделителем между числами и строками является пробел, а также любой другой символ пустого пространства (например, ‘\n’). Однако при считывании символа, пробел учитывается как символ; чтобы этого не произошло, в примере букву записали сразу после числа. Данные можно было бы ввести, разделяя их переходом на новую строку (опять же при этом надо иметь ввиду, как считывается символ).
В строке формата функции scanf() между спецификациями вполне допустимо поставить пробелы: %d %f %c %s . Они никакой роли не сыграют. Понятно, что данные можно было получить и так:
scanf("%d", &a); scanf("%f", &b); scanf("%c", &ch); scanf("%s", str);
Обратите внимание, перед переменной str отсутствует знак амперсанда. В последующих уроках вы узнаете, что имя массива уже само по себе является ссылкой на массив (другими словами, str содержит адрес начала массива).
В функции scanf() в спецификации формата вещественных чисел не указывается точность представления числа. Запись типа %.3f или %.10lf приведет к невозможности получить вещественное число. Чтобы получить число типа double используют формат %lf , для long double ‒ %Lf .
Для целых чисел: длинное целое ‒ %ld , короткое целое ‒ %hd . Существуют спецификации для ввода восьмеричных и шестнадцатеричных чисел.
Функция scanf() возвращает количество удачно считанных данных; т.е. значение, возвращаемое функцией, можно проанализировать и таким образом узнать, корректно ли были введены данные. Например:
int a; double b; char ch, str[30]; ch = scanf("%d %lf %s", &a, &b, str); if (ch == 3) printf("%d %.3lf %s\n", a, b, str); else printf("Error input\n");
Обычные символы в строке формата
В строке формата scanf() допустимо использование обычных символов. В этом случае при вводе данных также должны вводиться и эти символы:
int a, b, c; scanf("%d + %d = %d", &a, &b, &c); printf("Your answer is %d\n", c); printf("The correct is %d\n", a+b);
В данном случае, когда программа выполняется, ввод должен выглядеть примерно так: 342+1024 = 1366. Знаки «+» и » lang»>% , но перед буквой формата звездочку * . В таком случае данные считываются, но никакой переменной не присваиваются. Это можно использовать, например, когда нет определенной уверенности в том, что поступит на ввод, с одной стороны, и нужды сохранять эти данные, с другой:
float arr[3]; int i; for(i = 0; i 3; i++) scanf("%*s %f", &arr[i]); printf("Sum: %.2f\n", arr[0]+arr[1]+arr[2]);
Здесь предполагается, что перед каждым числом будет вводиться строка, которую следует проигнорировать, например:
First: 23.356 Second: 17.285 Third: 32.457 Sum: 73.098
Использование «шаблонов»
Для функции scanf() есть пара спецификаций формата, отдаленно напоминающих шаблоны командной оболочки и др. Формат […] позволяет получить строку, содержащую любые символы, указанные в квадратных скобках. Как только на ввод поступает символ, не входящий в указанный набор, считывание данных прекращается. Формат [^…] , наоборот, помещает в строку символы, не входящие в указанный набор, до тех пор пока не встретит любой из указанных.
В примере ниже как только поступает не цифра, считывание ввода завершается. При этом если первый символ — не цифра, то в str вообще ничего не записывается:
char str[30]=""; scanf("%[0-9]", str); printf("%s\n", str);
А в этом случае строке будет присвоена последовательность символов до любого из указанных знаков препинания:
scanf("%[^;. ]", str); printf("%s\n", str);
one two three four five! one two three four five
Обратите внимание, что в примере выше в строку были записаны как символы пробелов, так и символ перехода на новую строку. Таким образом, если надо прочитать одну строку вместе с пробелами, можно использовать такой подход:
scanf("%[^'\n']", str);
Здесь в строку считываются все символы, кроме перехода на новую строку. Как только встречается этот символ ‒ ‘\n’ , запись данных в переменную прекращается.
Некоторые особенности и ограничения функции scanf
Как только поступают некорректные данные, функция scanf() завершает свою работу. В примере:
scanf("%d%f", &a, &b);
если переменной a попытаться присвоить символ или строку, что невозможно, то в переменную b потом уже не получится записать число. Можно предположить, что так будет надежнее:
scanf("%d", &a); scanf("%f", &b);
Вроде бы неудачное считывание a не должно оказывать никакого влияния на b , т.к. это уже иной вызов scanf() . Но не все так просто: при некорректном вводе данные остаются в буфере и пытаются «навязать» себя последующим вызовам scanf() . Поэтому при использовании scanf() надо думать о том, как в случае некорректного ввода очистить буфер. Например, это можно сделать так, как показано ниже, или путем использования специальных функций (здесь не рассматриваются):
// если данные не удалось присвоить, if (scanf("%d", &a) != 1) // то выбросить их в виде строки scanf("%*s"); scanf("%f", &b);
- На прошлом занятии вы написали программу, содержащую функции, вычисляющие факториал числа и заданный элемент ряда Фибоначчи. Измените эту программу таким образом, чтобы она запрашивала у пользователя, что он хочет вычислить: факториал или число Фибоначчи. Затем программа запрашивала бы у пользователя либо число для вычисления факториала, либо номер элемента ряда Фибоначчи.
- Напишите программу, которая запрашивает у пользователя две даты в формате дд.мм.гггг. Дни, месяцы и года следует присвоить целочисленным переменным. Программа должна выводить на экран информацию о том, какая дата более ранняя, а какая более поздняя.
- Используя цикл, напишите код, в котором пользователю предлагается вводить данные до тех пор, пока он не сделает это корректно, т.е. пока все указанные в scanf() переменные не получат свои значения. Протестируйте программу.
Курс с решением задач:
pdf-версия
Пользовательский ввод чисел в строку
Функция input() возвращает все в виде строки, поэтому нужно выполнить явную конвертацию, чтобы получить целое число. Для этого пригодится функция int() .
# вывод суммы двух чисел, введенных пользователем
num_1 = int(input("Введите первое число: "))
num_2 = int(input("Введите второе число: "))
print("Тип num_1:", type(num_1))
print("Тип num_2:", type(num_2))
result = num_1 + num_2
print("Сумма введенных чисел:", result)int(string) конвертирует переданную строку в целое число.
Ввода числа float
По аналогии можно использовать функцию float() .
float_1 = float(input("Введите число: "))
print("Тип float_1:", type(float_1))
result = float_1 ** 2
print("Число в квадрате:", result)Введите число: 1.8 Тип float_1: Число в квадрате: 3.24Ввод чисел в строку через пробел
Но что произойдет, если вы не знаете количество элементов ввода?
Предположим, что от пользователя нужно получить список чисел и вернуть их сумму. При этом вы не знаете количество элементов в этом списке. Как же запросить ввод для него?
Для этого можно использовать split и функции map . Метод split() делит введенную строку на список подстрок. После этого map() выполняет функцию int() для каждого элемента списка.
entered_list = input("Введите список чисел, разделенных пробелом: ").split()
print("Введенный список:", entered_list)
num_list = list(map(int, entered_list))
print("Список чисел: ", num_list)
print("Сумма списка:", sum(num_list))Введите список чисел, разделенных пробелом: 1 34 4 6548 Введенный список: ['1', '34', '4', '6548'] Список чисел: [1, 34, 4, 6548] Сумма списка: 6587
- input() возвращает список, содержащий числа, разделенные запятыми.
- split() возвращает список строк, разделенных пробелами.
- map() выполняет операцию int() для всех элементов списка и возвращает объект map .
- list() конвертирует объект map снова в список.
Есть альтернативный способ получить список:
entered_list = input("Введите список чисел, разделенных пробелом: ").split()
num_list = [int(i) for i in entered_list]
print("Список чисел: ", num_list)Обработка ошибок при пользовательском вводе
Часто при конвертации типов возникает исключение ValueError .
Это происходит в тех случаях, когда введенные пользователем данные не могут быть конвертированы в конкретный тип.
Например, пользователь вводит случайную строку в качестве возраста.
num = int(input("Введите возраст: "))Функция int() ожидает целочисленное значение, обернутое в строку. Любое другое значение приводит к ошибке. Вот что будет, если, попробовать ввести «Двадцать»:
Введите возраст: Двадцать --------------------------------------------------------- ValueError Traceback (most recent call last) in ----> 1 num_1 = int(input('Введите возраст: ')) ValueError: invalid literal for int() with base 10: 'Двадцать'Чтобы убедиться, что пользователь вводит только подходящую информацию, нужно обработать массу подобных ошибок. Для этого будем использовать перехват исключений.
try:
num = int(input("Введите число: "))
print("Все верно. Число:", num)
except ValueError:
print("Это не число.")Посмотрим, как ввод «Двадцать» сработает теперь:
Введите число: Двадцать Это не число.В этом примере если пользователь вводит нечисловое значение, то возникает исключение. Однако оно перехватывается инструкцией except , которая в ответ выводит: «Это не число». Благодаря использованию конструкции try-except программа не прекратит работать при некорректном вводе.
Я создал этот блог в 2018 году, чтобы распространять полезные учебные материалы, документации и уроки на русском. На сайте опубликовано множество статей по основам python и библиотекам, уроков для начинающих и примеров написания программ.
Мои контакты: Почта
Python Q https://yandex.ru/q/loves/python Online
Python Q CEO Pythonru admin@pythonru.com https://secure.gravatar.com/avatar/b16f253879f7349f64830c64d1da4415?s=96&d=mm&r=g CEO Pythonru Python Александр Редактор https://t.me/cashncarryhttps://pythonru.com/https://yandex.ru/q/profile/cashnc/ PythonRu.com admin@pythonru.com Alex Zabrodin 2018-10-26 Online Python, Programming, HTML, CSS, JavaScript
Обработать неизвестное число строк из файла СИ
Решаю универскую задачку и встал вопрос: а как считать из файла неизвестное число строк исключительно на языке СИ? На с++ таких проблем нет, а вот как это сделать тут? Например, имею следующий код, который перенаправляет поток ввода из консоли в файл:
#include #include #include int main() < freopen("input.txt", "r", stdin); while (true) < char buff[100]; scanf("%s", buff); if (!strlen(buff))//пытаюсь таким образом определить конец файла break; >>На c++ можно остановить ввод с помощью !file.eof() , есть ли в данном случае сишная альтернатива? Заранее спасибо за ответ!
UPD: нашёл информацию о EOF и в си, но непонятно с чем сравнивать этот макрос в примере, чтоб понять - закончился ли файл.Отслеживать
Максим Герасимов
задан 20 фев 2022 в 15:25
Максим Герасимов Максим Герасимов
131 8 8 бронзовых знаков2 ответа 2
Сортировка: Сброс на вариант по умолчанию
"На c++ можно остановить ввод с помощью !file.eof() " — не стоит этого делать! См. Правильное использование проверки конца файла
Если уж читаете построчно - просто читайте while(fgets(buff,100,file))
