掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板2

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.28880

longjmpの正しい用い方
投稿者---RiSK(2006/11/15 08:49:18)


みなさんこんにちは。RiSKです。

longjmpの正しい用い方について質問します。
Super Technique 講座〜longjmpと例外 を見ると
void longjump( jmp_buf jmp, int ret );
その後、何かの処理を呼び出す。この時、setjmp() を呼び出した関数から抜けても、あるいは setjmp() の戻り値処理の if文からも、抜けてはならない。
# 強調は私によるもの

とありますが,強調部って正しいのでしょうか?
私は setjmp を呼び出した関数から抜けなければ,
たとえ if から抜けても大丈夫な気がします。

引用したサイトの筆者が正しいのでしたら,その根拠を教えてください。私には見つけられませんでした。
れいによって,規格書JIS X3010 7.13あたりはすでに調べてました。

もし間違っているのでしたら,その指摘と,なぜ筆者がそのような間違いをしたのか推測した点も
教えていただきたいです。

よろしくお願いします。


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:longjmpの正しい用い方 28882 たかぎ 2006/11/15 10:54:05


No.28882

Re:longjmpの正しい用い方
投稿者---たかぎ(2006/11/15 10:54:05)
http://takagi.in/


> setjmp() の戻り値処理の if文からも、抜けてはならない。
>
>とありますが,強調部って正しいのでしょうか?
>私は setjmp を呼び出した関数から抜けなければ,
>たとえ if から抜けても大丈夫な気がします。

間違っていると思います。
(関数ではなく)特定の有効範囲から抜けて動作が未定義になるのは、setjmpマクロを呼び出したのが、可変修飾型(可変長配列を含む型)の識別子の有効範囲だった場合のみです。

>もし間違っているのでしたら,その指摘と,なぜ筆者がそのような間違いをしたのか推測した点も
>教えていただきたいです。

可変修飾型のくだりを誤解したのか、使用していた処理系にバグがあってそれが仕様だと誤認したのか、自動変数にvolatile修飾子を付けていないので(変数の値が不定になった結果)そのように振る舞ったのか、(変数に代入するなど)setjmpマクロの返却値の扱いを間違ったためにそのように振る舞ったのか、誰かからがせネタを吹き込まれたのか、あるいは単なる思いこみかでしょう。
実際のところは本人に聞くしかないと思います。



この投稿にコメントする

削除パスワード

No.28888

Re:longjmpの正しい用い方
投稿者---RiSK(2006/11/15 15:14:22)


>> setjmp() の戻り値処理の if文からも、抜けてはならない。
>>
>>とありますが,強調部って正しいのでしょうか?
>>私は setjmp を呼び出した関数から抜けなければ,
>>たとえ if から抜けても大丈夫な気がします。
>
>間違っていると思います。

ありがとうございます。
自分が間違って覚えていたことが分かってよかったです。


>実際のところは本人に聞くしかないと思います。

はい。メールしてみます。
# longjump って typo もあるし。


この投稿にコメントする

削除パスワード

No.28889

Re:longjmpの正しい用い方
投稿者---かずま(2006/11/15 15:26:53)


> はい。メールしてみます。

次のプログラムが問題なく動くことを反証にしてみてはいかがでしょうか?
#include <stdio.h>
#include <setjmp.h>

jmp_buf jbuf;

int main(void)
{
    int n = setjmp(jbuf);
    if (n)
        printf("setjmp()=%d\n", n);
    while (scanf("%d", &n) == 1)
        longjmp(jbuf, n);
    return 0;
}



この投稿にコメントする

削除パスワード

No.28893

Re:longjmpの正しい用い方
投稿者---たかぎ(2006/11/15 15:56:02)
http://takagi.in/


>次のプログラムが問題なく動くことを反証にしてみてはいかがでしょうか?

たまたま動く場合があるので、反証は難しいと思います。

>    int n = setjmp(jbuf);
setjmpマクロの返却値は、上のように変数の初期化や代入に使うことができません。
また、setjmpマクロを呼び出す関数の中で定義する自動変数は、volatile修飾子を付けておかないと、longjmp関数で戻ってきた後の値が不定になります。



この投稿にコメントする

削除パスワード

No.28897

Re:longjmpの正しい用い方
投稿者---かずま(2006/11/15 17:00:34)


> setjmpマクロの返却値は、上のように変数の初期化や代入に使うことができません。

なるほど、規格書を見てみたら、次の形式でし使えないようですね。
    if (setjmp(jbuf)) ...
    if (!setjmp(jbuf)) ...
    if (setjmp(jbuf) == 0) ...
    setjmp(jbuf);
if の代わりに、switch, while などでもよい。
== の代わりに、!=, < などでもよい。


> また、setjmpマクロを呼び出す関数の中で定義する自動変数は、volatile修飾
> 子を付けておかないと、longjmp関数で戻ってきた後の値が不定になります。

それはちょっと言い過ぎではありませんか?
setjmp呼出しと longjmp呼出しの間で変更された自動変数は不定になりますが、
そうでない自動変数は不定になりません。

規格が不定になるといっているのは、次のような実装の場合を想定している
ものと思われます。

・自動変数がレジスタに割り付けられる。
・setjmp ですべてのレジスタを jmp_buf に保存する。

この場合、setjmp 呼出し後、レジスタ変数を変更しても、longjmp 呼出し時に
jmp_buf 内の元の値に引き戻されるからです。

自動変数がスタックフレーム上にひとつだけあるときは、その変数の変更は
生きていて不定にはならないはずです。

プログラムを自動変数を使わないように修正しました。
#include <stdio.h>
#include <setjmp.h>

jmp_buf jbuf;
int n;

