- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试分析提供给函数指针的大小不兼容(更少或更多参数)的额外或更少参数发生了什么。
考虑下面的例子
#include <stdio.h>
#include <conio.h>
typedef void (*fp)(int a, int b, int c);
void function1(int a, int b) {
printf("\n Function1: a: %d, b: %d (%d)", a, b, __LINE__);
}
void function2(int a, int b, int c) {
printf("\n Function1: a: %d, b: %d, c: %d (%d)", a, b, c, __LINE__);
}
void function3(int a, int b, int c, int d) {
printf("\n Function1: a: %d, b: %d, c: %d, d: %d (%d)", a, b, c, d, __LINE__);
}
int main() {
fp fp1 = (fp)function1;
fp fp2 = (fp)function2;
fp fp3 = (fp)function3;
fp1(1, 2, 3);
fp2(4, 5, 6);
fp3(7, 8, 9);
getch();
return 0;
}
可以观察到 fp1 和 fp3 采用与函数指针不同的参数大小(arguments)。但是我在执行程序时仍然没有观察到任何未定义的行为。
程序的输出是
Function1: a: 1, b: 2 (7)
Function1: a: 4, b: 5, c: 6 (11)
Function1: a: 7, b: 8, c: 9, d: 4200926 (15)
因此,在调用函数时,我们会根据调用约定将地址和参数压入堆栈,然后弹出。在上面的例子中,当我们将参数压入堆栈并弹出它们时,我们弹出的元素少了一个,实际上必须破坏返回地址(在 fp3 的情况下相同),但所有代码都正确执行。
在此stack overflow question函数参数没有不匹配。所以,我找不到我的问题的答案。
来自wikipedia ,已经提到被调用者/调用者需要像这样清理堆栈
被调用者清理[编辑]当被调用者从堆栈中清除参数时,需要在编译时知道堆栈需要调整多少字节。因此,这些调用约定与可变参数列表不兼容,例如打印()。然而,它们可能更节省空间,因为不需要为每个调用生成展开堆栈所需的代码。使用这些约定的函数在 ASM 代码中很容易识别,因为它们会在返回之前展开堆栈。 x86 ret 指令允许一个可选的 16 位参数,该参数指定返回给调用者之前要展开的堆栈字节数。这样的代码看起来像这样:
ret 12
但是我在上述程序的反汇编代码中没有观察到类似的东西。 (粘贴在下面的 function1)
--------------------------------------------------------------------------------
18 int main() {
0x004013C7 push %ebp
0x004013C8 mov %esp,%ebp
0x004013CA and $0xfffffff0,%esp
0x004013CD sub $0x20,%esp
0x004013D0 call 0x401a00 <__main>
19 fp fp1 = (fp)function1;
0x004013D5 movl $0x401334,0x1c(%esp)
20 fp fp2 = (fp)function2;
0x004013DD movl $0x40135e,0x18(%esp)
21 fp fp3 = (fp)function3;
0x004013E5 movl $0x40138f,0x14(%esp)
23 fp1(1, 2, 3);
0x004013ED movl $0x3,0x8(%esp)
0x004013F5 movl $0x2,0x4(%esp)
0x004013FD movl $0x1,(%esp)
0x00401404 mov 0x1c(%esp),%eax
0x00401408 call *%eax
24 fp2(4, 5, 6);
0x0040140A movl $0x6,0x8(%esp)
0x00401412 movl $0x5,0x4(%esp)
0x0040141A movl $0x4,(%esp)
0x00401421 mov 0x18(%esp),%eax
0x00401425 call *%eax
25 fp3(7, 8, 9);
0x00401427 movl $0x9,0x8(%esp)
0x0040142F movl $0x8,0x4(%esp)
0x00401437 movl $0x7,(%esp)
0x0040143E mov 0x14(%esp),%eax
0x00401442 call *%eax
27 getch();
0x00401444 call 0x401c40 <getch>
29 return 0;
0x00401449 mov $0x0,%eax
30 }
0x0040144E leave
0x0040144F ret
--------------------------------------------------------------------------------
6 void function1(int a, int b) {
0x00401334 push %ebp
0x00401335 mov %esp,%ebp
0x00401337 sub $0x18,%esp
7 printf("\n Function1: a: %d, b: %d (%d)", a, b, __LINE__);
0x0040133A movl $0x7,0xc(%esp)
0x00401342 mov 0xc(%ebp),%eax
0x00401345 mov %eax,0x8(%esp)
0x00401349 mov 0x8(%ebp),%eax
0x0040134C mov %eax,0x4(%esp)
0x00401350 movl $0x403024,(%esp)
0x00401357 call 0x401c78 <printf>
8 }
0x0040135C leave
0x0040135D ret
那么,有人可以指导/帮助我找到答案吗。
谢谢。
最佳答案
未定义行为的问题是它可以做你想做的事,只是不能保证在所有情况下都做同样的事情。 (即使在特定架构上给出了一个编译器)。对于定义的行为,有标准和特定于实现的保证。并且您可能永远不会发现您认为出乎意料的事情,因为实现者已决定做一些合理但不保证的事情。
在这种情况下,在堆栈上放太多东西可能是无害的,因为被调用者只引用压入的 X 的前 N 个。
当调用 function3
时,它为 d
获取 4200926
。这不是定义的行为,也不是任何值、段错误或任何其他定义的行为。这只是未定义的行为。
清理堆栈的并不总是被调用方,调用方可以将堆栈恢复到调用前的值,这不是定义的行为。任何一种方法都是可以接受的,清理堆栈的功能纯粹是实现定义的,实现者甚至可以允许标志来改变它。
关于c - 函数指针签名不匹配,执行程序时仍然没有遇到任何问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20313668/
我是 Haskell 世界的新手,我编译了一个简单的 exe.program main = do putStrLn "Hello, what's your name?" name
我正在使用 JUnit,现在我想在运行测试之前执行 Java 程序(主方法)。 即在我的项目中,我有一个包含一个具有 main 方法的类的包。我想在运行测试之前运行它(可能在一个单独的进程中),因为被
我的代码是: char* arg_list[] = { "gnuplot", "gnuplot_script.sh", NULL }; printf("Ready %s %s\n", arg_list
exec() 似乎在服务器上启用(function_exists('exec') 返回 true,并且命令未在 'disable_functions' 变量中列出)但我们得到 警告:出于安全原因,ex
我想从 Adobe AIR 应用程序中按下一个按钮并执行一些已安装的程序。例如,我会有一个名为“Start Winamp”的按钮。当按下它时,它应该直接启动 Winamp.exe...我不想执行一
我学习 CS 有一段时间了,似乎我(或我的许多 friend )从来不了解在制作、安装等方面幕后发生的事情。 纠正我,但是 make 是一种编译一组文件的方法吗? 在 Windows 上“将程序安装到
如何执行 MemoryStream 中的程序,这样我就不必先将它保存到硬盘上。该文件可能不会临时保存到硬盘。该程序必须在内存中 100% 正常运行。 static string s
我使用了一个程序(在 Windows 上),我不会透露其名称,它可以从命令行打开而无需通过任何身份验证。我正在尝试创建一些安全措施以防止其他人以这种方式访问它。 我计划用批处理文件替换这个程序的内
我正在开发一个 Web 应用程序,但我陷入了困境: 我想创建一个简单的标签来触发本地程序的执行如gedit、mozilla firefox等 我的项目基于 HTML、Javascript 和 PHP。
我有一个 C++ 程序需要 root 权限才能执行某些功能。如果我在 su 模式下运行编译后的代码,例如 linux-c5b6:/home/suman # 它满足了我的要求,但是在运行时我想检查我的进
我正在创建一个 shell 副本,但在执行自制程序时遇到了问题。我的意思是,当我想执行诸如 java、ls、wc 等之类的东西时,一切都很好......已经存在于路径变量。 现在我希望能够执行“myl
我试图让一些值显示在电子墨水显示器上(通过 SPI)。我已经编写了软件来初始化显示并显示作为命令行参数传递的值。问题是,由于电子墨水技术,显示需要几秒钟才能完全实现,所以此时显示程序也在运行。 另一个
所以我在 Oracle 11g 上创建了以下过程: create or replace PROCEDURE calc_fee (proc_borrowed_date IN Borrowing.borr
我想让 Maven 目标触发 java 类的执行。我正在尝试使用以下行迁移 Makefile: neotest: mvn exec:java -Dexec.mainClass="org.dha
我想用 os.system 运行命令,但出现错误 c:/fe ' is not recognized as an internal or external command, operable prog
我将Spark 1.5.2用于Spark Streaming应用程序。 Web UI的“执行者”选项卡中的存储内存是什么?如何达到530 MB?如何更改该值? 最佳答案 小心:您使用的非常,非常老旧且
我正在阅读 GitLab CI Multirunner documentation它介绍了如何设置 GitLab CI Multirunner 执行器,但我似乎无法在文档或在线搜索中找到有关更改初始设
我是 C 编程新手。我正在尝试使用 fork()、exec() 和 waitpid() 运行由用户指定的路径给出的程序命令。我已经尝试让它正确运行几个小时了,但我不断收到错误,我不知道如何排除故障,一
我是 Java 和 Selenium 的初学者,我在工作时遇到了 JavascriptExecutor。 想知道:虽然 Remote webdriver 和 Firefox driver 实现了 ja
我正在尝试通过 linux c++ 中的邮件命令发送电子邮件,但 execl 导致错误。 如何使用 exec 发送此命令? /bin/echo llol | /usr/bin/mail -s "tes
我是一名优秀的程序员,十分优秀!