Форум программистов, компьютерный форум, киберфорум
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 81, средняя оценка - 4.83
Uklunok
3 / 3 / 0
Регистрация: 08.05.2010
Сообщений: 135
#1

Как перенести параметры из ф-ции printf() в самодельную - C++

04.06.2011, 09:22. Просмотров 11119. Ответов 103
Метки нет (Все метки)

Ребят, подскажите как решить задачу.
Нужно перенести параметры функции printf() в самодельную ф-цию myfunk(). При условии если булева переменная Х==1. Если не равна 1, то не переносить.
Задача осложняется тем, что число параметров ф-ции может быть переменным.
Подскажите пожалуйста, буду очень признателен.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
04.06.2011, 09:22
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Как перенести параметры из ф-ции printf() в самодельную (C++):

Передача ф-ции как параметра другой ф-ции - C++
struct pupil{ char surname; int school; int answer; int num; } tmp, a; int usl(int i, pupil a) { }

Параметры printf - C++
Здравствуйте дорогие форумчане ;) У меня есть вопрос по следующему куску кода. Решив по практиковаться с консольным приложением у меня...

Динамические параметры printf() - C++
Есть код printf("%5s <-- %14s\n", buffer0.c_str(), "n/a"); Что делать если я не знаю на этапе компиляции числа 5 и 14? Для...

Чем ::printf предпочтительнее printf? - C++
Смотрю на код одного толкового программиста и, как новичек в С++, удивляюсь: какой смысл писать в таком стиле? Это же вроде одно и то же....

Калькулятор на Си. Как реализовать триногометрические ф-ции и функцию логарифм? - C++
Написал код в котором надо вводить каждый елемент(первое число, знак операции, второе число, знак операции, 3... знак "=") по отдельности....

Как перенести параметры с XP на 7 - Windows
Не знаю в какой ветке разместить тему, то ли в XP, то ли в 7... Всё началось с того, что установив win7, я не смог установить драйвер на...

103
Kastaneda
Jesus loves me
Эксперт С++
4689 / 2893 / 236
Регистрация: 12.12.2009
Сообщений: 7,356
Записей в блоге: 2
Завершенные тесты: 1
04.06.2011, 14:55 #16
Цитата Сообщение от fasked Посмотреть сообщение
а чем va_list не угодил?
Поскольку в функциями с переменным числом аргументов работать приходится не часто, то я, честно говоря, и не знал про va_list. А когда нужно было, то делал так, как показал выше.

Uklunok, т.е. ты хочешь влезть в код printf() ?

Добавлено через 1 минуту
Цитата Сообщение от Uklunok Посмотреть сообщение
Во первых выводить сообщение никакого ни надо, и моя самодельная функция тоже не должна выводить сообщение.
Это просто пример, как реализовать функцию с переменным числом аргументов, она не обязательно должна что-то выводить.
0
fasked
Эксперт С++
4948 / 2528 / 180
Регистрация: 07.10.2009
Сообщений: 4,311
Записей в блоге: 1
04.06.2011, 15:10 #17
Цитата Сообщение от Uklunok Посмотреть сообщение
то нужно скопировать их в самодельную ф-цию.
Так тоже самое все, только более опасное
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdarg.h>
 
void myprintf(const char *format, ...) {
    /* DO SOMETHING */
}
 
/* если убрать эту строку, то будет использоваться
 * библиотечный стандартный printf
*/
#define USE_MY_PRINTF
 
#if defined USE_MY_PRINTF
#define printf myprintf
#endif
 
int main() {
    printf("%d.%d %s", 10, 15, "TEST");
}
На самом деле так делать неправильно.
0
Evg
Эксперт CАвтор FAQ
18253 / 6378 / 438
Регистрация: 30.03.2009
Сообщений: 17,656
Записей в блоге: 28
04.06.2011, 15:23 #18
Цитата Сообщение от grizlik78 Посмотреть сообщение
Ну тем хуже, значит код непереносим из-за привязки к конкретному ABI
Чтобы доставать аргументы для "..." нужно использовать va_arg. На каждой платформе этот интерфейс реализован по своему (в соответствии со своим ABI). Ибо на каждой платформе имеются свои правила передачи параметров