int main(void)
{
    switch (setjmp(jbuf)) {
    case 0: puts("0"); break;
    case 1: puts("1"); break;
    case 2: puts("2"); break;
    case 3: puts("3"); break;
    default: puts("default"); break;
    }
    while (scanf("%d", &n) == 1)
        longjmp(jbuf, n);
    return 0;
}



この投稿にコメントする

削除パスワード

No.28903

Re:longjmpの正しい用い方
投稿者---たかぎ(2006/11/15 18:10:00)
http://takagi.in/


>> また、setjmpマクロを呼び出す関数の中で定義する自動変数は、volatile修飾
>> 子を付けておかないと、longjmp関数で戻ってきた後の値が不定になります。
>
>それはちょっと言い過ぎではありませんか?
>setjmp呼出しと longjmp呼出しの間で変更された自動変数は不定になりますが、
>そうでない自動変数は不定になりません。

そうですね。変更されたものだけが対象です。
しかし、今回の場合、初回のsetjmpマクロの呼び出し結果を使って初期化していますので、ある意味変更されたと考えてもよいのではないでしょうか?
そもそも、返却値を使っての初期化は未定義なので、規格の文章ではその辺のことが考慮されていないだけかと思います。



この投稿にコメントする

削除パスワード

No.28934

Re:longjmpの正しい用い方
投稿者---RiSK(2006/11/17 16:04:15)


放置するのもアレなので。

>はい。メールしてみます。

実は,ここで質問するのとほぼ同時に
この疑問について自分のブログでこぼしていたのですが,
私がメールする前に,ネタ元の杉浦さんが私のブログを見つけ(リファラ経由?),
問題の記事に補足を書いてくださいました。
# 私のブログのコメント欄で紹介してくれました。

その補足にも問題があるような気がしたので,
現在,私のブログの方で問題点を指摘しているところです。

2ヶ所で話を進めたいとは思いませんが,みなさんにも
補足された記事を読んでいただきたいと思います。
# gcc のバグないし規格のバグっぽいのが見え隠れします。
# このスレを使ってみなさんの意見を自由に書いても構いません。
# 私も参考にしたいと思います。

んで,ブログの方で結論が出たらまとめてここに報告したいと思います。


補足された記事
Super Technique 講座〜longjmpと例外


この投稿にコメントする

削除パスワード

No.28935

Re:longjmpの正しい用い方
投稿者---RiSK(2006/11/17 16:16:39)


補足。

># 私も参考にしたいと思います。

私のブログでこのスレッドへのリンクを張っています。
setjump の typo も直っていますし,
杉浦さん自身もこのスレッドの存在は知っていると思います。

ただ,(元々メールでやりとりするつもりでしたし)私と杉浦さんとだけで
話を進めたいと考えていますので,自分のブログへのリンクは今のところ
張るつもりはありませんし,張らないでください。


繰り返しにはなりますが,このスレッドを参考にはしたいので,
みなさんで自由に意見交換することはOKです。ってか,望みます。

よろしくお願いします。


この投稿にコメントする

削除パスワード

No.28945

Re:longjmpの正しい用い方
投稿者---たかぎ(2006/11/18 22:19:25)
http://takagi.in/


大体状況が見えてきました。

JIS X3010:2003 7.13.2.1 longjmp関数では、
すべてのアクセス可能なオブジェクトは, longjmpが呼び出された時の値を保持し,

とあります。この「アクセス可能」という表現をどう解釈するにせよ、既に生存期間を終えたオブジェクトがアクセス不能であることは間違いありませんから、longjmp関数を呼び出してsetjmpマクロのところまで戻ったときに、元の値が保持されていないのは当然ですね。
これは、
char *s = malloc(4);
strcpy(s, "777");
if (setjmp(env) == 0)
{
  printf("%.3s\n", s);
  free(s);
  {
    char *s = malloc(4);
    strcpy(s, "666");
    longjmp(env, 1);
  }
}
else
{
  printf("%.3s\n", s);
}
で、sが"777"から"666"に化けるというのと大差のない話で、if文から抜けるかどうかとは直接関係ないと思います。



この投稿にコメントする

削除パスワード

No.28980

Re:longjmpの正しい用い方
投稿者---RiSK(2006/11/22 01:09:07)


みなさま,お待たせいたしました。


JIS X3010:2003 7.13.2.1 longjmp関数では、
すべてのアクセス可能なオブジェクトは, longjmpが呼び出された時の値を保持し,
とあります。この「アクセス可能」という表現をどう解釈するにせよ、既に生存期間を終えたオブジェクトがアクセス不能であることは間違いありませんから、longjmp関数を呼び出してsetjmpマクロのところまで戻ったときに、元の値が保持されていないのは当然ですね。

あーなるほど。こっちの方が分かりやすいですね。

私は x がブロックを抜けて,不定になるので
「longjmp呼出しの間に変更された場合に不定となる」の
「変更」に引っかかると思い,その点を指摘していました。
で,杉浦さんも更新したページの中でその点について触れてくれました。

結局「setjmp() の戻り値処理の if文からも、抜けてはならない。」は
偽ということで落ち着きそうです。
# 問題の記述は残ったままですが。


どうやら,杉浦さんはifブロックの外でlongjmpを呼ばない方がいい
というスタンスを崩すつもりはないようです。
そして,その点について私はそれはそれで構わないと思いますので,
この件はこれにて終了としたいと思います。


また,みなさんの意見はすべて参考にさせていただきましたし,
かずまさんの反証コードやたかぎさんの考察した点は
私のブログで引用させていただきました。ありがとうございました。


最後に,「setjmp() の戻り値処理の if文からも、抜け」ない方がいい,の
根拠というかアンチパターンみたいなものを,
杉浦さんがさらに追記してますのでこの記事も見ることをおすすめします。
なかなか,おもしろいですよ。
補追2: 伝説の COME FROM


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