間違いだと思います。
addressee を内部的に次のような形に変形し、
void addressee(struct X *r) {
struct X result = { "world" };
*r = result;
}
呼び出し側が代入先オブジェクトへのポインタをこっそり渡せば
きちんとスタック内で完結するコードを生成可能だからです。
実際 cygwin の gcc (GCC) 4.3.4 20090804 (release) 1 はそのようなコードを生成しました。
問題は呼び出し側がどう一時変数を用意するかという点にあります。
例えば次のような関数呼び出しがあったとき、
void foo(void)
{
printf("%s%s", addressee().a, addressee().a);
}
gcc拡張の書き方になりますが、次のような形に変換されるかもしれませんし、
void foo(void)
{
/* gcc では ({ 文* }) という書き方で 文を式に変換できます。
式の値は内部の最後の式の値です。*/
printf("%s%s",
({ struct X tmp1; addressee(&tmp1); tmp1.a; }),
({ struct X tmp2; addressee(&tmp2); tmp2.a; }));
}
次のように変換されるかもしれません。
void foo(void)
{
struct X tmp1;
struct X tmp2;
addressee(&tmp1);
addressee(&tmp2);
printf("%s%s", tmp1.a, tmp2.a);
}
前者のやり方では printf が呼び出される前に一時変数の寿命が尽きますが、
後者のやり方ではfoo()の終わりまで一時変数は生きています。
Cコンパイラはどちらの変換を行ってもかまいません(未定義ですから)。
適合コードに関しては
struct X my_x = addressee();
を
struct X my_x;
addressee(&my_x);
と変換するようなCコンパイラなら割り込みが入っても問題なく実行できます。