Добавлено через 3 минуты
Цитата Сообщение от Uklunok Посмотреть сообщение
C
1
void myprintf(const char*,...);
- а это значит, что функция может принимать неограниченное количество параметров???
Самое интересное, что программа выводит сообщение, хотя нет ни одной ф-ции printf().

Вы все меня не очень хорошо поняли.
Во первых выводить сообщение никакого ни надо, и моя самодельная функция тоже не должна выводить сообщение.
Тут получается: надо вытащить из библиотеки ф-цию printf() и скопировать её параметры в самодельную ф-цию. И соответственно, если непосредственно в программе есть такая строка:
C
1
printf("%d.%d %s", 10, 15, "TEST");
то нужно скопировать их в самодельную ф-цию.
Вот
Такая постановка задачи честными способами практически не решается. Единственный с виду способ - это реализовать свою функцию под названием printf, которая будет вызывать "настоящий" printf и дополнительно делать свои пляски с бубном. Для вызова "настощего" printf'а можно воспользоваться vprintf'ом из поста #6

Ты лучше поясни, нафига тебе это нужно. Потому что есть подозрение, что у тебя есть постановка задачи, но ты пошёл её решать неверным путём
1
Kastaneda
04.06.2011, 15:27
  #19

Не по теме:

Чтобы доставать аргументы для "..." нужно использовать va_arg. На каждой платформе этот интерфейс реализован по своему (в соответствии с о своим ABI). Ибо на каждой платформе имеются свои правила передачи параметров
Значит va_arg - это не просто удобство. Ясно, спасибо)

0
Evg
04.06.2011, 15:28
  #20

Не по теме:

Цитата Сообщение от Kastaneda Посмотреть сообщение
Значит va_arg - это не просто удобство
Просто удобства обычно в Си++ или C#

0
Uklunok
3 / 3 / 0
Регистрация: 08.05.2010
Сообщений: 135
04.06.2011, 16:00  [ТС] #21
Цитата Сообщение от Evg Посмотреть сообщение

Ты лучше поясни, нафига тебе это нужно. Потому что есть подозрение, что у тебя есть постановка задачи, но ты пошёл её решать неверным путём
Ну если Вам интересно, это для компилятора Vinculum II. Микроконтроллеры семейства FTDI.

Собственно, чтобы более понятно было, мне нужно добавить в этот код ( это код для примера работы с функцией printf()):
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
** Runtime.c
**
** Copyright © 2010 Future Devices International Limited
**
**  C Source file for Vinculum II sample application
** Main module
**
** Author: FTDI
** Project: Vinculum II
** Module: Vinculum II Sample Applications
** Requires: VOS UART USBHost
** Comments:
**
** History:
**  1 – Initial version
**
*/
 
#include "vos.h"
 
#include "UART.h"
 
#include "config.h"
#include "ctype.h"
#include "errno.h"
#include "stdarg.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
 
VOS_HANDLE      hUart;
 
vos_tcb_t       *tcbFirmware;
 
void firmware(void);
 
char *months[] = {"January", "February", "March", "April",
                  "May", "June", "July", "August",
                  "September", "October", "November", "December"};
 
