5

I'm playing with the stack and function's call parameters. What I want to achieve here is to get the value of variable parameters directly using the stack.

It works (or seems to work) fine when I don't use variable parameters. Here is what is working:

void test(int a, int b)
{
  unsigned char *ptr;
  int i;

  ptr = (unsigned char*)&a;
  for (i = 0; i < 4; i++)
    {
      printf("%d,", *ptr);
    }    
}

That works, I can retrieve the value of b; The same code using

void test(int a, ...);

as function's prototype doesn't work.

I cant understand what's going on here.

Can you help me? Thanks !

Edit: Ok, then it seeems there is no stable and reliable way to do that kind of stuff on my own. Lets say that in the callee function I know the data size (but not the type) of variable argument, is there a way to grab them ?

7
  • 4
    What you're doing is Undefined Behavior. What are you trying to achieve? Commented Aug 23, 2011 at 20:34
  • 1
    How are you attempting to get the variable arguments? Tip - stdargs.h , va_start, va_arg, va_end. Commented Aug 23, 2011 at 20:35
  • @jweyric: I'm using gcc. I know i could use va_* macro, but that is purely expiremental, i'm just trying stuff to learn how it works :P Commented Aug 23, 2011 at 20:41
  • 6
    @Xaqq: Be aware the stack layout is not guaranteed to be the same across different compilers, or even with different compiling flags & optimisations. Commented Aug 23, 2011 at 20:41
  • 3
    Be even aware that there might be no stack at all. A stack is just one possible implementation of a function call convention. E.g on some architecture any reasonable number of arguments might be held in registers. Commented Aug 23, 2011 at 21:09

2 Answers 2

2

As long as you know or can determine the number of arguments, you can use the macros from <stdarg.h>:

#include <stdio.h>
#include <stdarg.h>

void test1(int n, ...)
{
    va_list args;
    va_start(args, n);
    for (int i = 0; i < n; i++)
    {
        int j = va_arg(args, int);
        printf("%d: %d\n", i, j);
    }
    va_end(args);
}

void test2(int a, ...)
{
    va_list args;
    int i = 0;
    printf("%d: %d\n", i++, a);
    va_start(args, a);
    int j;
    while ((j = va_arg(args, int)) > 0)
        printf("%d: %d\n", i++, j);
    va_end(args);
}

The difference is in how these two functions are called:

int main(void)
{
    test1(4, 1, 3, 7, 9);
    test2(1, 3, 7, 9, 0);
    return(0);
}

The printf() family uses an alternative but equivalent technique; those functions scan the format string and determine the type of each argument (as well as the number of arguments) from the information in the format string. So, your main options are:

  • count - test1()
  • sentinel - test2()
  • format string - printf()
Sign up to request clarification or add additional context in comments.

6 Comments

The problem with your answer is that you're assuming that argument are of type int. In my case knowing the type of my argument are not an option.I know the the numbers and the size of them. How can I treat them as raw binary data ? Is there a way to do something like "data = va_arg(ap, get_size_from_somewhere()); That would be very usefull because I would be able to store those data as they are.
Idea: I could construct my raw data using int, looping with va_arg() for my_data_size / 4 + 1 iterations. But then, what about memory alignment or argument having a size of 1 or 2 bytes?
If you don't know, or cannot deduce, the type of the variables on the stack, you're stuck. You can't declare a local variable to hold the parameters, all else apart. If you know the types, you can do it; I assumed a homogeneous list of integers because it was convenient. However you do it, the calling code must provide sufficient information to the called function that the called function knows the number and type (and indirectly the size) or each parameter represented by the ellipsis ('...'). I have no idea what you mean by 'raw binary data'; treating them as uniformly int does that.
Alternatively, if you're really dealing with an array of bytes, pass the length of the array and a pointer to the start - but that does not require a variable number of arguments.
Well, let's say that i have struct my_struct test. That type is not know by the callee code at compile time. I would like to be able to store it in my calle(sizeof(my_struct), test) function. Problem is calle must be able receive any type of parameter (thas is why I use ...), for exemple I must be able to do calle(sizeof(int), 4) or calle(sizeof(char), 'a'). calle function will store those data in a unsigned char array, for later use.
|
1

In functions with ... you can use va_* macro

void test(int a, ...) {
  va_list ap;
  va_start(ap, a);
  // Your code
  va_end(ap);    
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.