0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GCC拡張のcleanup属性は大域脱出と共存できない【俺を信じろ】

Last updated at Posted at 2020-10-19

GCC拡張のcleanup属性とは?

C言語でC++のRAII的なことができる。参照

大域脱出(nonlocal exit)とは?

C言語では、setjmp() / longjmp()、略してsjljのこと。参照

大域(non-local)とはどういう意味?

コールスタックのフレームひとつが「ローカル」と考える。フレームにはローカル変数が入ってるし。

普通はreturnで一つ根本方向のフレームに戻るだけだが、大域脱出では一つと言わず、いくつでも戻れる(戻らなくてもいい)。ゆえにnon-local。

コールスタックの根本方向に向かうということは、sjljは例外に似ている?

ある意味では似ている。致命的な違いは、スタックの巻き戻しを行わないこと。

行わせる方法はない。抜け道はない。俺を信じろ。

共存できないとは?

# include <stdio.h>
# include <setjmp.h>

# define _cleanup_(x) __attribute__((cleanup(x)))
# define _cleanup_free_ _cleanup_(freep)

static jmp_buf buf;

static void freep(void *p) {
    printf("free!\n");
    // free(*(void**) p);
}

static void foo(int jmp) {
    _cleanup_free_ char *c_auto_free = NULL;
    printf("Hello\n");
    if (jmp) {
        longjmp(buf, 1);
    }
}

int main() {
    foo(0);

    if (!setjmp(buf)) {
        foo(1);
    }

    printf("Out\n");
    return 0;
}

出力:

Hello
free!
Hello
Out

longjmp()でmain()に戻ったときには、freep()が実行されない。sjljはスタックの巻き戻しを行わないのでこうなる。

sjlj以外の方法は?

GCCのC言語には、sjlj以外の大域脱出の方法がない。ないったらない。俺を信じろ。

GCC拡張にはcleanup属性があるのに、なぜスタックの巻き戻しを行う大域脱出の方法がないのか? わからない。とにかく、ないものはない。俺を信じろ。

GCC以外なら?

WindowsのC言語には構造化例外(SEH)がある。もちろんMSVC限定、かと思ったら最近のMingwでも使えるらしい。

まとめ

これは、「プログラミング言語はみんな同じようなもの」と思ったら深刻に大間違いなケースのひとつ。大多数の言語でものすごく頻繁に使われている制御構造2つを同時には使えないのだ。「C言語(GCC)にはこういう制約がある」とよくよく体に染み込ませた上で設計しないと、大域脱出とスタックの巻き戻しの両方をうっかり使ってしまう。

そんな鍛え方をしたくない? 人がいない? じゃあC言語、もうやめたほうがいいな?

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?