void main(void)
{
    // UART configuration context
    uart_context_t uart_ctx;
 
    vos_init(10, VOS_TICK_INTERVAL, 1);
    vos_set_clock_frequency(VOS_48MHZ_CLOCK_FREQUENCY);
 
    if (vos_get_package_type() == VINCULUM_II_64_PIN)
    {
        // UART to V2EVAL board pins
        vos_iomux_define_output(32,IOMUX_OUT_UART_TXD); //UART Tx
        vos_iomux_define_input(39,IOMUX_IN_UART_RXD); //UART Rx
        vos_iomux_define_output(40,IOMUX_OUT_UART_RTS_N); //UART RTS#
        vos_iomux_define_input(41,IOMUX_IN_UART_CTS_N); //UART CTS#
    }
    else // VINCULUM_II_48_PIN
    {
        // UART to V2EVAL board pins
        vos_iomux_define_output(41,IOMUX_OUT_UART_TXD); //UART Tx
        vos_iomux_define_input(42,IOMUX_IN_UART_RXD); //UART Rx
        vos_iomux_define_output(43,IOMUX_OUT_UART_RTS_N); //UART RTS#
        vos_iomux_define_input(44,IOMUX_IN_UART_CTS_N); //UART CTS#
    }
 
    uart_ctx.buffer_size = VOS_BUFFER_SIZE_128_BYTES;
    uart_init(0, &uart_ctx);
 
    tcbFirmware = vos_create_thread(29, 0x1000, firmware, 0);
 
    vos_start_scheduler();
 
main_loop:
    goto main_loop;
}
 
unsigned short ush;
 
void firmware(void)
{
    // UART ioctl request block
    common_ioctl_cb_t uart_iocb;
    char x[4];
    char mth;
    short sh;
    char ch;
 
    // open the USB and UART interfaces
    hUart = vos_dev_open(0);
 
    // setup the UART interface
    uart_iocb.ioctl_code = VOS_IOCTL_COMMON_ENABLE_DMA;
    vos_dev_ioctl(hUart,&uart_iocb);
 
    // set baud rate
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_BAUD_RATE;
    uart_iocb.set.uart_baud_rate = UART_BAUD_9600;
    vos_dev_ioctl(hUart,&uart_iocb);
 
    // set flow control
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_FLOW_CONTROL;
    uart_iocb.set.param = UART_FLOW_RTS_CTS;
    vos_dev_ioctl(hUart,&uart_iocb);
 
    // set data bits
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_DATA_BITS;
    uart_iocb.set.param = UART_DATA_BITS_8;
    vos_dev_ioctl(hUart,&uart_iocb);
 
    // set stop bits
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_STOP_BITS;
    uart_iocb.set.param = UART_STOP_BITS_1;
    vos_dev_ioctl(hUart,&uart_iocb);
 
    // set parity
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_PARITY;
    uart_iocb.set.param = UART_PARITY_NONE;
    vos_dev_ioctl(hUart,&uart_iocb);
 
    stdioAttach(hUart);
 
    printf("\nNo format\n");
 
    ch = -46;
    printf("Decimal signed %d\n", ch);
    printf("Decimal unsigned %u\n", -46);
    sh = -48;
    printf("Decimal signed %d unsigned %u\n", sh, -47);
 
    ush = 0xface;
    printf("Hex caps %X lower %x\n", 0xfeed, ush);
 
    fprintf(stderr, "Character %c\n", 0x41);
 
    printf("String %s and %s\n", "here", "here!");
 
    printf("Pointers %p %p %p\n", &uart_iocb, &ch, &ush);
 
    printf("Escape d-quote \" s-quote \' tab \t bslash \\\n");
 
    fwrite("--HELLO--\r\n", 11, 1, stdout);
 
    for (mth = 1; mth <= sizeof(months)/sizeof(char*); mth++)
    {
        printf("Month %d is %s\n", mth, months[mth - 1]);
    }
}
Сам исходный код ф-ции я не знаю, т.к. он в билиотеке, открытого нет. Придётся обходиться малой кровью, сам не знаю как. А если скопировать параметры из ф-ции printf() которые есть в этом когде??
В общем сильно на меня не кричите))

Добавлено через 3 минуты
Задание звучит так: реализация ф-ций с переменным числом параметров на языке "с" и передача значений в такую ф-цию.
0
ForEveR
В астрале
Эксперт С++
7983 / 4742 / 321
Регистрация: 24.06.2010
Сообщений: 10,545
Завершенные тесты: 3
04.06.2011, 16:24 #22
C
1
2
main_loop:
    goto main_loop;
первый раз такое вижу... бесконечный цикл с помощью меток...
0
Uklunok
3 / 3 / 0
Регистрация: 08.05.2010
Сообщений: 135
04.06.2011, 16:32  [ТС] #23
Тут походу просто в бесконечном цикле по УАРТ передаётся информация (ф-ция printf() выводит сообщения)
0
Evg
Эксперт CАвтор FAQ
18253 / 6378 / 438
Регистрация: 30.03.2009
Сообщений: 17,656
Записей в блоге: 28
04.06.2011, 17:35 #24
Цитата Сообщение от Uklunok Посмотреть сообщение
Собственно, чтобы более понятно было, мне нужно добавить в этот код ( это код для примера работы с функцией printf())
Добавить что?

Цитата Сообщение от Uklunok Посмотреть сообщение
Задание звучит так: реализация ф-ций с переменным числом параметров на языке "с" и передача значений в такую ф-цию
В посте #6 есть пример. Что в нём не так?

Вообще, внятно поставленный вопрос - это уже половина ответа. А мне думается, что никто до сих пор не понял, чего конкретно ты хочешь
0
Jupiter
Каратель
Эксперт С++
6559 / 3980 / 227
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
Завершенные тесты: 2
04.06.2011, 19:35 #25
Цитата Сообщение от Uklunok Посмотреть сообщение
Задание звучит так: реализация ф-ций с переменным числом параметров на языке "с" и передача значений в такую ф-цию.
и для чего сдесь выдерание параметров из printf ?
0
ValeryLaptev
Эксперт С++
1042 / 821 / 48
Регистрация: 30.04.2011
Сообщений: 1,659
04.06.2011, 20:02 #26
Лучший ответ Сообщение было отмечено автором темы, экспертом или модератором как ответ
Вот информация о функциях с переменным числом параметров.

Функции с переменным числом параметров
Язык C++ вслед за С позволяет писать функции с переменным числом параметров. Одним из простых примеров может служить функция, вычисляющая среднее арифметическое своих аргументов. Другой уже классический пример — функция сцепления произвольного количества строк, которая является естественным обобщением функции сцепления двух строк.
Переменный список параметров задается в заголовке функции многоточием:
C++
1
int f()
Этот заголовок не вызывает у компилятора протестов. Такая запись означает, что при определении функции компилятору неизвестны ни количество параметров, ни их типы, и он, естественно, не может ничего проверить. Количество параметров и их типы становятся известными только при вызове функции.

Однако у программиста с написанием таких функций сразу возникают проблемы. Ведь имена параметров отсутствуют! Поэтому доступ можно осуществить только одним способом – косвенным, используя указатель. Вспомним, что все параметры при вызове помещаются в стек. Если мы каким-то образом установим указатель на начало списка параметров в стеке, то, манипулируя с указателем, мы, в принципе, можем «достать» все параметры!

Таким образом, список параметров совсем пустой быть не может, должен быть прописан хотя бы один явный параметр, адрес которого мы можем получить при выполнении программы. Заголовок такой функции может выглядеть так:
C++
1
int f(int k...)
Ни запятая, ни пробел после параметра не обязательны, хотя можно их и прописать.

Есть одно обстоятельство, которое ограничивает применение таких функций: при написании функции с переменным числом параметров помимо алгоритма обработки программист должен разрабатывать и алгоритм доступа к параметрам. Так что список необъявленных параметров не может быть совсем уж произвольным – в языке C++ не существует универсальных средств распознавания элементов этого списка. Это же означает, что передача аргумента не того типа, который задумывался, или не тем способом, который подразумевался при разработке, приведет к катастрофическим последствиям – компилятор-то ничего не проверяет.

Попробуем написать функцию, вычисляющую среднее арифметическое своих аргументов. Для этого требуется решить несколько проблем
- как установиться на список параметров в стеке;
- как «перебирать» параметры;
- как закончить перебор.
Для доступа к списку параметров нам потребуется указатель, значением которого будет адрес последнего явного параметра в списке. Ответ на второй вопрос очевиден – надо изменять значение этого указателя, чтобы переместиться на следующий параметр. Отсюда следует, что указатель должен быть типизированным, поскольку с бестиповым указателем нельзя выполнять арифметические операции. Это же означает, что программист при разработке функции с переменным числом параметров должен отчетливо себе представлять типы аргументов, которые будет обрабатывать функция. Кроме того, способ передачи параметров должен быть одинаковым для всех параметров: либо все – по значению, либо все – по ссылке, либо все – по указателю.

Ответ на последний вопрос не вызывает затруднений. Это можно сделать одним из двух способов:
- явно передать среди обязательных параметров количество аргументов;
- добавить в конец списка аргумент с уникальным значением, по которому будет определяться конец списка параметров;

И тот, и другой способ имеют право на жизнь — все определяется потребностями задачи и вкусами программиста. В данном случае сначала попробуем второй способ: последним значением списка параметров будет ноль (листинг 7.7).
C++
1
2
3
4
5
6
7
8
9
10
11
Листинг 7.7.  Вычисление среднего арифметического аргументов (ноль в конце)
double f(double n, ...) //--заголовок с переменным числом параметров
{   double *p = &n;     //--установились на начало списка параметров
    double sum = 0, count = 0;  
    while (*p)      //--пока аргумент не равен нулю
    { sum+=(*p);        //--суммируем аргумент
      p++;          //--«перемещаемся на следующий аргумент
      count++;      //--считаем  количество аргументов
    }
    return ((sum)?sum/count:0); //--вычисляем среднее
}
Вызов такой функции может выглядеть таким образом:
C++
1
double y = f(1.0, 2.0, 3.0, 4.0, 0.0);
Переменная y получит значение 2.5. Так как компилятор ничего не проверяет, то попытка вызвать такую функцию с целыми аргументами f(1,2,3,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++)         //--преобразование int* ->double* 
      sum+=(*p); 
    return ((sum)?sum/count:0);
}
Такой вариант тоже неработоспособен
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)?sum/count:0); 
}
Причина кроется в том, что изменение указателя производится на столько байт, сколько в памяти занимает базовый тип. В обоих случаях мы установились не на начало списка double-параметров, а на sizeof(int) байтов «раньше» — на целую переменную. И от этого адреса происходит изменение указателя на 8 байт (sizeof(double)), что приводит к совершенно неверным результатам. Решение заключается в том, чтобы сначала изменить «целый» указатель, а потом уже его преобразовать в double *. Так всегда необходимо делать, если тип первого параметра отличается от типов отсутствующих параметров (листинг 7.8).
C++
1
2
3
4
5
6
7
8
9
10
Листинг 7.8. Вычисление среднего арифметического аргументов (количество)
double f(int n, ...)            //--количество элементов
{  int *p = &n;
    p++;                //-3-установка «целого» на double
    double *pp = (double *)p;   //--преобразование типа указателя
    double sum = 0, count = n;
    for (;n--;pp++)             //--правильное увеличение на 8
       sum+=(*pp); 
    return ((sum)?sum/count:0);
}
В строке //-3- операция p++ устанавливает указатель на первый элемент списка параметров типа double. Для дальнейшего изменения указателя на 8 мы использовали преобразование типа указателя:
C++
1
double *pp = (double *)p;
После этой строки операция pp++ будет увеличивать указатель на sizeof(double)=8, что нам и требуется.

Мы использовали способ передачи параметров по значению. В этом случае в качестве фактических аргументов можно задавать произвольные выражения. Однако можно использовать и передачу ссылки – это несколько осложняет вызов, поскольку в этом случае в списке аргументов могут прописываться только переменные. Необходимо также помнить, что все фактические аргументы должны передаваться одинаковым способом. Тогда прототип первого варианта функции выглядит так (тело функции не изменяется):
C++
1
double f(double &n, ...)
При вызове можно использовать элементы массива:
C++
1
2
double m[] = {1.0,2.0,3.0,4.0,0.0};
cout <<f(m[0],m[1],m[2],m[3],m[4])<<endl;
Эта программа выведет на экран 2.5.

Язык C++ в качестве элементов переменного списка аргументов разрешает прописывать указатели. Однако обработка такого варианта вызывает сложности – нам требуется двойной косвенный доступ, а указатель для доступа к стеку — «одноразовый». Таким образом, если передавать параметры-указатели, то в приведенной выше программе (с первым параметром-количеством) значение *рр – это не число типа double, а адрес этого числа. Тут без «обмана» компилятора не обойтись, поскольку он просто так не пропускает преобразование «одноразовой» косвенности в двойную. Но мы помним, что все типизированные указатели, независимо от типа и косвенности всегда представляют собой адрес, размер которого в Intel – 4 байта. Тогда можно использовать union (листинг 7.9).
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
Листинг 7.9. Переменный список параметров-указателей (количество)
double f(int n, ...)
{  int *p = &n;     //--«одноразовый» указатель    
    p++;                    //--«достаем» список параметровd-указателей
   union Pointer 
   {double **pp; double *kp; }; //--«подстава» указателей
    Pointer A;
    A.kp = (double *)p;         //-«обманываем» компилятора
    double sum = 0, count = n;
    for (;n--;A.pp++)       //--изменяем двойной указатель!
         sum+=(**A.pp);         //--двойной доступ!
    return ((sum)?sum/count:0);
}
Хотя по стандарту такое использование union означает undefined behaviour (неопределенное поведение), и непереносимо, но на практике (например, на платформе Intel) работает хорошо. Однако при переносе на другую платформу надо будет проверять корректность работы такой функции.

Необходимо обратить внимание на то, что изменяем мы «двойной» указатель – «одноразовый» использовать нельзя, поскольку будет изменение на sizeof(double)=8 байт, а нам требуется изменение на sizeof(double*)= 4 байта. И при суммировании используется двойной косвенный доступ. Обращение к такой функции выполняется так:
c
C++
1
out <<f(2,&a,&b)<<endl;
Аналогичная функция без счетчика аргументов может использовать в качестве признака окончания списка параметров нулевой указатель. Но в данном случае «обманывать» компилятор не требуется — в функции непосредственно используется «двойной» указатель (листинг 7.10).
C++
1
2
3
4
5
6
7
8
9
10
Листинг 7.10. Переменный список параметров-указателей (ноль в конце)
double f(double *a, ...)
{   double **p = &a;        //--берем адрес-адреса
    double sum = 0, count = 0;
    while (*p!=0)       //-- NULL – прямо в списке параметров
    { sum+=(**p);       //--выбираем значения
       count++; p++;        //--бежим по списку
    }
    return ((sum)?sum/count:0);
}
Вызов такой функции выглядит так:
C++
1
f(&a,&b,0)
Особо обратите внимание на следующее: в списке параметров 0 – это значение, а не адрес. Поэтому в теле функции проверка окончания цикла делается с одной звездочкой, а не с двумя.
Напоследок осталось рассмотреть пример, в котором список указателей переменной длины составляют указатели на char. Мы выделяем этот случай по двум причинам:
- размер данных (char) меньше, чем размер указателя (char*) – в остальных случаях размер данных больше или равен размеру указателя;
- char* — это единственный указатель, вместо которого при вызове можно задавать не адрес.

Типичной функцией, в которой можно применить переменный список параметров, является функция сцепления произвольного количества строк в одну. Заголовок такой функции может выглядеть так:
C++
1
char *f(char *s1, ...)
Функция должна сначала вычислить количество памяти, необходимой для целевой строки, а потом уже помещать туда результат сцепления. Используем тот же прием, что и в предыдущем примере – последний параметр должен быть 0 (листинг 7.11).
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Листинг 7.11. Сцепление строк (ноль в конце)
char *f(char *s1, ...)
{ char **cp = &s1;          //--адрес первого указателя
   int len = 0;     
   // цикл для определения общей длины сцепляемых строк
   while (*cp) { len += strlen(*cp); cp++; }                        
  char *s = new char[len+1];    //--память для строки
  s[0]=0;                       //-- "очищаем" строку
// цикл для сцепления строк
  cp=&s1;                       //-- опять установка на 1-й параметр
  while (*cp) 
  {  strcat(s, *cp);            //-- прицепляем первую (и следующие)
     cp++;                      //-- перемещаемся на следующую
  }     
  return s;
}
Вызов такой функции может быть таким
C++
1
char *ss = f(s1, s2, s3, 0);
где s1, s2, s3 – это либо объявленные константы, либо переменные типа char *. Ту же функцию можно вызывать и с явно прописанными константами:
C++
1
char *sd = f(“First “,“Two  “,“Three “, 0);
Очевидно, вместо параметра-указателя (ссылки) можно подставлять выражение, имеющее результатом указатель (ссылку). В частности, на месте указателя на char можно вызвать функцию, которая вводит строку с клавиатуры.

Стандартные средства

В стандарт языка входит набор макросов для работы со списками параметров переменной длины, определенный в stdarg.h. При их использовании точно так же требуется указывать в списке явный параметр, объявить и установить на него указатель и перемещаться по списку, изменяя его. В конце списка должен стоять NULL. Макросы, обеспечивающие стандартный доступ к спискам параметров переменной длины, имеют следующий формат:
C++
1
2
3
void va_start(va_list prm, последний-явный-параметр);
тип va_arg(va_list prm, тип);
void va_end(va_list prm);
Тип указателя определяется с помощью оператора typedef как va_list. Макрос va_start устанавливает указатель типа va_list на явный параметр, макрос va_arg перемещает указатель на следующий параметр, а макрос va_end обнуляет указатель. Указанные макросы используются следующим образом:
1. в теле функции с переменным числом параметров до первого использования указанных макросов должно появиться объявление объекта типа va_list, например va_list LastP; фактически это является объявлением указателя;
2. указанный объект связывается с последним явным параметром (перед многоточием) переменного списка параметров с помощью макроса va_start, например va_start(LastP,P); таким образом происходит инициализация указателя;
3. передвижение по переменному списку параметров выполняется макросом va_arg. Для этого, как указано выше, тоже необходимо явно указывать тип очередного параметра, то есть программист должен его знать в момент написания программы. Если все параметры в списке целого типа, то вызов va_arg выглядит так: va_arg(LastP,int);
4. после всей обработки ставится вызов va_end, например va_end(LastP).

