Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么堆栈中定义的C函数指针在传递时无效?_C_Gcc_Nested Function - Fatal编程技术网

为什么堆栈中定义的C函数指针在传递时无效?

为什么堆栈中定义的C函数指针在传递时无效?,c,gcc,nested-function,C,Gcc,Nested Function,我试图将父块范围内定义的函数指针传递给另一个函数。我在不同的环境中工作和工作。(我不是C专家) 守则: #包括 #包括 无效测试_函数(bool(*函数_指针)(int x)){ printf(“addr传递的函数\u指针%p\n”,函数\u指针); if(函数_指针(100)){ printf(“run:true\n”); }否则{ printf(“run:false\n”); } } 布尔函数在主(int x)外{ 返回x0; } printf(“addr function\u inside

我试图将父块范围内定义的函数指针传递给另一个函数。我在不同的环境中工作和工作。(我不是C专家)

守则:

#包括
#包括
无效测试_函数(bool(*函数_指针)(int x)){
printf(“addr传递的函数\u指针%p\n”,函数\u指针);
if(函数_指针(100)){
printf(“run:true\n”);
}否则{
printf(“run:false\n”);
}
}
布尔函数在主(int x)外{
返回x<0;
}
内部主(空){
//使用全局定义的函数运行
printf(“addr function\u在\u main%p\n之外”,function\u在\u main之外);
测试功能(主功能外的功能);
//使用此堆栈块中定义的函数运行
布尔函数在主函数内(int x){
返回x>0;
}
printf(“addr function\u inside\u main%p\n”,function\u inside\u main);
test_function(function_inside_main);//地址不应该有效吗?
}
在Ubuntu 16.04.4和GCC版本
5.4.0
(在Amazon EC2上)上,它与输出一起工作:

addr function_outside_main 0x400620
addr passed function_pointer 0x400620
  run: false
addr function_inside_main 0x7ffc018d5690
addr passed function_pointer 0x7ffc018d5690
  run: true
在具有GCC版本
9.3.0
(在Windows WSL下)的Ubuntu 20.04.1上,它会出现以下故障:

addr function_outside_main 0x7ff19c8631dd
addr passed function_pointer 0x7ff19c8631dd
  run: false
addr function_inside_main 0x7ffffc033b20
addr passed function_pointer 0x7ffffc033b20
zsh: segmentation fault (core dumped)  ./a.out

使用此gcc扩展,您不能在其定义的函数范围之外调用它。这是一种未定义的行为。非常类似于撤销函数范围外指针引用的本地自动变量,我没有访问WSL(或9.3 gcc)的权限,但我注意到一个警告,即内部函数在其定义的函数返回后可能无法调用。通常,对于延迟的函数来说,这会很麻烦,但我怀疑编译器已经用函数尾声折叠了final语句(
test\u function(function\u inside\u main);//地址不应该有效吗?
);因此,您可以添加一行:
printf(“你好,世界”)

这就是定义自己的类C语言的扩展的危险。

像这样的嵌套函数是gcc扩展,而不是C标准的一部分

gcc对此的实现通常1涉及为嵌套函数创建堆栈上thunk,因此调用它需要可执行堆栈支持。较新版本的Linux(和Windows)默认为不可执行堆栈,因此会崩溃

要实现这一点,您可以使用gcc的
-z execstack
选项,也可以使用
execstack
工具在创建二进制文件后修改二进制文件以指定可执行堆栈



1在某些版本的带有-O的gcc中,它可以确定何时嵌套函数实际上不需要嵌套(它们从不引用包含的范围),并且在这些情况下不使用thunk

这不是“堆栈中”定义的函数指针,而是在其他无效函数中定义的整个函数。@i486它在gcc中有效。这是gcc扩展。问题可能是您不应该使用%p打印函数指针;它仅用于对象指针。请参阅@dave谢谢您提供的信息,我甚至尝试删除所有printf语句以生成sames resultsOk。我认为问题在于,在WSL上,堆栈不是可执行的,嵌套函数必须是可执行的——请查看此扩展的GCC文档,GCC文档()与您不一致:“通过存储其地址或将地址传递给另一个函数,可以从其名称的作用域之外调用嵌套函数”这不是关于生存期而不是作用域吗?如果我有
void foo(void){int x=7;otherfunc(&x);}
,则
other_func
取消引用传递的指针是完全合法的,只要它不保存在
foo
返回后被取消引用的副本即可。嵌套函数的情况是相同的。我测试了这个想法,它为WSL产生了相同的分段错误。然后是调试器/检查程序集时间。如果你在那一行中断,然后在函数_in_main的地址上设置一个abs断点(如前所述),这就是蹦床。通过蹦床,你将进入嵌套函数,然后单步退出。我在WSL下用gdb运行了一个调试器,当它执行
If(函数_指针(100)){
由于“程序接收信号SIGSEGV,分段故障”而失败。"你必须一步一步地组装才能通过蹦床。从那句话中,重复stepi,它应该需要大约5个stepi,才能在main中实现函数。有趣的是,这对我来说是一个新领域。我安装了execstack,然后运行了
gcc-z execstack test.c
,但它仍然出现故障。我还尝试运行了
execstack a.out
哪个输出“X a.out”然后运行,但仍然出现故障。再说一次,这不是我的专业知识,但你和@dave对此做了类似的说明。我不太担心这是否只是Windows WSL的限制。谷歌搜索
WSL execstack
发现这是WSL上的一个常见问题,它不支持设置execstack(它忽略该设置,并始终使堆栈不可执行)。