在不事先声明的情况下调用 C 函数
Call C function without declaring it beforehand
简短版本:我想在调用它的同一语句中声明一个函数。我正在寻找的语法是这样的:
// foo is undeclared in this file, and implemented in another file
int main() {
void* p = (cast_to_function_that_receivs_ints_and_returns_pointer)foo(1,2);
}
长版:
以下代码会产生明显的 implicit declaration
警告和 undefined reference
错误,因为调用了 foo
:
// a.c
int main() {
void* p = foo(1,2);
}
我在编译中加入如下文件来解决undefined reference
:
// b.c
void* foo(int a, int b) {
return (void*)0xbadcafe;
}
我现在想解决implicit declaration
。通常的解决方案是将 a.c
修改为 #include
对 foo 的声明或声明它本身,例如:
// a.c
void* foo(int a, int b);
int main() {
void* p = foo(1,2);
}
但我宁愿不声明 foo,而是修改调用 foo 的行,类似于函数指针语法,或者类似于我在 "short versions" 中发布的示例.有可能吗?
假设我精通 C 并且我有一个有效的动机 - 我想通过 -Dfoo=bar
.
重新编译 "override" foo
的行为
您可以在调用时转换函数:
void *p = ((void *(*)(int, int))foo)(1, 2);
它很丑,我看不出有什么正当理由,但你可以。
没有办法绕过申报要求。您必须定义一个符号供编译器使用。一些编译器允许您使用 pragma 或其他非标准功能来创建符号和 physical/virtual 地址之间的映射。
将您的 mock_foo.c 文件和 link 目标文件编译到程序而不是 foo.c。
另一种方法是只通过宏定义调用:
#ifdef MOCK_FOO
#define (FOO(a, b) mock_foo(a, b))
#else
#define (FOO(a, b) foo(a, b)
#endif
否则,您必须了解 compiler/linker 和 OS/loader 的工作原理,才能正确挂钩函数以调用模拟。高质量模拟框架的工具成本如此之高是有原因的。它们非常复杂。
所以如果我理解正确的话,你的动机是你有看起来像
的现有代码
p = bar(1,2);
并且您想定义宏,以便它调用 foo(1,2)
。但是您不想修改源文件以包含 foo
的声明 - 您希望通过命令行宏定义来完成所有操作。我没听错吗?
既然你已经标记了这个 gcc, perhaps you are willing to consider non-standard gcc extensions to the C language. If so, you can do it with gcc statement expressions,也由 clang 和 icc 支持。定义 bar
以扩展为包含块的表达式,该块声明 foo
并且其值是指向 foo
的指针。即:
#define bar ({ extern void *foo(int, int); foo; })
或从命令行:
gcc -D'bar=({ extern void *foo(int, int); foo; })' call_bar.c
这有
一个变体是定义一个带有两个参数的宏bar(a,b)
,其中相应的语句表达式实际调用foo
:
gcc -D'bar(a,b)=({ extern void *foo(int, int); foo((a), (b)); })' call_bar.c
但如果原始代码尝试调用 p = (bar)(a,b)
或尝试获取 bar
的地址,这将失败。
我不知道有什么方法可以在标准 C 中获得这种确切效果。但另一种方法是创建一个包含 foo
声明的头文件,然后使用 -include
到 "inject" 它在源文件的顶部:
gcc -include declare_foo.h -Dbar=foo call_bar.c
这在技术上不是您所要求的,因为在某种程度上它确实涉及声明 foo
"beforehand",但它仍可能有助于解决您的问题。在这种情况下,一切都是标准 C,但我们已将 "non-portability" 从代码移至构建过程。
另一方面,如果 bar
所需的替代品足够简单,可以放入宏中,例如示例中的常量 return,那么您可以去掉中间人 foo
然后定义一个宏:
gcc -D'bar(a,b)=((void *)0xbadcafe)' call_bar.c
简短版本:我想在调用它的同一语句中声明一个函数。我正在寻找的语法是这样的:
// foo is undeclared in this file, and implemented in another file
int main() {
void* p = (cast_to_function_that_receivs_ints_and_returns_pointer)foo(1,2);
}
长版:
以下代码会产生明显的 implicit declaration
警告和 undefined reference
错误,因为调用了 foo
:
// a.c
int main() {
void* p = foo(1,2);
}
我在编译中加入如下文件来解决undefined reference
:
// b.c
void* foo(int a, int b) {
return (void*)0xbadcafe;
}
我现在想解决implicit declaration
。通常的解决方案是将 a.c
修改为 #include
对 foo 的声明或声明它本身,例如:
// a.c
void* foo(int a, int b);
int main() {
void* p = foo(1,2);
}
但我宁愿不声明 foo,而是修改调用 foo 的行,类似于函数指针语法,或者类似于我在 "short versions" 中发布的示例.有可能吗?
假设我精通 C 并且我有一个有效的动机 - 我想通过 -Dfoo=bar
.
foo
的行为
您可以在调用时转换函数:
void *p = ((void *(*)(int, int))foo)(1, 2);
它很丑,我看不出有什么正当理由,但你可以。
没有办法绕过申报要求。您必须定义一个符号供编译器使用。一些编译器允许您使用 pragma 或其他非标准功能来创建符号和 physical/virtual 地址之间的映射。
将您的 mock_foo.c 文件和 link 目标文件编译到程序而不是 foo.c。
另一种方法是只通过宏定义调用:
#ifdef MOCK_FOO
#define (FOO(a, b) mock_foo(a, b))
#else
#define (FOO(a, b) foo(a, b)
#endif
否则,您必须了解 compiler/linker 和 OS/loader 的工作原理,才能正确挂钩函数以调用模拟。高质量模拟框架的工具成本如此之高是有原因的。它们非常复杂。
所以如果我理解正确的话,你的动机是你有看起来像
的现有代码p = bar(1,2);
并且您想定义宏,以便它调用 foo(1,2)
。但是您不想修改源文件以包含 foo
的声明 - 您希望通过命令行宏定义来完成所有操作。我没听错吗?
既然你已经标记了这个 gcc, perhaps you are willing to consider non-standard gcc extensions to the C language. If so, you can do it with gcc statement expressions,也由 clang 和 icc 支持。定义 bar
以扩展为包含块的表达式,该块声明 foo
并且其值是指向 foo
的指针。即:
#define bar ({ extern void *foo(int, int); foo; })
或从命令行:
gcc -D'bar=({ extern void *foo(int, int); foo; })' call_bar.c
这有
一个变体是定义一个带有两个参数的宏bar(a,b)
,其中相应的语句表达式实际调用foo
:
gcc -D'bar(a,b)=({ extern void *foo(int, int); foo((a), (b)); })' call_bar.c
但如果原始代码尝试调用 p = (bar)(a,b)
或尝试获取 bar
的地址,这将失败。
我不知道有什么方法可以在标准 C 中获得这种确切效果。但另一种方法是创建一个包含 foo
声明的头文件,然后使用 -include
到 "inject" 它在源文件的顶部:
gcc -include declare_foo.h -Dbar=foo call_bar.c
这在技术上不是您所要求的,因为在某种程度上它确实涉及声明 foo
"beforehand",但它仍可能有助于解决您的问题。在这种情况下,一切都是标准 C,但我们已将 "non-portability" 从代码移至构建过程。
另一方面,如果 bar
所需的替代品足够简单,可以放入宏中,例如示例中的常量 return,那么您可以去掉中间人 foo
然后定义一个宏:
gcc -D'bar(a,b)=((void *)0xbadcafe)' call_bar.c