Related
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);
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.
This question already has answers here:
Couldn't implement function with variable arguments
(1 answer)
Function with unknown number of parameters in C
(1 answer)
Closed 5 years ago.
I have learned that this code will print 10, 30, 60 in Terminal.
#include <stdio.h>
void add(int num, ...);
int main(int argc, char const *argv[])
{
int a=10, b=20, c=30;
add(1, a);
add(2, a, b);
add(3, a, b, c);
return 0;
}
void add(int num, ...)
{
int* p = NULL;
p = &num + 1;
if (num == 1)
printf("%d \n", p[0]);
else if (num == 2)
printf("%d \n", p[0] + p[1]);
else
printf("%d \n", p[0] + p[1] + p[2]);
}
But, it only print odd numbers... :(
I just want to print 10, 30, 60 within .
Where do you think I should fix?
You can't get variadic arguments just by taking the address of the last given parameter and adding to it. How function arguments are laid out on the stack (if a stack is used) is compiler and system dependent. That's why you're getting strange numbers.
The way to do this portably is to use a va_list as follows:
void add(int num, ...)
{
// the va_list used to retrieve the extra arguments
va_list args;
int i, sum = 0;
// use va_start to start processing arguments, passing in the last explicit argument
va_start(args, num);
for (i=0; i<num; i++) {
// extract the next argument with the given type
sum += va_arg(args, int);
}
// cleanup
va_end(args);
printf("%d \n", sum);
}
For more details, see the stdarg man page.
I have this exercise to make a transpose of a matrix in C. I made a function to check for type n*n but when I'm trying to ask the user for the matrix I don't know how I should declare the array. And I'm getting this compile error "type of formal parameter 1 is incomplete" in the function on the [n2] part.
The parameters of the functions for multi dimensional arrays shouldn't be like this -> int matrix[][n2]. or is cause i'm using a variable and not a constant or a pre defined size. ?
#include <stdio.h>
#define prompt "Dimenção da matriz (nxn) >>"
#define prompt_1 "Introduza os valores : "
void getType( int *n1, int *n2 );
void getMatrix( int matrix[][n2], int lim1, int lim2);
//void trans(int matrix[][n2]);
int main(int argc, char const *argv[]) {
int n1, n2;
getType(&n1, &n2);
int matrix[n1][n2];
//printf("%dx%d\n", n1, n2);
getMatrix(matrix, n1, n2);
//trans(matrix);
return 0;
}
void getType(int *n1, int *n2){
printf("%s", prompt );
scanf("%dx%d", &(*n1), &(*n2));
}
void getMatrix( int matrix[][n2], int lim1, int lim2){
printf("%s\n", prompt_1 );
for(int line = 0; line < lim1; line++ ){
for(int column = 0; column < lim2; column++){
printf("Linha %d coluna %d ->", line, column );
scanf("%d", &matrix[line][column]);
}
}
}
The signature should be:
void getMatrix( int lim1, int lim2, int matrix[lim1][lim2] )
You are allowed omit the lim1 inside square brackets but it is good documentation to include it.
The main point is that the variable inside the square brackets must either be a parameter from earlier in the parameter list, or some other variable in scope (which can only be a global variable, but that's usually a bad idea).
Also it would be good to check scanf return value otherwise you may create matrix with garbage dimension.
I have this homework to do: "Determine the minimum of 10 precision double numbers from a string (implicit values or from the KB) using a function with a variable number of parameters. The first 7 values will be considered initially, next the last 3 and at the end these 2 values." Well I made it all but I don't know why it gives me some strange results. Here's the code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <conio.h>
double min(double,...);
void main(){
double a,b,c;
printf("Introduceti numerele: ");
scanf("%lf%lf%lf",&a,&b,&c);
printf("\nMinimul este %lf",min(10,1.34,4.34,7,5.23,6.23,2,8.232,a,b,c));
_getch();
}
double min(double x,...){
int i;
double y;
va_list ap;
va_start(ap,x);
y=va_arg(ap,double);
for(i=0;i<x;i++){
if(y>va_arg(ap,double))
y=va_arg(ap,double);
}
va_end(ap);
return y;
}
Also i don't know why the compiler knows about what argument is next cause i is not used in va_arg(ap,double).
for(i=0;i<x;i++){
if(y>va_arg(ap,double))
y=va_arg(ap,double);
The first parameter in the call to your function min is the number of arguments, and it has the type int:
#include <stdarg.h>
double min( int numberOfArgs, ... )
// ^^^
{
va_list argptr;
va_start( argptr, numberOfArgs ); // initialize argument pointer
double minData = va_arg( argptr, double ); // initialize the minimum with the first argument
// and increment argument pointer
for ( int i = 1; i < numberOfArgs; i ++ ) // for all of the following arguments
{
double data = va_arg( argptr, double ); // get argument and increment argument pointer
if ( data < minData ) // test if argument is less than mnimum
minData = data;
}
va_end( argptr );
return minData;
}
Ensure that all of your arguments in the argument list are floating point values of type double:
int main()
{
double a, b, c;
printf("Introduceti numerele: ");
scanf_s("%lf%lf%lf", &a, &b, &c);
double minVal = min( 10, 1.34, 4.34, 7.0, 5.23, 6.23, 2.0, 8.232, a, b, c)
// ^^ ^^
printf("\nMinimul este %lf", minVal);
return 0;
}