函数可以知道调用它的是什么吗?
Can a function know what's calling it?
一个函数是否可以通过使用内存地址来判断调用它的是什么?例如,函数 foo();
获取有关它是否在 main();
而不是其他函数中被调用的数据?
如果是这样,是否可以根据调用内容更改 foo();
的内容?
示例:
int foo()
{
if (being called from main())
printf("Hello\n");
if (being called from some other function)
printf("Goodbye\n");
}
这个问题可能有点悬而未决,但是是否有某种 C 技巧可以使这成为可能?
知道谁调用了特定函数本质上就是堆栈跟踪可视化的内容。但是,没有通用的标准方法来提取它。理论上,可以编写针对软件将 运行 使用的每种系统类型的代码,并为它们中的每一个实现堆栈跟踪功能。在那种情况下,您可以检查堆栈并查看当前函数之前的内容。
但是尽管如此,您可能应该问的问题是为什么?编写从特定函数调用时以特定方式运行的函数并不是很好的隔离逻辑。相反,您可以考虑将参数传递给导致逻辑更改的函数。这也将使结果更具可测试性和可靠性。
如何实际提取堆栈跟踪已在此处收到许多答案:How can one grab a stack trace in C?
我认为 C 中的 if
循环不能有你提到的条件。
如果要检查此函数是否从 main()
调用,则必须在 main()
和另一个函数中执行 printf
语句。
我真的不知道你想要实现什么,但根据我的理解,你可以做的是每个函数将传递一个额外的参数,该参数将以字符数组、整数的形式唯一标识该函数或枚举。
例如:
enum function{main, add, sub, div, mul};
并调用如下函数:
add(3,5,main);//adds 3 and 5. called from main
对代码的更改很典型,就像您要添加更多功能一样。但这是一种更简单的方法。
没有。 C语言不支持获取函数调用者的名字等信息。
如所有其他答案所示,这只能使用外部工具获得,例如使用堆栈跟踪和 compiler/linker 发出的符号表。
对于高度优化的 C 来说,它没有任何意义。编译器尝试优化的难度越大,最终的 executable 与源代码的相似度就越低(特别是对于 link-time 代码生成,旧的“单独编译单元”问题不再阻止大量优化)。至少在理论上(但通常在某些编译器的实践中)存在于源代码中的函数可能不存在于最终的 executable 中(例如,可能已内联到它们的调用者中);可能会生成源代码中不存在的函数(例如,编译器检测许多函数中的公共序列并将它们“外联”到一个新函数中以避免代码重复);和函数可以被数据替换(例如,“int abcd(uint8_t a, uint8_t b)
”被 abcd_table[a][b]
查找 table 替换)。
对于严格的 C(没有扩展或修改),没有。它根本不能支持这样的东西,因为它不能期望(对于任何编译器,包括尚不存在的未来编译器)最终的 output/executable 类似于源代码。
一个实现定义的扩展,或者甚至只是一个涉及内联汇编的 hack,可能是“技术上可行的”(特别是如果编译器没有很好地优化代码)。最可能的方法是(ab)使用调试信息来确定调用者,从“函数应该 return 到它何时 return”。
编译器支持这样的假设扩展的更好方法可能是编译器使用我提到的一些优化 - 具体来说,将原始 foo()
拆分为 2 个单独的版本,其中一个版本是只从 main()
调用过,另一个版本用于其他调用者。这样做的好处是让编译器也可以优化分支——它可以变成像 int foo_when_called_from_main() { printf("Hello\n"); }
,它可以直接内联到调用者中,因此 foo
的两个版本都不存在于最终的 executable。当然,如果 foo()
有所有调用者都使用的其他代码,那么可以将通用代码提取到一个新函数中,而不是复制它(例如,它可能会变成 int foo_when_called_from_main() { printf("Hello\n"); foo_common_code(); }
)。
可能没有任何假设的编译器可以像那样工作,但没有真正的理由你不能自己做这些相同的优化(并让它在所有编译器上工作)。
注意:是的,这只是建议您 can/should 重构代码的一种狡猾方式,这样它就不需要知道调用它的是哪个函数。
一个函数是否可以通过使用内存地址来判断调用它的是什么?例如,函数 foo();
获取有关它是否在 main();
而不是其他函数中被调用的数据?
如果是这样,是否可以根据调用内容更改 foo();
的内容?
示例:
int foo()
{
if (being called from main())
printf("Hello\n");
if (being called from some other function)
printf("Goodbye\n");
}
这个问题可能有点悬而未决,但是是否有某种 C 技巧可以使这成为可能?
知道谁调用了特定函数本质上就是堆栈跟踪可视化的内容。但是,没有通用的标准方法来提取它。理论上,可以编写针对软件将 运行 使用的每种系统类型的代码,并为它们中的每一个实现堆栈跟踪功能。在那种情况下,您可以检查堆栈并查看当前函数之前的内容。
但是尽管如此,您可能应该问的问题是为什么?编写从特定函数调用时以特定方式运行的函数并不是很好的隔离逻辑。相反,您可以考虑将参数传递给导致逻辑更改的函数。这也将使结果更具可测试性和可靠性。
如何实际提取堆栈跟踪已在此处收到许多答案:How can one grab a stack trace in C?
我认为 C 中的 if
循环不能有你提到的条件。
如果要检查此函数是否从 main()
调用,则必须在 main()
和另一个函数中执行 printf
语句。
我真的不知道你想要实现什么,但根据我的理解,你可以做的是每个函数将传递一个额外的参数,该参数将以字符数组、整数的形式唯一标识该函数或枚举。 例如:
enum function{main, add, sub, div, mul};
并调用如下函数:
add(3,5,main);//adds 3 and 5. called from main
对代码的更改很典型,就像您要添加更多功能一样。但这是一种更简单的方法。
没有。 C语言不支持获取函数调用者的名字等信息。
如所有其他答案所示,这只能使用外部工具获得,例如使用堆栈跟踪和 compiler/linker 发出的符号表。
对于高度优化的 C 来说,它没有任何意义。编译器尝试优化的难度越大,最终的 executable 与源代码的相似度就越低(特别是对于 link-time 代码生成,旧的“单独编译单元”问题不再阻止大量优化)。至少在理论上(但通常在某些编译器的实践中)存在于源代码中的函数可能不存在于最终的 executable 中(例如,可能已内联到它们的调用者中);可能会生成源代码中不存在的函数(例如,编译器检测许多函数中的公共序列并将它们“外联”到一个新函数中以避免代码重复);和函数可以被数据替换(例如,“int abcd(uint8_t a, uint8_t b)
”被 abcd_table[a][b]
查找 table 替换)。
对于严格的 C(没有扩展或修改),没有。它根本不能支持这样的东西,因为它不能期望(对于任何编译器,包括尚不存在的未来编译器)最终的 output/executable 类似于源代码。
一个实现定义的扩展,或者甚至只是一个涉及内联汇编的 hack,可能是“技术上可行的”(特别是如果编译器没有很好地优化代码)。最可能的方法是(ab)使用调试信息来确定调用者,从“函数应该 return 到它何时 return”。
编译器支持这样的假设扩展的更好方法可能是编译器使用我提到的一些优化 - 具体来说,将原始 foo()
拆分为 2 个单独的版本,其中一个版本是只从 main()
调用过,另一个版本用于其他调用者。这样做的好处是让编译器也可以优化分支——它可以变成像 int foo_when_called_from_main() { printf("Hello\n"); }
,它可以直接内联到调用者中,因此 foo
的两个版本都不存在于最终的 executable。当然,如果 foo()
有所有调用者都使用的其他代码,那么可以将通用代码提取到一个新函数中,而不是复制它(例如,它可能会变成 int foo_when_called_from_main() { printf("Hello\n"); foo_common_code(); }
)。
可能没有任何假设的编译器可以像那样工作,但没有真正的理由你不能自己做这些相同的优化(并让它在所有编译器上工作)。
注意:是的,这只是建议您 can/should 重构代码的一种狡猾方式,这样它就不需要知道调用它的是哪个函数。