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

 更新时间:2013年05月15日 09:18:39   作者:  
本篇文章是对c语言中函数指针的两个示例做了详细的分析介绍,需要的朋友参考下

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

复制代码 代码如下:

#include <stdio.h>
#include <string.h>
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 <strcmp@plt>
(gdb) print strcmp
$2 = {<text variable, no debug info>} 0xb7e59d20 <strcmp>
(gdb) n
16 printf("Address of strcmp(): %p\n", f);
(gdb) print strcmp
$3 = {<text variable, no debug info>} 0xb7e59d20 <strcmp>
(gdb) print *strcmp
$4 = {<text variable, no debug info>} 0xb7e59d20 <strcmp>
(gdb) print *f
$5 = {int (const char *, const char *)} 0x8048310 <strcmp@plt>
(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 <stdio.h>
#include <string.h>
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.o
objdump -S -D main.o > main.asm
我更倾向于第二种.

相关文章

  • 详解C/C++中const限定符总结

    详解C/C++中const限定符总结

    const是一种限定符,被const所限定的变量其值不可以被改变。。这篇文章主要介绍了C/C++中const限定符总结,通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • C++逐步介绍日期类的使用

    C++逐步介绍日期类的使用

    下面小编就为大家带来一篇C++实现日期类(Date类)的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2022-07-07
  • 浅析c语言中的内存

    浅析c语言中的内存

    在c++中,内存分为5个区,分别是栈区,堆区,自由存储区,全局/静态存储区和常量存储区.
    2017-09-09
  • Java C++ 题解leetcode1619删除某些元素后数组均值

    Java C++ 题解leetcode1619删除某些元素后数组均值

    这篇文章主要为大家介绍了Java C++ 题解leetcode1619删除某些元素后数组均值示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • 一起来看看C语言世界中的结构体

    一起来看看C语言世界中的结构体

    这篇文章主要为大家详细介绍了C语言世界中的结构体,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • 一张图总结C++中关于指针的那些事

    一张图总结C++中关于指针的那些事

    今天小编就为大家分享一篇关于一图总结C++中关于指针的那些事,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-02-02
  • C++11如何引入的尾置返回类型

    C++11如何引入的尾置返回类型

    C++11 标准引入的尾置返回类型,可以让返回复杂类型的函数声明更加清晰易读,在无法使用C++14 标准的情况下,通过尾置返回类型的语法来推导函数模板的返回类型无疑是最简便的方法,这篇文章主要介绍了C++11引入的尾置返回类型,需要的朋友可以参考下
    2023-01-01
  • C语言实现将字符串转换成整数

    C语言实现将字符串转换成整数

    这篇文章主要为大家详细介绍了如何用C语言写一个函数,把字符串转换成整数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • C++数组和指针的区别与联系

    C++数组和指针的区别与联系

    这篇文章主要介绍了C++数组和指针的区别与联系,数组是具有固定大小和连续内存空间的相同数据集合,指针是指存放内存地址的变量,更多详细资料请参考下面文章内容
    2022-01-01
  • C++中list的使用方法及常用list操作总结

    C++中list的使用方法及常用list操作总结

    这篇文章主要介绍了C++中list的使用方法及常用list操作总结的相关资料,需要的朋友可以参考下
    2017-06-06

最新评论