在递归函数中打印函数参数

时间:2014-11-17 07:17:43

标签: c recursion stack stack-trace

我需要从另一个函数打印递归函数函数的参数值。以下是功能。

void printarg()
{
  // Print the values of a, b, c, countval (function arguments to func1) 
   int j, nptrs;
   void *buffer[100];
   char **strings;

   nptrs = backtrace(buffer, 100); 

   strings = backtrace_symbols(buffer, nptrs);
   if (strings == NULL) {
   perror("backtrace_symbols");
   exit(EXIT_FAILURE);
   }

   for (j = 0; j < nptrs; j++) {
      printf(" a %s\n", strings[j]);
   }

   free(strings);

}
int func1(int a, short b, char c, int count)
{
int x, y, z;  
if (!count) {
  printarg();
  return a; 
}
a = a*3; 
b = b -1 ;
c = c^1;
count--;
return func1(a, b, c, count);
} 

int main()
{
   func1(1,2,5,3);
}

我可以使用bactrace实用程序获取堆栈帧。但我不确定如何从函数printargs中的堆栈帧打印a,b,c的值。任何人都可以帮助我。

1 个答案:

答案 0 :(得分:0)

放弃希望所有进入这里的人!

以下是获取所需信息的两种方法。两者都不是便携式的,我不能保证它们会在您的机器上工作(他们在我的工作)。我提供了main调用foo的示例,它是递归的,并在递归完成时调用bar

两者都依赖于这样布局的堆栈(正如CDECL调用约定所规定的那样):

local data in `bar`
`ebp` of foo on nth call
return address in `foo` on nth call
...
`ebp` of foo on (n-1)th call
return address in `foo` on (n-1)th call
first argument to `foo` on (n-1)th call
2nd argument
3rd argument
...

如果你不遵循这个,它将支付你去搜索“调用约定”并看看函数调用如何在汇编中工作。

在示例中,我的所有参数都是整数,即4个字节。如果你想使用不同类型的参数,他们可能占用堆栈上不同数量的空间,因此你必须改变代码。

第一种方式使用内联汇编,因此它是特定于x86的(这是一件坏事)。您无需知道bar中本地数据占用多少空间,因为您直接抓取ebp(这是一件好事)。

#include <stdio.h>

void bar() {
    int * ebp;
    // inline assembler to get base pointer value (gives a location
    // on the stack where the caller's (foo's) base pointer can be found)
    __asm__("movl %%ebp, %[ebp]" : [ebp] "=r" (ebp));
    // go back to base pointer of caller (final foo)
    ebp = (int *)*ebp;
    // skip over the address of the base pointer of the previous caller
    // and the return address
    ebp += 2;
    // here are the arguments to the call of foo that terminated the 
    // recursion
    printf("a=%d , b=%d, c=%d \n", *ebp, *(ebp+1), *(ebp+2));
}   

void foo(int a, int b, int c) {
    if (a) {
        foo(--a, ++b, c);
    } else {
        bar();
    }
}

int main(int argc, void ** argv) {
    foo(1, 2, 3);
}

使用gcc -g filename.c -o filename进行编译。

第二种方法使用堆栈上的局部变量的地址来计算堆栈在内存中的位置,然后返回到调用者的基本指针。它不使用内联汇编,因此它可能在x86以外的平台上运行。但它需要知道编译器如何将局部变量放在bar的堆栈中,所以它实际上可移植(我不得不查看汇编文件来计算我的{{} 1}}变量在堆栈中比基址指针高16个字节。

x

正如我在评论中指出的那样,C语言不希望你这样做。它旨在在void bar() { // The variable we will take the address of unsigned int x = 0x999; // Get the address of x (it is on the stack, because it is a local variable) int *xadd = &x; // Get ebp of caller; the magic number is 4 32-bit words or 16 bytes. // On my system gcc puts 0x999 at 16 bytes past ebp, // the address of 0x999 at 12 bytes past ebp, // and junk at 4 and 8 bytes past ebp. xadd += 0x4; xadd = (int *)*xadd; // go back to ebp of caller xadd += 2; // jump past ebp of previous caller and return address printf("a=%d , b=%d, c=%d \n", *xadd, *(xadd+1), *(xadd+2)); } // foo and main as above 中提供abc本地范围。我认为你被要求这样做是一种残忍和不寻常的惩罚(虽然我承认它可能有助于你理解堆栈布局)。