3

私はCを初めて使用し、ポインターを介して構造体のメンバーを参照したときに得られる結果に混乱しています。例については、次のコードを参照してください。初めてtst->numberを参照するとどうなりますか?私がここで見逃している基本的なことは何ですか?

#include <stdio.h>
#include <stdlib.h>

typedef struct {
   int number;
} Test;

Test* Test_New(Test t,int number) {
    t.number = number;
    return &t;
}    

int main(int argc, char** argv) {    
    Test test;
    Test *tst = Test_New(test,10);
    printf("Test.number = %d\n",tst->number);
    printf("Test.number = %d\n",tst->number);
    printf("Test.number = %d\n",tst->number);
}

出力は次のとおりです。

Test.number = 10
Test.number = 4206602
Test.number = 4206602
4

8 に答える 8

14

テストを Test_New 関数に渡すときは、それを値で渡しているため、Test_New 関数の関数スコープのスタックにローカル コピーが作成されます。変数のアドレスを返すため、関数がスタックを返すと、スタックは役に立たなくなりますが、古いスタックの構造体へのポインタが返されます! したがって、スタック値は何も上書きされていないため、最初の呼び出しが正しい値を返すことがわかりますが、後続の呼び出し (すべてスタックを使用する) は値を上書きし、誤った結果をもたらします。

これを行うには、Test_New 関数を正しく書き直して、ポインターを取得し、構造体へのポインターを関数に渡します。

Test* Test_New(Test * t,int number) {
    t->number = number;
    return t;
}

int main(int argc, char ** argv)  {
   Test test;
   Test * tst = Test_New(&test,10);

   printf("Test.number = %d\n",tst->number);
   printf("Test.number = %d\n",tst->number);
   printf("Test.number = %d\n",tst->number);

}
于 2009-01-01T22:18:22.733 に答える
3

とは関係なく、ローカル変数のアドレスを返すstructことは常に正しくありません。通常、ローカル変数のアドレスをグローバル変数に入れたり、ヒープに割り当てられたオブジェクトに格納したりすることも正しくありませんmalloc。一般に、オブジェクトへのポインターを返す必要がある場合は、他の誰かにポインターを提供してもらうか、ポインターmallocを返すスペースを割り当てる必要があります。その場合、関数のAPIの一部でfree、オブジェクトが不要になったときに呼び出す責任者を指定する必要があります。

于 2009-01-02T05:01:44.900 に答える
1

メソッドに渡したアドレスではなく、tメソッドで宣言されたアドレスを返します。つまり、値によって渡されているので、代わりにポインタを渡す必要があります。Test_Newtesttest

だから、これがあなたが電話したときに何が起こるかですTest_New。名前の付いた新しいTest構造体tが作成され、 (初期化していない)t.numberの値と等しくなるように設定されます。test.number次に、メソッドに渡しt.numberたパラメーターに等しい値を設定してから、のアドレスを返します。ただし、これはローカル変数であり、メソッドが終了するとすぐにスコープから外れます。したがって、存在しなくなったデータへのポインタを返すことになり、それがゴミになってしまう理由です。numbertt

の宣言をTest_Newに変更します

Test* Test_New(Test* t,int number) {
    t->number = number;
    return t;
}

経由してそれを呼び出す

Test *tst = Test_New(&test,10);

そして、すべてがあなたが期待しているように行きます。

于 2009-01-01T22:15:32.503 に答える
1

問題は、参照を に渡していないことTest_Newです。値を渡しています。次に、ローカル変数のメモリ位置を返しています。問題を示す次のコードを検討してください。

#include <stdio.h>

typedef struct {
} Test;

void print_pass_by_value_memory(Test t) {
  printf("%p\n", &t);
}

int main(int argc, char** argv) {
  Test test;
  printf("%p\n", &test);
  print_pass_by_value_memory(test);

  return 0;
}

私のマシンでのこのプログラムの出力は次のとおりです。

0xbfffe970
0xbfffe950
于 2009-01-01T22:20:34.770 に答える
1

BlodBathの答えを拡張するために、これを行うとメモリ内で何が起こるか考えてみてください。

