直接C程序中任何错误处理的好习惯用法?

Wil*_*ung 29 c error-handling

回到一些C工作.

我的许多功能看起来像这样:

int err = do_something(arg1, arg2, arg3, &result);
Run Code Online (Sandbox Code Playgroud)

意图结果由函数填充,返回值是调用的状态.

黑暗面是你得到这样的天真:

int err = func1(...);
if (!err) {
    err = func2(...);
    if (!err) {
        err = func3(...);
    }
}
return err;
Run Code Online (Sandbox Code Playgroud)

我想我可以宏观:

#define ERR(x) if (!err) { err = (x) }
int err = 0;
ERR(func1(...));
ERR(func2(...));
ERR(func3(...));
return err;
Run Code Online (Sandbox Code Playgroud)

但这只有在我连接函数调用时才有效,而不是做其他工作.

显然,Java,C#,C++有一些例外,可以很好地处理这些事情.

我只是好奇其他人做了什么,以及其他人如何在他们的C程序中进行错误处理.

Jud*_*den 32

如果你有资源需要在最后发布,那么有时老信托goto可以派上用场!

int
major_func(size_t len)
{
    int err;
    char *buf;

    buf = malloc(len);

    if (err = minor_func1(buf))
        goto major_func_end;
    if (err = minor_func2(buf))
        goto major_func_end;
    if (err = minor_func3(buf))
        goto major_func_end;

major_func_end:
    free(buf);
    return err;
}
Run Code Online (Sandbox Code Playgroud)

  • 我看过的许多开源代码库都使用了"转到资源集合上的错误"的成语.这是一个受其他诽谤指令闪耀的地方. (8认同)
  • +1用于处理资源清理的现实世界现实 (6认同)
  • 积极使用`goto`的+1.那么根据需要清理多少goto标签的另一个例子(反向顺序)呢?我一直用那个成语. (4认同)
  • 这可以通过使用return(CleanupOnError(err,buf))在没有goto的情况下完成; (该方法包含您在goto中的代码).除了避免goto,你有时可以从多个处理函数中使用相同的清理函数(只要它们当然需要相同的清理) (2认同)
  • @Jason Williams:您可以在没有控制流的情况下编写整个程序,但这也不是一个好主意。在这种情况下使用 return 几乎没有那么糟糕,但没有理由不在这里使用 `goto`。 (2认同)

egr*_*nin 16

两种典型模式:

int major_func()
{
    int err = 0;

    if (err = minor_func1()) return err;
    if (err = minor_func2()) return err;
    if (err = minor_func3()) return err;

    return 0;
}

int other_idea()
{
    int err = minor_func1();
    if (!err)
        err = minor_func2();
    if (!err)
        err = minor_func3();
    return err;            
}

void main_func()
{
    int err = major_func();
    if (err)
    {
        show_err();
        return;
    }
    happy_happy_joy_joy();

    err = other_idea();
    if (err)
    {
        show_err();
        return;
    }
    happy_happy_joy_joy();
}
Run Code Online (Sandbox Code Playgroud)

  • +1:我个人会选择major_func()中显示的实现.许多人厌恶使用多个return语句编写代码,但这是局部性原则应该覆盖这些问题的一种情况.PS.happy_happy_joy_joy()的实现在哪里? (5认同)
  • @torak我认为人们普遍认为happy_happy_joy_joy()的实现是留给读者的练习. (4认同)
  • 在返回之前必须释放资源时,`other_idea`最好(例如malloc/free或mutex lock/unlock).否则,我也更喜欢`major_func`.但是,我必须承认我有时会使用`major_func`加上一个`goto`.;) (4认同)
  • `happy`之前'happy _...`似乎很肤浅. (3认同)

Aar*_*ron 8

你在else发言中做了什么?如果没有,试试这个:

int err = func1(...);
if (err) {
    return err;
}

err = func2(...);
if (err) {
    return err;
}

err = func3(...);

return err;
Run Code Online (Sandbox Code Playgroud)

这样你就可以使整个功能短路,甚至不会打扰下面的函数调用.

编辑

回过头再读一遍,我意识到你在else陈述中所做的并不重要.这些代码可以很容易地在if块之后立即进行.


lhf*_*lhf 6

如果错误代码是布尔值,那么尝试下面的简单代码:

return func1() && func2() && func3()
Run Code Online (Sandbox Code Playgroud)


Nic*_*unt 5

OpenGL 采取的一种方法是根本不从函数返回错误,而是呈现可以在函数调用后检查的错误状态。这种方法的一个好处是,当您有一个函数实际上想要返回错误代码以外的内容时,您可以以相同的方式处理错误。另一个好处是,如果用户想要调用多个函数并且只有在所有函数都成功时才成功,您可以在 x 次调用后检查错误。

/* call a number of functions which may error.. */
glMatrixMode(GL_MODELVIEW);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);

/* ...check for errors */
if ((error = glGetError()) != GL_NO_ERROR) {
    if (error == GL_INVALID_VALUE)
        printf("error: invalid value creating view");
    else if (error == GL_INVALID_OPERATION)
        printf("error: invalid operation creating view");
    else if (error == GL_OUT_OF_MEMORY)
        printf("error: out of memory creating view");
}
Run Code Online (Sandbox Code Playgroud)

  • -1 用于推荐全局状态/全局变量。如果您的 API 有一个对象/上下文结构来保持错误状态,那么这种方法非常好。但是如果你不得不求助于一个全局变量,你有各种各样的噩梦需要处理:(1)线程安全,(2)当你试图使它成为线程安全时的性能/可移植性,(3)问题动态加载的模块、全局变量和可能相关的内存泄漏,.... (2认同)