c语言复制函数指针,c语言:基于函数指针的两个示例分析

第一个:------------------------------------------------------

#include

#include

void tell_me(int f(const char *, const char *));

int main(void)

{

tell_me(strcmp);

tell_me(main);

return 0;

}

void tell_me(int f(const char *, const char *))

{

if (f == strcmp) /*

printf("Address of strcmp(): %p\n", f);

else

printf("Function address: %p\n", f);

}

--------------------------------------------------------------

其中我不理解的是,这个程序表达的应该是说f是一个指向函数的指针,判断的时候是判断f是否指向函数strcmp,如果是的话,就输出strcmp这个函数的地址.如果不是,就输出main函数的地址

因为函数名可以作为指针,所以if (f == strcmp)应该是说判断2个指针的地址是否相同对吧?

我用gdb 断点到此,print f和printfstrcmp得到的是不同的地址啊,并且可以发现f和*f的内容居然一样,strcmp和*strcmp也一样,请问是什么原因,如何解释?

(gdb) print f

$1 = (int (*)(const char *, const char *)) 0x8048310

(gdb) print strcmp

$2 = {} 0xb7e59d20

(gdb) n

16 printf("Address of strcmp(): %p\n", f);

(gdb) print strcmp

$3 = {} 0xb7e59d20

(gdb) print *strcmp

$4 = {} 0xb7e59d20

(gdb) print *f

$5 = {int (const char *, const char *)} 0x8048310

(gdb) n

Address of strcmp(): 0x8048310

19 }

(gdb) n

后来我查到plt是指的过程链接表,是不是说只有在执行到f == strcmp时候,才把f的地址和strcmp的位置指向同一位置?

后来别人通过反汇编发现的情况:==============================================

如下红色的几行,main与strcmp此时为常量(你也会发现没有.data段),在汇编代码中他是把这两个常量写入函数堆栈,然后调用函数,作出对比,然后输出。而你所说的 f ,也就是函数参数,实际上它只作为预分配的参考(汇编代码中,你是找不到 f 的)。

-------------------------------------------------------------------------------------

.file "1.c"

.text

.globl main

.type main, @function

main:

leal 4(%esp), %ecx

andl $-16, %esp

pushl -4(%ecx)

pushl %ebp

movl %esp, %ebp

pushl %ecx

subl $4, %esp

movl $strcmp, (%esp)

call tell_me

movl $main, %eax

movl %eax, (%esp)

call tell_me

movl $0, %eax

addl $4, %esp

popl %ecx

popl %ebp

leal -4(%ecx), %esp

ret

.size main, .-main

.section .rodata

.LC0:

.string "Address of strcmp(): %p\n"

.LC1:

.string "Function address: %p\n"

.text

.globl tell_me

.type tell_me, @function

tell_me:

pushl %ebp

movl %esp, %ebp

subl $8, %esp

cmpl $strcmp, 8(%ebp)

jne .L4

movl 8(%ebp), %eax

movl %eax, 4(%esp)

movl $.LC0, (%esp)

call printf

jmp .L6

.L4:

movl 8(%ebp), %eax

movl %eax, 4(%esp)

movl $.LC1, (%esp)

call printf

.L6:

leave

ret

.size tell_me, .-tell_me

.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"

.section .note.GNU-stack,"",@progbits

==================================================

00401090 push ebp //第一题的反汇编

00401091 mov ebp,esp

00401093 sub esp,40h

00401096 push ebx

00401097 push esi

00401098 push edi

00401099 lea edi,[ebp-40h]

0040109C mov ecx,10h

004010A1 mov eax,0CCCCCCCCh //应该说在函数传递时,f与strcmp的地址都相同

004010A6 rep stos dword ptr [edi]

13: printf("%0x\t%0x\n",f,strcmp); //看这里,输出f与strcmp的地址是相同的

004010A8 push offset strcmp (004011a0)

004010AD mov eax,dword ptr [ebp+8]

004010B0 push eax

004010B1 push offset string "%0x\t%0x\n" (0042201c)

004010B6 call printf (00401120)

004010BB add esp,0Ch

14: if (f == strcmp) /*

004010BE cmp dword ptr [ebp+8],offset strcmp (004011a0)

004010C5 jne tell_me+4Ah (004010da)

15: printf("Address of strcmp(): %0x\n", f);

004010C7 mov ecx,dword ptr [ebp+8]

004010CA push ecx

004010CB push offset string "Address of strcmp(): %0x\n" (00422044)

004010D0 call printf (00401120)

004010D5 add esp,8

16: else

004010D8 jmp tell_me+5Bh (004010eb)

17: printf("Function address: %p\n", f);

004010DA mov edx,dword ptr [ebp+8]

004010DD push edx

004010DE push offset string "Function address: %p\n" (00422028)

004010E3 call printf (00401120)

004010E8 add esp,8=======================================================

第二个:--------------------------------------------------------------------------------------------

#include

#include

int main(void)

{

char p1[20] = "abc", *p2 = "pacific sea";

printf("%s %s %s\n", p1, p2, strcat(p1, p2)); /*

return 0;

}

---------------------------------------------------------------------------------------------

输出我的认为应该是先输出p1, p2以后再执行strcat. 但是实际输出的情况:

abcpacific sea pacific sea abcpacific sea

可以发现strcat先于p1执行,改变了p1的内容,请问这个内部是怎么的顺序?难道printf不是顺序执行的?

=======================================================

得到的解答:不同的编译器printf的函数参数进栈顺序不同,printf函数中的strcat可以看反汇编代码.6: printf("%s\t%s\t%s\n", p1, p2, strcat(p1, p2)); /*

00401045 mov edx,dword ptr [ebp-18h]

00401048 push edx

00401049 lea eax,[ebp-14h]

0040104C push eax

0040104D call strcat (00401130) //可以看到是先调用strcat函数的,

00401052 add esp,8

00401055 push eax

00401056 mov ecx,dword ptr [ebp-18h]

00401059 push ecx

0040105A lea edx,[ebp-14h]

0040105D push edx

0040105E push offset string "%s\t%s\t%s\n" (0042201c)

00401063 call printf (004010a0) //最后调用printf函数输出

00401068 add esp,10h

===============================================

汇编直观而简单的说明了一些疑问....

再说下gcc如何产生汇编代码:

1: gcc -S main.c 可以生成2: gcc -c main.c 生成main.oobjdump -S -D main.o > main.asm

我更倾向于第二种.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值