16

関数から返された構造体のメンバーを出力するのに問題があります。

#include <stdio.h>

struct hex_string
{
    char a[9];
};

struct hex_string to_hex_string_(unsigned x)
{
    static const char hex_digits[] = "0123456789ABCDEF";
    struct hex_string result;
    char * p = result.a;
    int i;
    for (i = 28; i >= 0; i -= 4)
    {
        *p++ = hex_digits[(x >> i) & 15];
    }
    *p = 0;
    printf("%s\n", result.a);   /* works */
    return result;
}

void test_hex(void)
{
    printf("%s\n", to_hex_string_(12345).a);   /* crashes */
}

内部のprintf呼び出しto_hex_string_は正しい結果を出力しますが、printf内部の呼び出しはtest_hex私のプログラムをクラッシュさせます。それはなぜですか?それは一生の問題ですか、それとも別の問題ですか?

printf呼び出しをに置き換えるとputs(to_hex_string_(12345).a)、コンパイラ エラーが発生します。

invalid use of non-lvalue array

何が起きてる?

4

3 に答える 3

18

C には、めったに有効にならない規則があります。

関数呼び出しの結果を変更しようとしたり、次のシーケンス ポイントの後にアクセスしようとした場合の動作は未定義です。(C99 §6.5.2.2)

この場合、引数printf()が評価された後、printf()関数自体が実行される前に、シーケンス ポイントがあります。渡すポインターは、戻り値自体の要素へのポインターですprintf()そのprintf()ポインターを介して文字列にアクセスしようとすると、クラッシュします。

関数の値は左辺値ではないため、この問題に遭遇するのは困難です&

于 2011-11-01T08:19:59.867 に答える
13

あなたは言語のかなりあいまいなコーナーケースに遭遇することができました.

配列型の式は、ほとんどのコンテキストで、配列の最初の要素へのポインターに暗黙的に変換されます。例外は、式が単項演算子のオペランドである&場合、単項演算子のオペランドであるsizeof場合、および配列オブジェクトの初期化に使用される初期化子の文字列リテラルである場合です。ここでは、これらの例外は適用されません。

しかし、その変換には暗黙の前提があります。ポインターは配列objectの最初の要素です。

ほとんどの配列式 (実際にはほとんどすべて) は、宣言された配列変数、多次元配列の要素など、何らかの配列オブジェクトを参照します。関数は配列を返すことができないため、左辺値以外の配列式を取得することはできません。

しかし、ご覧のとおり、関数は配列を含む構造体を返すことができ、配列式に関連付けられたオブジェクトはありませんto_hex_string_(12345).a

新しい ISO C11 標準では、保存期間を説明するセクションに新しい文言を追加することで、この問題に対処しています。 N1570 ドラフトのセクション 6.2.4p8 には、次のように記載されています。

構造体または共用体型の非左辺値式で、構造体または共用体に配列型のメンバーが含まれている場合 (含まれているすべての構造体および共用体のメンバーを再帰的に含む) は、自動ストレージ期間と一時的な有効期間を持つオブジェクトを参照します。その有効期間は式が評価されたときに始まり、その初期値は式の値です。含まれている完全な式または完全な宣言子の評価が終了すると、その有効期間は終了します。一時的な有効期間を持つオブジェクトを変更しようとすると、未定義の動作が発生します。

実際には、これは関数から返される値 (ほとんどの関数の結果とは異なります) が一時的なオブジェクトの値であることを示しており、その配列メンバーの減衰によって (一時的に) 有効なポインターが得られます。

しかし、コンパイラが新しい C 標準を完全にサポートするまで (数年はサポートされません)、返された構造体の配列メンバーを参照することは避ける必要があります。

于 2011-11-01T08:07:31.187 に答える
-1

あなたが直面している問題は次のとおりです。result返される変数は関数_to_hex_stringのローカル変数です。つまり、関数呼び出しの最後に削除されます。そのため、関数で確認しようとすると、test_hexもう利用できません。

あなたの問題を解決するには、ポインターを扱うことができます。

ここにコードの変更があります

struct hex_string
{
    char a[9];
};

struct hex_string * to_hex_string_(unsigned x) // here you return a pointer
{
    static const char hex_digits[] = "0123456789ABCDEF";
    struct hex_string result;

    result = (struct hex_string *) malloc(sizeof(struct hex_string));
    char * p = result->a;
    int i;

    for (i = 28; i >= 0; i -= 4)
    {
        *p++ = hex_digits[(x >> i) & 15];
    }

    *p = 0;
    printf("%s\n", result->a);   /* works */
    return result;
}

void test_hex(void)
{
    printf("%s\n", to_hex_string_(12345)->a);  /* works */
}

良い一日を

于 2011-11-01T10:37:17.517 に答える