I read this page to know how to use variable arguments:
https://www.tutorialspoint.com/cprogramming/c_variable_arguments.htm
All right, integer result is ok.
But when I replace its type to unsigned char, everything goes wrong:
#include <stdio.h>
#include <stdarg.h>
double average(int num,...) {
va_list valist;
double sum = 0.0;
int i;
/* initialize valist for num number of arguments */
va_start(valist, num);
/* access all the arguments assigned to valist */
for (i = 0; i < num; i++) {
sum += va_arg(valist, int);
}
/* clean memory reserved for valist */
va_end(valist);
return sum/num;
}
void foo(unsigned char arg_count,int num,...) {
va_list valist;
int i;
/* initialize valist for num number of arguments */
va_start(valist, num);
/* access all the arguments assigned to valist */
for (i = 0; i < arg_count; i++) {
printf("%02x,",va_arg(valist, int));
}
/* clean memory reserved for valist */
va_end(valist);
}
void bar(int num,...) {
va_list valist;
int i;
/* initialize valist for num number of arguments */
va_start(valist, num);
/* access all the arguments assigned to valist */
for (i = 0; i < num; i++) {
printf("%02x,",va_arg(valist, int));
}
/* clean memory reserved for valist */
va_end(valist);
}
int main() {
printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
foo(3,'a','b','c');
printf("\n");
bar('a','b','c');
}
Result as following:
Compiling the source code....
$gcc main.c -o demo -lm -pthread -lgmp -lreadline 2>&1
Executing the program....
$demo
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000
62,63,400aab,
62,63,00,0a,c0a4700,4009e0,b0ef50a,01,64f5ead8,40000,400934,00,440c7120,400540,64f5ead0,00,00,83cc7120,be487120,00,00,00,bea8d73,be96d10,b6b9950,00,00,00,400540,64f5ead0,40056a,64f5eac8,c0c0180,01,64f5f3d3,00,64f5f3d8,64f5f473,64f5f48d,64f5f4a9,64f5f4b2,64f5f4c8,64f5f4e5,64f5f50d,64f5f796,64f5f7af,64f5f7d9,64f5f7f8,64f5f802,64f5f80a,64f5f823,64f5f83b,64f5f850,64f5f876,64f5f87e,64f5f898,64f5f8d0,64f5f8db,64f5f8e3,64f5f946,64f5f972,64f5f998,64f5fa2d,64f5fa63,64f5fa79,64f5ff2f,64f5ffc9,00,21,64fdb000,10,bfebfbff,06,1000,11,64,03,400040,04,38,05,09,07,be98000,08,00,09,400540,0b,30,0c,30,0d,30,0e,30,17,
Everything was the same with the int version, but why result is different?
Replace:
va_start(valist, num);
with
va_start(valist, arg_count);
in foo and change its prototype to:
void foo(unsigned char arg_count, ...)
You're declaring 3 arguments but starting the va_list 2 from the end.
Also, change this
bar('a','b','c');
to:
bar(3, 'a','b','c');
in main. You have omitted the num argument.
Maybe I don't understand what it is that you are trying to do with your code, but aren't you just sending 'a', which typically will be the character code 97, as the number of arguments to the function bar? So it tries to print 97 arguments.
va_start(valist, num) initializes valist to start with the argument after num. In both function calls, to foo and bar, 'a' is in the position of num, so the first value from va_arg will be 'b', which is 98 decimal, or 62 hexadecimal.
Related
I'm trying to compare the bits of variable number of arguments and print their value. but I'm getting garbage. what am I doing wrong?
My Code:
#include <stdio.h>
#include <stdarg.h>
void and_bits(unsigned int num, ...)
{
unsigned int i = 0;
/* result is 11111111 in binary*/
unsigned int result = 255;
va_list arglist;
va_start(arglist, num);
/* this loop takes each argument, and apply AND bitwise operator to the next argument*/
for (i = 0; i < num; i++)
{
/* bitwise AND operator */
result &= va_arg(arglist,unsigned int);
printf("%u, ",va_arg(arglist,unsigned int));
}
va_end(arglist);
printf("\nthe return value: base 10 = %u\t", result);
printf("base 16 = %x\n", result);
return;
}
int main(void)
{
and_bits(7,2,3);
return 0;
}
The Output:
3, 0, 0, 1987831328, 0, 3742925664, 3742924928,
the return value: base 10 = 0 base 16 = 0
So, you have two problems. The first is covered in comments, but essentially you're not calling and_bits correct. The first argument should be a count.
But you have a bigger problem, right here:
for (i = 0; i < num; i++)
{
/* bitwise AND operator */
result &= va_arg(arglist,unsigned int);
printf("%u, ",va_arg(arglist,unsigned int));
}
Every time you call va_arg(), you advance the argument pointer by one. So assuming you've called and_bits(3, 7, 2, 3), then on the first iteration of the loop:
You call va_arg(), which returns the value 7, and you set result to result & 7.
Then on the next line, you call va_arg(), which returns 2, so you print that out.
Then in the next iteration of the loop:
You call va_arg(), which returns 3, and set result to result & 3.
You call va_arg() again, but you've already consumed three arguments so now you're reading beyond the argument list and you get undefined results.
You're going to go through the loop one more time, and at this point all values returned by va_arg() will be invalid.
You should modify the loop so that it only calls va_arg() once:
#include <stdio.h>
#include <stdarg.h>
void and_bits(unsigned int num, ...)
{
unsigned int i = 0;
/* result is 11111111 in binary*/
unsigned int result = 255;
va_list arglist;
va_start(arglist, num);
/* this loop takes each argument, and apply AND bitwise operator to the next argument*/
for (i = 0; i < num; i++)
{
/* bitwise AND operator */
unsigned int val = va_arg(arglist,unsigned int);
result &= val;
printf("%u: val=%u, result=%u\n", i, val, result);
}
va_end(arglist);
printf("\nthe return value: base 10 = %u\t", result);
printf("base 16 = %x\n", result);
return;
}
int main(void)
{
and_bits(3, 7,2,3);
return 0;
}
Which produces as output:
0: val=7, result=7
1: val=2, result=2
2: val=3, result=2
the return value: base 10 = 2 base 16 = 2
I would like to make a function that can take a variable number of parameters at once to do something like this:
void function(char *args[]){ ... }
int main(){
function("a","b","c");
}
You are looking for Variadic functions.
Check this example:
#include <stdio.h>
#include <stdarg.h>
double average(int num, ...) {
va_list valist;
double sum = 0.0;
int i;
/* initialize valist for num number of arguments */
va_start(valist, num);
/* access all the arguments assigned to valist */
for (i = 0; i < num; i++) {
sum += va_arg(valist, int);
}
/* clean memory reserved for valist */
va_end(valist);
return sum/num;
}
int main(void) {
printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2, 3, 4, 5));
printf("Average of 5, 10, 15 = %f\n", average(3, 5, 10, 15));
}
Output:
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000
Read more in Variadic Functions in C and the Reference.
Tip: Think twice before using Variadic functions. As #user694733 commented, they have no compile time type safety or protection against invalid number of arguments. They can usually be avoided with better program design.
I am trying to make a variadic function in C with stdarg.h, and I followed this tutorial and wrote the code exactly as he did: https://www.youtube.com/watch?v=S-ak715zIIE. I included the output in the code. I cannot figure out why the first argument is not printed, and also, why are there zeros printed at the end? I am beyond confused. Thanks!
#include <stdio.h>
#include <stdarg.h>
void printNums(int num, ...) {
va_list args;
va_start(args, num);
for (int i = 0; i < num; i++) {
int value = va_arg(args, int);
printf("%d: %d\n", i, value);
}
va_end(args);
}
int main() {
printNums(5, 2, 3, 4);
return 0;
}
/*
Output:
0: 2
1: 3
2: 4
3: 0
4: 0
*/
va_start's first argument is the last parameter that isn't variadic. So num holds the 5, and the rest hold the variadics:
#include <stdio.h>
#include <stdarg.h>
void printNums(int num, ...) {
va_list args;
va_start(args, num);
printf("%d: %d\n", 0, num);
for (int i = 1; i <= num; i++) {
int value = va_arg(args, int);
printf("%d: %d\n", i, value);
}
va_end(args);
}
int main() {
printNums(5, 2, 3, 4);
return 0;
}
0: 5
1: 2
2: 3
3: 4
4: 0
5: 0
also, why are there zeros printed at the end? I am beyond confused. Thanks!
Because of this line:
for (int i = 1; i <= num; i++) {
You pass the value 5 as num to printNums(). In the for loop you act as though it describes the number of variadic arguments to read, but it doesn't - you passed 3 variadics, not 5. The last 2 calls to va_start therefore yield undefined behavior, since you've read past the end of valid variadic arguments. It's just mere chance that you happen to get 0 here - it could be some other random value.
Note that there is no way with mere variadic macros to know how many arguments were passed. Nor is there a way to assert their type. You can assume their type and specify their length at runtime if you wish:
$ ./t3
0: 5
1: 2
2: 3
3: 4
#include <stdio.h>
#include <stdarg.h>
void printNums(int num, ...) {
va_list args;
va_start(args, num);
for (int i = 0; i < num; i++) {
int value = va_arg(args, int);
printf("%d: %d\n", i, value);
}
va_end(args);
}
int main() {
printNums(4, 5, 2, 3, 4);
return 0;
}
Variadic functions are primarily valuable when writing functions like printf, where unknown types and quantities of arguments are required (see the example from the man page) Using passing a list of known types would be more conveniently accomplished by passing an array and count int:
$ cat t.c
#include <stdio.h>
void printNums(int count, int* nums) {
for (int i = 0; i < count; i++) {
printf("%d: %d\n", i, nums[i]);
}
}
int main() {
int nums[] = {5,2,3,4};
printNums(4, nums);
return 0;
}
that just doesn't make a very good video about variadics :P
Whenever we use variable argument function in C language, we have to provide the total number of arguments as the first parameter. Is there any way in which we can make a function with variable arguments without giving the total number of arguments?
[update from comment:]
I want to use functions like sum(1,2,3) should return 6. i.e, no counter should be there.
Several ways:
pass simple, explicit count (which you don't want in this question)
pass some format string, similar to printf and scanf
pass some "mode" parameter, and have each mode require specific varargs
have all varargs to be of same type, and require last argument to be some special value, AKA sentinel value, such as NULL for pointer list or max/min value of the type for integer types, or NaN for doubles.
However you do it, you have to have some way for the function to know the types of the varargs, as well as a way for it to know when they end. There is no built-in way in C, argument count is not passed to the function.
I want to use functions like sum(1,2,3) should return 6. i.e, no counter should be there
You could define a sentinel. In this case 0 might make sense.
/* Sums up as many int as required.
Stops adding when seeing the 1st 0. */
int sum(int i, ...)
{
int s = i;
if (s)
{
va_list ap;
va_start(ap, i);
/* Pull the next int from the parameter list and if it is
equal 0 leave the while-loop: */
while ((i = va_arg(ap, int)))
{
s += i;
}
va_end(ap);
}
return s;
}
Call it like this:
int sum(int i, ...);
int main(void)
{
int s = sum(0); /* Gives 0. */
s = sum(1, 2, 3, 0); /* Gives 6. */
s = sum(-2, -1, 1, 2, 0); /* Gives 0. */
s = sum(1, 2, 3, 0, 4, 5, 6); /* Gives 6. */
s = sum(42); /* Gives undefined behaviour! */
}
The sum() function alternatively could also look like this (but would do one useless addition of 0):
/* Sums up as many int as required.
Stops adding when seeing the 1st 0. */
int sum(int i, ...)
{
int s = i;
if (s)
{
va_list ap;
va_start(ap, i);
/* Pull the next int from the parameter list and if it is
equal 0 leave the do-loop: */
do
{
i = va_arg(ap, int);
s += i;
} while (i);
va_end(ap);
}
return s;
}
You don't have to supply that number of arguments. For instance, consider the signature for printf:
int printf( const char* format, ... );
It "finds out" how many arguments it needs by parsing the string you give it. Of course, your function needs to know the amount of arguments in some way, otherwise what sense does it make for it to take a variable number of arguments?
It is possible to create a variadic function which takes a count as the first argument, then use variadic macros to add the count value automatically:
#include <stdarg.h>
#define count_inner(a1, a2, a3, a4, a5, num, ...) (num)
#define count(...) count_inner(__VA_ARGS__, 5, 4, 3, 2, 1)
#define sum(...) sum_func(count(__VA_ARGS__), __VA_ARGS__)
int sum_func(int count, ...)
{
va_list ap;
va_start(ap, count);
int total = 0;
while(count--)
total += va_arg(ap, int);
va_end(ap);
return total;
}
Using the sum macro instead of sum_func allows the count to be omitted, provided there are between 1 and 5 arguments. More arguments/numbers can be added to the count_inner/count macros as required.
int main(void)
{
printf("%d\n", sum(1));
printf("%d\n", sum(1, 2));
printf("%d\n", sum(1, 2, 3));
printf("%d\n", sum(1, 2, 3, 4));
}
Output:
1
3
6
10
Well, the problem is that you have to somewhat indicate to the function that your argument list is exhausted. You've got a method from printf(3) which is that you can express the order and the type of arguments in your first parameter (forced to be a string arg) you can express it in the first parameter, or, for the adding, as the value 0 doesn't actually add to the sum, you can use that value (or some other at your criteria) to signal the last parameter. For example:
int sum(int a0, ...)
{
int retval = a0;
va_list p;
va_start(p, a0);
int nxt;
while ((nxt = va_arg(p, int)) != 0) {
retval += nxt;
}
return retval;
}
This way, you don't have to put the number of arguments as the first parameter, you can simply:
total = sum(1,2,3,4,5,6,7,8,9,10,0);
But in this case you have to be careful, that you never have a middle parameter equal to zero. Or also you can use references, you add while your reference is not NULL, as in:
int sum(int *a0, ...)
{
int retval = *a0;
va_list p;
va_start(p, a0);
int *nxt;
while ((nxt = va_arg(p, int*)) != NULL) {
retval += *nxt;
}
return retval;
}
and you can have:
int a, b, c, d, e, f;
...
int total = sum(&a, &b, &c, &d, &e, &f, NULL);
Can we pass arguments of different datatypes to same variadic function at the same time?
sure, look at common usages of printf:
printf("Error %d: %s", errNum, errTxt);
so ross$ expand < variadic.c && cc -Wall -Wextra variadic.c
#include <stdio.h>
#include <stdarg.h>
void f(int, ...);
struct x { int a, b; } y = { 5, 6 };
int main(void) {
float q = 9.4;
f(0, 1.234, &q, "how now", 123, &y);
return 0;
}
void f(int nothing, ...) {
va_list ap;
va_start(ap, nothing);
double f = va_arg(ap, double);
float *f2 = va_arg(ap, float *);
char *s = va_arg(ap, char *);
int i = va_arg(ap, int);
struct x *sx = va_arg(ap, struct x *);
va_end(ap);
printf("%5.3f %3.1f %s %d %d/%d\n", f, *f2, s, i, sx->a, sx->b);
}
so ross$ ./a.out
1.234 9.4 how now 123 5/6
Here's a printf-free example ( old version: http://codepad.org/vnjFj7Uh )
#include <stdarg.h>
#include <stdio.h>
/* return the maximum of n values. if n < 1 returns 0 */
/* lying to the compiler is not supported in this version, eg: **
** va_max(4, 8, 8, 8, 8, 8, 8, 8, 8) **
** or **
** va_max(4, 2, 2) **
/* is a bad way to call the function (and invokes Undefined Behaviour) */
int va_max(int n, ...) {
int res;
va_list arg;
if (n < 1) return 0;
va_start(arg, n);
n--;
res = va_arg(arg, int);
while (n--) {
int cur = va_arg(arg, int);
if (cur > res) res = cur;
}
return res;
}
int main(void) {
int test6 = va_max(6, 1, 2, 3, 4, 5, 6);
int test3 = va_max(3, 56, 34, 12);
if (test6 == 6) puts("6");
if (test3 == 56) puts("56");
return 0;
}
I made a function to unpack binary data using a varadic function,it takes different types based on
what you want "encoded/decoded".
You'd use it like:
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t *buf = ....;
depickle(buf,"sis",&a,&b,&c);
where 's' expects an uint16_t* and decodes 2 bytes from buf into a as little endian, 'i' says to decode 4 bytes as little endian into 'b'
Or to e.g. decode 4 bytes as big endian into a uint32_t:
uint32_t a;
uint8_t buf[] = {0x12,0x34,0x56,0x78};
depickle(buf,"I",&a);
There's still an early version around here.