Форум программистов, компьютерный форум CyberForum.ru

Как реализована функция printf - C++

Восстановить пароль Регистрация
Другие темы раздела
C++ Элементы матрицы переписать построчно в одномерный массив http://www.cyberforum.ru/cpp-beginners/thread604070.html
Задача - элементы заданой матрицы A(N,N) переписовать построчно в одномерный массив до тех пор, пока не встретится нулевой элемент.(в С++)
C++ Меню на с++ Задание: Написать программу на c++ это должно быть меню, при этом в каждый из пунктов можно было заходить, и при нажатии на exit программа бы показывала вопрос, и в случае нажатия на yes выходила бы. http://www.cyberforum.ru/cpp-beginners/thread604060.html
C++ Создание и запись в файл
программа 1) создаёт файл 2) записывает данные в конец файла 3) записывает данные в начало файла 4) записывает данные в середину файла есть идеи? сказали, что делать с помощью fseek ()
C++ Рекурсивный поиск файлов и папок
в консоли выводится бесконечный повторяющийся список, как этого избежать?! #include <stdio.h> #include <windows.h> #include <conio.h> #include <string> using namespace std; void findfiles(string dir){ HANDLE hFind;
C++ Как правильно собрать код http://www.cyberforum.ru/cpp-beginners/thread604024.html
Доброго времени суток. Нужно нижеприведенный код собрать в кучу. Помогите разобрать, что куда. Условие. Нужно написать приложение создающее в своем окне, однострочный редактор текста и кнопку с надписью ОК. После ввода текста и нажатии кнопки, на экране появляется сообщение из введенного текста. //До winmain # define ID_EDIT // будет использоваться при обработке сообщения текстового...
C++ GDI+, насколько актуален этот интерфейс Добрый вечер! Вот задался вопросом насчет GDI+, насколько актуален этот интерфейс,где его конкретно применяли, какие продукты написаны с его применением. Я особо ничего не нагуглил... P.S. Интересно посмотреть как они выглядят,а то всякие елипсы и прямоугольники студентов не очень впечетляют8-) подробнее

Показать сообщение отдельно
Avazart
 Аватар для Avazart
6904 / 5144 / 253
Регистрация: 10.12.2010
Сообщений: 22,629
Записей в блоге: 17
13.06.2012, 01:39  [ТС]     Как реализована функция printf
Читаю
Для определения функции с переменным числом параметров необходимо знать две вещи: как задать переменный список в прототипе функции и как обрабатывать переменный список в теле функции. Переменный список параметров задается в заголовке функции многоточием:int f(…)