メイン ルーチンに入ると、auto であるため、新しい自動 Test 構造体がスタック上に作成されます。したがって、スタックは次のようになります

    | | メインのリターンアドレス | 一番下に使用します
    | | argc | 環境からスタックにコピー
    | | argv アドレス | 環境からスタックにコピー
-> | テスト番号 | 定義によって作成された テスト テスト;

->スタックの最後に使用された要素へのスタック ポインターを示します。

を呼び出すと、次Test_new()のようにスタックが更新されます。

    | | メインのリターンアドレス | 一番下に使用します
    | | argc | 環境からスタックにコピー
    | | argv アドレス | 環境からスタックにコピー
    | | テスト番号 | 定義によって作成された テスト テスト;
    | | Test_new のアドレスを返します| 一番下に戻っていた
    | | test.number | のコピー C は常に値渡しを使用するため、スタックにコピーされます
-> | 10 | スタックにコピー

あなたが戻ってきたとき、あなた&tはどの住所を取得していますか? 答え: スタック上のデータのアドレス。しかし、あなたが戻ると、スタックポインタはデクリメントされます。を呼び出すとprintf、スタック上のこれらの単語が再利用されますが、アドレスはまだそれらを指しています。アドレスとして解釈されたスタック内のその場所の数値が指し示す値が 4206602 であることが起こりますが、それはまったくの偶然です。運が良ければセグメンテーション違反が発生し、何かが実際に壊れていることがわかります。

于 2009-01-01T22:33:37.133 に答える
1

Test_New()で宣言されたTest tはローカル変数です。ローカル変数のアドレスを返そうとしています。関数が存在するとローカル変数が破棄されるため、メモリが解放されます。つまり、コンパイラは、ローカル変数が保持されていた場所に他の値を自由に配置できます。

プログラムで 2 回目に値にアクセスしようとすると、メモリの場所が別の変数またはプロセスに割り当てられている可能性があります。したがって、間違った出力が得られます。

値ではなく参照によって main() から構造体を渡すことをお勧めします。

于 2009-01-02T10:21:42.867 に答える
0

test の内容を値渡しで Test_New に渡しました。IOW Test_New を呼び出したときに、Test 構造体の新しいコピーがスタックに割り当てられました。関数から返されるのは、この Test のアドレスです。

tst->number を初めて使用すると、そのスタックは巻き戻されていますが、そのメモリは他に使用されていないため、値 10 が取得されます。ただし、最初の printf が呼び出されるとすぐに、スタック メモリは必要なものに再利用されますが、tst はまだそのメモリを指しています。したがって、その後の tst->number の使用は、printf がそのメモリに残したものを取得します。

代わりに、関数シグネチャで Test &t を使用してください。

于 2009-01-01T22:22:46.547 に答える
0

少し簡単にするために、次のようなことを行うことができます。

typedef struct test {
   int number;
} test_t;

test_t * Test_New(int num)
{
   struct test *ptr;

   ptr = (void *) malloc(sizeof(struct test));
   if (! ptr) {
     printf("Out of memory!\n");
     return (void *) NULL;
   }

   ptr->number = num;

   return ptr;
}

void cleanup(test_t *ptr)
{
    if (ptr)
     free(ptr);
}

....

int main(void)
{
    test_t *test, *test1, *test2;

    test = Test_New(10);
    test1 = Test_New(20);
    test2 = Test_new(30);

    printf(
        "Test (number) = %d\n"
        "Test1 (number) = %d\n"
        "Test2 (number) = %d\n",
        test->number, test1->number, test2->number);
    ....

    cleanup(test1);
    cleanup(test2);
    cleanup(test3);

    return 0;
}

... ご覧のとおり、test_t のいくつかの完全に異なるインスタンスに余裕を簡単に割り当てることができます。たとえば、後で元に戻せるように既存の状態を保存する必要がある場合などです。

もちろん、それをローカルに保持しなければならない何らかの理由がある場合を除きます..しかし、私は本当にそれを考えることができません.

于 2009-01-02T05:57:22.417 に答える