我大多说服自己,我遇到了一些g ++ 4.8.3错误,但我想我会首先问这个列表,因为我对setjmp/longjmp的经验很少.我将我的代码简化为以下foo.cxx:
#include <setjmp.h>
#include <string.h>
// Changing MyStruct to be just a single int makes the compiler happy.
struct MyStruct
{
int a;
int b;
};
// Setting MyType to int makes the compiler happy.
#ifdef USE_STRUCT
typedef MyStruct MyType;
#elif USE_INT
typedef int MyType;
#endif
void SomeFunc(MyType val)
{
}
static void static_func(MyType val)
{
SomeFunc(val);
}
int main(int argc, char **argv)
{
jmp_buf env;
if (setjmp(env))
{
return 1;
}
MyType val;
#ifdef USE_STRUCT
val.a = …Run Code Online (Sandbox Code Playgroud) 我想在C程序中使用setjmp和longjmp,该程序链接到用C++实现的库(但有一个C API).
C++代码确实进行了动态内存分配,指针通过API传递,但只要代码的C端正确管理那些(不透明)对象,使用longjmp就不会有任何混乱,对吧?
我知道在C++代码中使用这些函数是不安全的,但它是否应该在链接到C++代码的C代码中安全?
以下代码:
main.cpp:
# include <iostream>
# include <csetjmp>
# include <stdexcept>
using namespace std ;
void do_work(jmp_buf context)
{
try
{
throw runtime_error("Ouch !") ;
}
catch(exception & e)
{
}
longjmp(context, -1) ; //BP1
}
int main(int, char *[])
{
jmp_buf context ;
try
{
if( setjmp(context) != 0 )
{
throw runtime_error("Oops !") ; //BP2
}
do_work(context) ;
}
catch(exception & e)
{
cout << "Caught an exception saying : " << e.what() …Run Code Online (Sandbox Code Playgroud) 我想使用 longjmp 返回错误代码,并从调用 setjmp 的函数传递它。简化代码:
int do_things(stuff ........)
{
int error_code;
jmp_buf jb;
if ((error_code = setjmp(jb)) == 0) {
/* do stuff */
return 0;
}
else {
return error_code;
}
}
Run Code Online (Sandbox Code Playgroud)
但我读到:“setjmp 宏的调用只能出现在以下上下文之一中:”
the entire controlling expression of a selection or iteration statement
if (setjmp(jb)) {
switch (setjmp(jb)) {
while (setjmp(jb)) {
Run Code Online (Sandbox Code Playgroud)
或者
one operand of a relational or equality operator with the other operand
an integer constant expression, with the resulting expression being
the entire controlling expression …Run Code Online (Sandbox Code Playgroud) 我知道人们总是说不要使用longjmp,它是邪恶的,它是危险的。
但我认为它对于退出深度递归/嵌套函数调用很有用。
是不是一次下单longjmp比多次重复检查和退货要快一些if(returnVal != SUCCESS) return returnVal;?
至于安全性,只要动态内存等资源释放得当,应该不会有问题吧?
到目前为止,使用似乎longjmp并不困难,甚至使我的代码更简洁。我很想经常使用它。
(恕我直言,在许多情况下,首先在深度递归中没有分配动态内存/资源。深度函数调用对于数据解析/操作/验证来说似乎更常见。动态分配通常发生在更高级别,然后调用函数,其中setjmp出现。)
我一直试图在我的代码中查找间歇性崩溃错误(使用setjmp),并将其缩小为:在使用/ O2进行编译时显示,使用/ O2/Oy-消失,即仅显示省略帧指针.
http://msdn.microsoft.com/en-us/library/2kxx5t2c(v=vs.80).aspx建议setjmp需要一个帧指针.从而:
似乎当使用/ O2编译使用setjmp的程序时,Visual C++会静默生成导致间歇性堆栈损坏的代码.这是真的吗,还是我错过了什么?
在我看来,只有调用setjmp的函数才需要用帧指针编译,程序的其余部分 - 甚至是调用longjmp的函数 - 应该可以省略帧指针.这是真的?
编辑:我把它缩小了一点.
在调用setjmp的函数上启用帧指针没有任何区别,但那是因为编译器已经这样做了,就像它应该的那样,显然注意到它需要完成,并自动完成.
有什么区别是在主要上启用帧指针.这并不像听起来那么奇怪,因为坠机是从主要的回归中显现出来的.现在,我想到了它,我可以在快速谷歌搜索setjmp使用中找到的所有示例,在main中执行.也许碰巧Microsoft编译器团队只是以这种方式测试它.
这是使用它的惯用方法,也许最好的解决方法是让我将setjmp-using函数内联到main中.
setjmp()应该将包含"返回地址"和"堆栈指针"的寄存器保存到"jmp_buf"中.当我编译(gcc和clang)并使用glibc在x86_64下调试以下程序时,我无法理解"jmp_buf"中的内容以及"返回地址"和"堆栈指针"位于"jmp_buf"中的位置.
#include <stdio.h>
#include <setjmp.h>
int main()
{
int i;
jmp_buf env;
i = setjmp(env);
printf("i = %d\n", i);
if (i != 0) return;
longjmp(env, 2);
printf("Does this line get printed?\n");
}
Run Code Online (Sandbox Code Playgroud)
当程序在"printf("i =%d \n",i);"之前的断点处停止时,我尝试了gdb功能:"p/x env"; 但是我在这个包含__jmpbuf和__saved_mask的结构(env)中找不到"返回RIP"和"之前的RSP".任何人都知道这两个函数究竟是如何工作的以及它们在x86_64下用glibc保存的是什么(我使用的是ubuntu 14.04)?
我对setjmp至少在x86_64 linux中究竟做了什么感兴趣,所以我搜索了glibc源代码,但我无法真正找到寄存器保存的位置.你能解释一下这里发生了什么吗?
SETJMP.H
extern int _setjmp (struct __jmp_buf_tag __env[1]) __THROWNL;
#define setjmp(env) _setjmp (env)
Run Code Online (Sandbox Code Playgroud)
BSD-_setjmp.c
int
_setjmp (jmp_buf env)
{
return __sigsetjmp (env, 0);
}
libc_hidden_def (_setjmp)
Run Code Online (Sandbox Code Playgroud)
setjmp.c
int
__libc_sigsetjmp (jmp_buf env, int savemask)
{
__sigjmp_save (env, savemask);
__set_errno (ENOSYS);
return 0;
}
weak_alias (__libc_sigsetjmp, __sigsetjmp)
stub_warning (__sigsetjmp)
Run Code Online (Sandbox Code Playgroud)
sigjmp.c
int
__sigjmp_save (sigjmp_buf env, int savemask)
{
env[0].__mask_was_saved = (savemask &&
__sigprocmask (SIG_BLOCK, (sigset_t *) NULL,
&env[0].__saved_mask) == 0);
return 0;
}
Run Code Online (Sandbox Code Playgroud) 在实现通信协议时,我们有一个编码器,它以递归方式遍历某些结构并将它们编码为二进制消息.
到目前为止一直很好,但现在缓冲区必须分成多个固定大小的块,例如接收缓冲区的大小.因为为完整消息分配内存并因此削减它似乎太浪费了(消息的大小 - 理论上 - 没有限制),现在的想法是用setjmp/longjmp实现协同程序.
目前,我有一个带有两个跳转缓冲区的原型 - 一个用于恢复编码功能的缓冲区,另一个用于模拟函数的返回行为以跳回其调用者.
好吧,它似乎工作,但代码看起来像直接来自地狱.是否存在用于实现可中断递归函数的"约定",可能是一组宏或什么?我想只使用标准化的功能,没有内联asm以便保持便携.
增加:
原型在这里:https://github.com/open62541/open62541/compare/master...chunking_longjmp
'用法'显示在单元测试中.目前,针对非递归函数实现了协程行为Array_encodeBinary.但是,'coroutine'行为应该扩展到UA_encodeBinary这里的一般递归函数:https://github.com/open62541/open62541/blob/master/src/ua_types_encoding_binary.c#L1029
我正在尝试测试我的两个函数,它们模仿setjmp和longjmp用于作业 - 这是非常困难的,因为我们不允许使用内置函数或汇编asm()来实现longjmp和setjmp函数.(是的,那真的是作业.)
问题:我一直收到错误的返回值.所以,简而言之,当main()调用foo()和foo()调用bar(),而bar()调用longjump()时,bar()不应该返回foo(),而是setjmp()应该返回main的返回值为1,应打印"error"(参见下面的main()).
相反,我的输出结果如下:
start foo
start bar
segmentation fault
Run Code Online (Sandbox Code Playgroud)
分段错误,我尝试通过使用malloc初始化指针*p来修复,但似乎没有做任何事情.虽然,分段错误,是我没有得到正确的返回值的原因?
码:
#include <stdio.h>
#include <stdlib.h>
int setjmp(int v);
int longjmp(int v);
int foo(void);
int bar(void);
int *add;
int main(void) {
int r;
r = setjmp(r);
if(r == 0) {
foo();
return(0);
} else {
printf("error\n");
return(2);
}
}
int _main(void) {
return(0);
}
int setjmp(int v)
{
add = &v;
return(0);
}
int longjmp(int v)
{
int *p;
p = &v;
*(p - 1) = …Run Code Online (Sandbox Code Playgroud) setjmp ×10
c ×8
longjmp ×6
c++ ×3
coroutine ×1
debugging ×1
g++4.8 ×1
gdb ×1
memory-leaks ×1
mingw ×1
performance ×1
pointers ×1
recursion ×1
stack ×1
visual-c++ ×1