Этот заголовок не вызывает у компилятора протестов. Такая запись означает, что при определении функции компилятору неизвестны ни количество параметров, ни их типы, и он, естественно, не может ничего проверить. Количество параметров и их типы становятся известными только при вызове функции.
Однако у программиста с написанием таких функций сразу возникают проблемы. Ведь имена параметров отсутствуют! Спрашивается, как же написать алгоритм обработки, не зная имен параметров? Естественно, только одним способом – использовать для доступа к параметрам указатель! Вспомним, что все параметры при вызове помещаются в стек. Если мы каким-то образом установим указатель на начало списка параметров в стеке, то манипулируя с указателем, мы, в принципе, можем «достать» все параметры!
Таким образом, совсем пустой список параметров мы написать не можем — должен быть прописан хотя бы один явный параметр, адрес которого мы собираемся получить при выполнении программы. Заголовок такой функции может выглядеть так:
C++
1
int f(int k...)
Как мы видим, ни запятая, ни пробел после параметра не обязательны, хотя можно их и прописать.
При написании функции с переменным числом параметров помимо алгоритма обработки программист должен разрабатывать и алгоритм доступа к параметрам. Нам надо ответить на два вопроса:
как «перебирать» параметры;
как закончить перебор.
Ответ на первый вопрос очевиден – надо изменять значение указателя, чтобы переместиться на следующий параметр. Отсюда следует, что указатель должен быть типизированным, поскольку с бестиповым указателем нельзя выполнять арифметические операции. Это же означает, что программист при разработке функции с переменным числом параметров должен отчетливо себе представлять типы аргументов, которые будет обрабатывать функция. Кроме того, способ передачи параметров должен быть одинаковым для всех: либо все – по значению, либо все – по ссылке, либо все – по указателю.
Передача аргумента не того типа, который задумывался, или не тем способом, который подразумевался при разработке, приведет к катастрофическим последствиям – компилятор-то ничего не проверяет.
Ответ на второй вопрос не вызывает затруднений. Это можно сделать одним из двух способов:
* явно передать среди обязательных параметров количество аргументов;
* добавить в конец списка аргумент с уникальным значением, по которому будет определяться конец списка параметров;
И тот, и другой способ имеют право на жизнь — все определяется потребностями задачи и вкусами программиста.
Попробуем написать функцию, вычисляющую среднее арифметическое своих аргументов. Пусть наша функция обрабатывает последовательность чисел типа double. Для ограничения списка используем второй способ: последним значением списка параметров будет ноль. Это означает, что все числа должны быть положительными. Ограничение, конечно, слишком сильное, но мы не будем придавать этому большое значение, так как нам важнее разобраться с доступом к списку параметров.
Таким образом, определение функции будет выглядеть так:double f(double n, ...) //--заголовок с переменным числом параметров
C++
1
2
3
4
5
6
7
8
9
{   double *p = &n;            //--установились на начало списка параметров
    double sum = 0, count = 0;    
    while (*p)                 //--пока аргумент не равен нулю
    { sum+=(*p);             //--суммируем аргумент
      p++;                 //--«перемещаемся на следующий аргумент
      count++;                 //--считаем  количество аргументов
    }
    return (sum/count);            //--вычисляем среднее
}
Вызов такой функции может выглядеть таким образом:
C++
1
double y = f(1.0, 2.0, 3.0, 4.0, 0.0);
Переменная y получит значение 2.5. Интересно, что попытка вызвать такую функцию с целыми аргументами f(1,2,3,0) в Visual C++ 6 приводит к явно неверному результату, в то время как в системе Borland выдается неверный, но правдоподобный результат 0.5. Интересно, что такой же результат получается в Visual C++ 6, если завершить список целых параметров двумя нулями: f(1,2,3,0,0). Это потому, что типы параметров не проверяются.
Реализация функции, которая в качестве первого параметра получает количество аргументов, на первый взгляд, не вызывает затруднений. Однако, если первый аргумент – целое число, то требуется преобразование указателя. И тут не все варианты проходят. Не будет работать такой вариант:
C++
1
2
3
4
5
6
7
double f(int n, ...)            //--количество элементов
{   int *p = &n;            //--указатель – «целый»
    double sum = 0, count = n;
    for (;n--;(double *)p++)         //--преобразование указателя в «double» 
      sum+=(*p); 
    return (sum/count);
}
Такой вариант тоже неработоспособен
C++
1
2
3
4
5
6
7
double f(int n, ...)            //--количество элементов
{   double *p = (double *)&n;        //--преобразование адреса
    double sum = 0, count = n;
    for (;n--;p++)             //--изменение указателя 
      sum+=(*p); 
    return (sum/count);
}
Причина кроется в том, что изменение указателя производится на столько байт, сколько в памяти занимает базовый тип. В обоих случаях мы установились не на начало списка double-параметров, а на sizeof(int) байтов «раньше» — на целую переменную. И от этого адреса происходит изменение указателя на 8 (размер переменных типа double), что приводит к совершенно неверным результатам. Решение заключается в том, чтобы сначала изменить «целый» указатель, а потом уже его преобразовать в double*. Так всегда необходимо делать, если тип первого параметра отличается от типов отсутствующих параметров.
C++
1
2
3
4
5
6
7
8
9
double f(int n, ...)            //--количество элементов
{   int *p = &n;
    p++;                //--изменение «целого» на sizeof(int)
    double *pp = (double *)p;        //--преобразование адреса
     double sum = 0, count = n;
     for (;n--;pp++)             //--правильное увеличение на 8
       sum+=(*pp); 
     return (sum/count);
}
Преобразование указателя написано в стиле С, но только потому, что так короче.

То же самое — стандартными средствами

В качестве примера рассмотрим реализацию функции вычисления среднего арифметического (вариант с параметром-количеством аргументов) с использованием этих макросов.
C++
1
2
3
4
5
6
7
8
9
10
11
double f(int n, double a, ...)
{   va_list p;                 //--объявление указателя
    double sum = 0, count = 0;
    va_start(p, n);            //--инициализация указателя
    while(n--)             
    {   sum+=va_arg(p,double);        //--перемещение указателя 
        count++; 
    }
    va_end(p);                //--«закрытие» указателя
    return (sum/count);
}
Очень похоже выглядит и вариант с нулем в конце списка.
C++
1
2
3
4
5
6
7
8
9
10
11
12
double f(double a, ...)
{   va_list p;                 //--объявление указателя
    double sum = 0, count = 0;
    va_start(p, a);            //--инициализация указателя
    double k = a;                //--промежуточная переменная 
    do {
        sum+=k;
        count++;
    } while(k=va_arg(p,double));     //--пока не ноль в списке-передвигаемся
    va_end(p);                //--«закрыли» указатель
    return (sum/count);
}
Однако в этом случае удобно использовать цикл do while, так как указатель p сразу устанавливается на слагаемое. Передвижение по списку выполняется прямо в условии цикла, что обеспечивает одновременную проверку на ноль.
http://www.rsdn.ru/forum/cpp/777944.all.aspx

Добавлено через 17 минут


Ага вот нашел...кое что более нормальное
http://www.cplusplus.com/reference/clibrary/cstdarg/

А есть ли книжная литература по данному вопросу?
По тому как раньше не встречал....
 
Текущее время: 23:17. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru