-
Notifications
You must be signed in to change notification settings - Fork 160
/
Copy pathvarargs_h.c
153 lines (112 loc) · 3.85 KB
/
varargs_h.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
149
150
151
152
153
/*
# varargs.h
Header that contains helpers necessary
to deal with the variadic function language feature.
# Variadic functions
Language feature that allows functions with variable number of arguments.
They are the functions that contain three dots as an argument `...`
# va_start
va_start(varname, last_argname)
Initialize `va_list` variable varname. Indicates that varargs come after `last_argname`.
`last_argment` is the last non-variadic argument. It's value is not important: only it's address.
This is only possible because `va_list` is a macro, which enables it to the the address of `last_argname`.
Mandatory.
TODO: what does it really do in assembly?
# va_end
Analogous to va_start after usage. Mandatory.
# Vs C99 array literal
If all arguments are of a single type, use arrays, they are saner.
*/
#include "common.h"
int variadic_add(int numargs, ...) {
va_list args;
/* numargs is just the first argument, not necessarily the length! */
va_start(args, numargs);
int sum = 0;
for (int i = 0 ; i < numargs; i++)
sum += va_arg(args, int);
va_end(args);
return sum;
}
int variadic_add_null(int dummy, ...) {
int arg;
int sum;
va_list args;
va_start(args, dummy);
sum = 0;
while (1) {
arg = va_arg(args, int);
if (!arg)
break;
sum += arg;
}
va_end(args);
return sum;
}
int sprintf_wrapper(char *s, const char *fmt, ...) {
int ret;
va_list args;
va_start(args, fmt);
ret = vsprintf(s, fmt, args);
va_end(args);
return ret;
}
int main(void) {
/* Basic examples */
assert(variadic_add(3, 1, 2, 3) == 6);
assert(variadic_add(5, 1, 2, 3, 4, 5) == 15);
/*
# Variadic function without non variadic arguments
Not possible:
http://stackoverflow.com/questions/2622147/is-it-possible-to-have-a-variadic-function-in-c-with-no-non-variadic-parameter
`va_start` takes the first argument.
*/
/*
# Get number of variadic arguments
It is impossible except from other arguments.
http://stackoverflow.com/questions/5272703/how-do-vararg-functions-find-out-the-number-of-arguments-in-machine-code
`printf` just deduces that form the format string, and reads the stack away
*/
{
/* Extra integer argument technique. */
assert(variadic_add(3, 1, 2, 3) == 6);
/*
Trailing null pointer technique `(void *)NULL`.
TODO: how to distinguish 0 and (void *)0?
C standard says they should compare equal, so impossible?
But then why not use `0` instead of `NULL`?
*/
/*assert(variadic_add_null(3, 1, 2, 0, 3, (void *)0) == 6);*/
}
/*
# vprintf
This is the raison d'etre for the `vprintf` family, which takes a va_list argument.
It allows you to forward `va_list` easily.
*/
{
char s[32];
sprintf_wrapper(s, "%c", 'a');
assert(s[0] == 'a');
/*
The only problem with the wrapper is that compile time error checking is not done.
This could be achieved via the gcc `__attribute__((format,X,Y))` extension
*/
{
/* Error checking is not done for the wrapper. */
/* Might segfault at runtime. */
char s[32];
if (0) {
sprintf_wrapper(s, "%s" /*missing arg*/);
printf("sprintf_wrapper wrong = %s\n", s);
}
/* WARN type error checking is done for `sprintf`. */
/*sprintf(s, "wrong %s");*/
}
}
/*
# Array argument
http://stackoverflow.com/questions/280940/calling-a-c-function-with-a-varargs-argument-dynamically
http://stackoverflow.com/questions/14705920/passing-an-array-as-parameters-to-a-vararg-function
*/
return EXIT_SUCCESS;
}