В качестве примера рассмотрим реализацию функции вычисления среднего арифметического (вариант с количеством аргументов) с использованием этих макросов (листинг 7.12).
C++
1
2
3
4
5
6
7
8
9
10
11
12
Листинг 7.12. Вычисление среднего с использование стандартных средств (количество)
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)?sum/count:0);
}
Очень похоже выглядит и вариант с нулем в конце списка (листинг 7.13).
C++
1
2
3
4
5
6
7
8
9
10
11
Листинг 7.13. Вычисление среднего стандартными средствами (ноль в конце)
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)? sum/count: 0);
}
Однако в этом случае удобно использовать цикл do while, так как указатель p сразу устанавливается на слагаемое. Передвижение по списку выполняется прямо в условии цикла, что обеспечивает одновременную проверку на ноль.
7
Evg
Эксперт CАвтор FAQ
18253 / 6378 / 438
Регистрация: 30.03.2009
Сообщений: 17,656
Записей в блоге: 28
04.06.2011, 23:39 #27
ValeryLaptev, в твоих примерах аргументы достаются не через va_arg, а через поинтерную арифметику. За такие статьи надо к стенке ставить, потому что начинающие начитаются такой ереси и начинают быдлокоды писать
0
ValeryLaptev
Эксперт С++
1042 / 821 / 48
Регистрация: 30.04.2011
Сообщений: 1,659
05.06.2011, 09:59 #28
Цитата Сообщение от Evg Посмотреть сообщение
ValeryLaptev, в твоих примерах аргументы достаются не через va_arg, а через поинтерную арифметику. За такие статьи надо к стенке ставить, потому что начинающие начитаются такой ереси и начинают быдлокоды писать
А вы про стандартные средства не увидели?
А во-вторых, надо знать, как НА САМОМ деле реализуются стандартные средства...
И не забывайте, что гроссмейстеры постоянно внушают: конь на краю доски - плохо! Но сами-то частенько так ходят! Все зависит от конкретной ситуации...
Ибо в-третьих, профи придумали это механизм для себя любимых. Если этого не пробовать, как становиться профи...
Вы про реализацию таблицы виртуальных функций помните? Указатель на указателе сидит, и указателем погоняет!
0
Uklunok
3 / 3 / 0
Регистрация: 08.05.2010
Сообщений: 135
05.06.2011, 11:56  [ТС] #29
Всё ребят, понял что нужно. Извините, что морочил Вам голову
Постараюсь объяснить что требуется:
Есть функция myprintf(), по сути она должна делать тоже самое что и printf(), т.е. выводить сообщение.
К примеру нам нужно вывести на экран сообщение:
C
1
2
3
4
5
int num_i; 
float num_f; 
num_i = 5; 
num_f = 10.5; 
printf(“num_i = %d, num_f = %f”, num_i, num_f);
Только вместо printf(......); должно стоять myprintf(......);
Значит нужно сделать так, чтобы ф-ция myprintf() вызывала из библиотеки stdio ф-цию printf(), вставляла туда нужные нам параметры для вывода и вывела их на экран. Прототип ф-ции находится в stdio.h а библиотека вот stdio.a.
Как это сделать не знаю.
В общем должно выглядеть так: код который я вставлял, в нём к примеру есть строка:
C
1
printf("String %s and %s\n", "here", "here!");
Я хочу чтобы было так:
C
1
myprintf("String %s and %s\n", "here", "here!");
Ну соответсвенно остальные строчки с printf тоже меняем на myprintf. И сделать не через макросы.
Вот как). Задача осложняется тем, что это не БИЛДЕР и не ВИЖАК, а компилятор микропроцессора, так что всё должно быть в очень примитивном виде (без сложных конструкций языка СИ)
Спасибо за внимание.

Добавлено через 2 минуты
Кстати в файле stdio.h прототип ф-ции выглядит так:
C
1
2
// write to stdout
int    printf(const char *fmt, ...);
0
grizlik78
Эксперт С++
1967 / 1460 / 120
Регистрация: 29.05.2011
Сообщений: 3,019
05.06.2011, 12:00 #30
Ну так в уже упомянутом посте номер 6 и есть решение вашей задачи в чистом виде. Только макросы оказались ненужными
И какие такие сложные конструкции языка?
0
05.06.2011, 12:00
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
05.06.2011, 12:00
Привет! Вот еще темы с ответами:

Не могу понять как исправить ошибку Warning: printf() [function.printf]: Too few arguments in Z:\home\ksards.ru\www\id\1.php on line 76 - PHP БД
Помогите связать бд с сайтом я новичок в этом вот код php &lt;?php do { printf(&quot;&lt;div class='blog'&gt;&lt;a href='2.php?id=%s'&gt;%s&lt;/a&gt;...

Узнать в ф-ции имя вызвавшей ф-ции - Python
Это возможно? Т.е. есть некая ф-ция debug(msg), передавать имя вызвавшей ее процедуры в параметрах не вариант, можно ли как-то внутри это...

DLL - как подменить функцию, используя самодельную DLL? - C++ Builder
Очень прошу пособить, кому не трудно... . Проблема в целом не сложная: имеется сторонняя программа (только exe), которая при определенном...

Перетаскивание формы за самодельную рамку - C#
Всем привет. Подскажите, убрал стандартную рамку у программы (FormBorderStyle = None) и сделал свою рамку из picturebox. Теперь я делаю...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
30
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru