-2

I have a tough question for you, that I have been playing around with today, but for which I could not yet come up with an elegant solution.

We know, that functions of the form void func() take infinitely many arguments. Now I have put together a very simple minimal, working code snippet:

#include <stdio.h>

int func()
{

    printf("%d, %d, %d, %d\n");
    return 0;
}

int func() {
    sucks(0 /* Offset */, 1, 2, 3, 4);
}

Alright, we can now call func() with as many arguments as we want. The questions that I am experimenting with atm is: how can we properly access the content of those arguments? The printf() function prints out something like this ... just to verify to ourselves that the arguments are actually there:

anonymous@melina:/tmp$ ./a.out 
1 2 3 4

So now the question: The above snippet is a bit hackish. Is there a proper way to access these arguments? Or do you have to actually mess around with the stack pointer and inline assembly? I have, as a first try, thought about getting the stack pointer at the beginning of the function, e.g.,

uint64_t sp;
asm( "mov %%rsp, %0" : "=rm" ( sp ));

... and somehow use it, to guess where (in memory) those arguments actually are. However ... I have had no success so far.

Skriptkiddie
  • 411
  • 2
  • 7
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/184551/discussion-on-question-by-skriptkiddie-functions-with-infinitely-many-unnamed). – Samuel Liew Dec 01 '18 at 09:48
  • 2
    [A certain standard](http://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html) does not mention stack-or-heap for `malloc`. I think you're down $100. – Jongware Dec 01 '18 at 09:52
  • 1
    The C standard does not mention stack or heap in ***any*** context. – Weather Vane Dec 01 '18 at 09:55
  • Note that the argument list is not 'infinite'; it is more undefined or unspecified or undeclared (that's probably a better term) — the compiler cannot check any call against a prototype because there is no prototype. The number of arguments is 'indefinite' but still finite. And code that calls the function with the incorrect number or types of arguments invokes undefined behaviour, but without a prototype, the compiler cannot help protect you from yourself. – Jonathan Leffler Dec 01 '18 at 16:08

2 Answers2

6

Is there a proper way to access these arguments?

Yes. Specify a parameter list in the function definition with types and identifiers of the function.

You can do it "old style" (don't do it, it should not be used in new code):

int func(a, b, c, d)
    int a; 
    int b;
    int c;
    int d;
{
   printf("%d %d %d %d\n", a, b, c, d);
}

Or normal:

int func(int a, int b, int c, int d) {
   printf("%d %d %d %d\n", a, b, c, d);
}

or using stdarg.h :

int func(int a, ...) {
   va_list ap; 
   va_start(va, a); 
   int b = va_arg(va, int);
   int c = va_arg(va, int);
   int d = va_arg(va, int);
   printf("%d %d %d %d\n", a, b, c, d);
   va_end(ap);
}

or using stdarg.h from the second arg:

int func(int a, int b, ...) {
   va_list ap; 
   va_start(va, b); 
   int c = va_arg(va, int);
   int d = va_arg(va, int);
   printf("%d %d %d %d\n", a, b, c, d);
   va_end(ap);
}

or using stdarg.h from the third arg:

int func(int a, int b, int c, ...) {
   va_list ap; 
   va_start(va, c); 
   int d = va_arg(va, int);
   printf("%d %d %d %d\n", a, b, c, d);
   va_end(ap);
}

stdarg.h needs at least one, first argument in the parameter list to be declared. So there is no way to handle function with unspecified number and type of arguments, but there is a way (stdarg.h) to handle function with at least 1 argument followed by unspecified number and type of arguments.

... functions of the form void func() take infinitely many arguments

This is not true. I don't think there is a C standard way to pass infinite number of arguments to a function.

Function of the form func() take unspecified number and type of arguments. They take a finite number of arguments, but the number is unspecified in the transaction unit where such function declaration is encountered. If the function definition takes 5 arguments, they take 5 arguments, otherwise undefined behavior. The number of arguments is not specified, if the function definition takes elipsis (, ...) in which case no information about the number or types of the parameters after the comma is supplied. (C11 6.7.6.3p9).

Or do you have to actually mess around with the stack pointer and inline assembly?

Yes, you have to "mess around" with implementation defined behavior.

From GCC docs 18.10:

... The ISO implementation of va_start takes an additional second argument. The user is supposed to write the last named argument of the function here.

However, va_start should not use this argument.

You need to specify the second argument to va_start. Maybe more on such topic can be found in this thread.

printf("%d %d %d %d\n");

This is undefined behavior. Once your program has undefined behavior, nasal demons start spawning in your room and are fighting for independence. It may have well defined behavior on your platform or implementation, but this is wrong, it's no longer a C language. If there is a "correct" behavior according to your mental model of the language, that model is simply wrong; from this thread. From the language point of view, nasal demons fight for independence and the program is invalid.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 1
    Don’t suggest K&R style. That should never be used in new code. – Jonathan Leffler Dec 01 '18 at 09:41
  • @JonathanLeffler: it is still valid C, although marked "obsolescent" in 6.11.7 in ISO 9899:1999; there may be a newer spec that marks it "invalid", though. (That was just the first one that popped up in a search.) Kamil Cuk is right to list it. (I must be careful referring to 9899:1999, though, as the 6.11.6 right above it explicitly mentions "[t]he use of function declarators with empty parentheses" ... Also as "obsolescent" but still: if I regard 6.11.7 valid, than so should 6.11.6, and the reverse.) – Jongware Dec 01 '18 at 15:47
  • 1
    I didn't say it is 'invalid'. I said you should not suggest using it. Especially, you should not suggest using it first and without cautions and qualifications (which have been added since I wrote my previous comments). It is still 'standard C', but it is not something that people learning C should be in the slightest encouraged to write. As a dismissive throwaway comment at the end of the answer, for completeness sake, it would be OK. An answer without the topic would be OK too — not quite as complete, at one level, but for people learning C in the 21st Century, it isn't important to know. – Jonathan Leffler Dec 01 '18 at 15:56
1

[...] functions of the form void func() take infinitely many arguments.

Nope.

Functions used without a prototype must be called according to their definition.

void foo(); // not a prototype
int main(void) {
    // foo() must be called according to its definition
    foo(12, 4, "bar", -42); // error (undetected by compiler)
    foo("---", 12, 4, "bar", -42); // no error
}
void foo(const char *msg, int n, ...) { /* ... */ }

Note that pre-C89 function definitions had a different way of defining arguments. Desire for backwards compatability still allows for this syntax. I believe the same restrictions apply as above

void bar() /* pre-C89 syntax */
const char *msg;
int n;
{ /* ... */ }
pmg
  • 106,608
  • 13
  • 126
  • 198