1

非常に基本的な質問があり、助けが必要です。動的に割り当てられたメモリ (ヒープ上) の範囲を理解しようとしています。

#include <stdio.h>
#include <malloc.h>

//-----Struct def-------
struct node {
        int x;
        int y;
};

//------GLOBAL DATA------

//-----FUNC DEFINITION----
void funct(){
  t->x = 5; //**can I access 't' allocated on heap in main over here ?**
  t->y = 6; //**can I access 't' allocated on heap in main over here ?**
  printf ("int x = %d\n", t->x);
  printf ("int y = %d\n", t->y);
  return;
}

//-----MAIN FUNCTION------
int main(void){
      struct node * t = NULL;// and what difference will it make if I define 
                                 //it outside main() instead- as a global pointer variable
          t = (struct node *) malloc (sizeof(struct node));
      t->x = 7;
      t->y = 12;
      printf ("int x = %d\n", t->x);
      printf ("int y = %d\n", t->y);

    funct(); // FUNCTION CALLED**
    return 0;
}

ここで、引数(へのポインタ)を渡さずにメモリが割り当てられていても、構造体にアクセスできますか?tヒープはスレッドに共通なので? グローバル変数として外部で定義すると、どのような違いがあり、何か問題がありますか?funct()main()tfunction functstruct node * t = NULLmain()

4

10 に答える 10

8

malloc() を使用すると、malloc() によって返されるポインターを持つ変数が表示されると仮定すると、それによって返されるメモリにはコード内のどこからでもアクセスできます。

したがって、コードで t がグローバルである場合、main と funct() で表示され、両方で使用できます。

現状では、以前の回答が述べたように、funct() は t が何であるかわかりません。t の宣言と定義は main; にあるためです。t は機能の範囲外です。functがtが何であるかを知っていれ、 tに割り当てたメモリはfunctで使用できます。

于 2009-03-28T11:40:35.360 に答える
3

変数にアクセスするには、関数はこの変数のアドレス ("t" ポインターの値) を知る必要があります。したがって、それを関数に渡す必要があります。そうしないと、関数は何にアクセスするかを知る手段がありません。

グローバル変数 pinter "t" を宣言し、それを任意の関数から使用することもできますが、これも明示的に行うことができます。

于 2009-03-28T11:36:58.130 に答える
3

いいえ、そのように t にアクセスすることはできません。main でグローバルにアクセス可能なヒープ上の何かを指すように t を作成したとしても、変数 t 自体は main に対してローカルな (そしてスタック上にある) ポインターであるためです。 )。

于 2009-03-28T11:36:53.217 に答える
2

割り当てられたメモリにはアクセスできますが、それは問題ではありません。変数 't' は、宣言した場所であるため、main内でのみ表示されます。これらは、理解する必要がある 2 つの異なる概念です。データのストレージは、それを参照する変数と同じではありません。

于 2009-03-28T11:37:35.213 に答える
1

いいえ、t をグローバルにするか、ポインタを funct() に渡す必要があります。2 番目の方法をお勧めします。現在、t は main() のみのスコープにあります。

一度に t で複数のスレッドを操作する予定がある場合は、構造体にミューテックスを追加することも検討してください。これを行う場合は、mlock() を使用して、構造体がページ アウトされたり、ロックが遅くなったりしないようにすることも検討してください。

于 2009-03-28T11:40:17.810 に答える
1

そのメモリへのポインタを受け入れるように関数を変更するだけです。

/* changing the variable name to make it clear that p is scoped to funct() */
/* when it's passed, just as t is local to main() */
void funct(struct node *p){
p->x = 5;
p->y = 6;
printf ("int x = %d\n", p->x);
printf ("int y = %d\n", p->y);
return;
}

...

funct(t);

/* now free the memory you allocated */
free(t);
return 0;

これが、ヒープにメモリを割り当てる要点です。自動 (スタック) 変数は、それらを宣言するブロックのスコープに限定されます。ヒープ メモリはどこでも使用できます。ポインタを渡すだけで、他の関数がどこにあるかがわかります。保存したものを見つけます。

于 2009-03-28T11:45:10.980 に答える
1

受け入れられた答えは少し誤解を招くものです。

malloc() を使用すると、malloc() によって返されるポインターを持つ変数が表示されると仮定すると、それによって返されるメモリにはコード内のどこからでもアクセスできます。

これは、ヒープ、スタック、静的のいずれであっても、C または C++ で割り当てられたすべてのメモリに等しく当てはまります。& をローカル変数の前に置くだけで、プログラムの他の部分がその変数のストレージに到達し、変数名自体を見ることができるかのように効果的に使用できるようになります。

ローカル変数名は、それらが宣言されているスコープ内でのみ表示されますが、そのストレージは、アドレスが取得されて渡されていると仮定して、任意のスレッドのどこからでもアクセスできます。

C/C++ でローカル変数のアドレスを取得することは危険ですが、頻繁に必要となるため、残念ながら、これを理解せずにこれらの言語を効果的に使用することは不可能です。

アップデート

@kevinf 言います:

ローカルのスコープ内でローカルのアドレスを使用することは問題ありませんが、あなたの提案はメモリセーフではありません

スコープライフタイムを区別することが重要です。スコープは識別子 (変数名など) を指し、コード内で識別子を指定できる場所のセットです。これはコンパイル時の概念です。

これは、メモリの安全性、またはストレージの場所にあるオブジェクトの寿命とは別の概念です。これはランタイムの概念です。例えば:

void g(int *p)
{
    (*p)++;
}

void f()
{
    int n = 1;
    g(&n);
}

ここで、識別子nは 内でのみ有効ですf。の実行中に存在する保存場所に名前を付けますf。その保管場所のアドレスを取得し、&nそれを に渡しますgg名前nを直接使用できないことに注意してください。nここでは範囲外です! ただし、この例fではまだ未完成 (完了を待機中) であるため、保管場所は完全に使用可能gです。

ほとんどの実際の C および C++ プログラムはこれを頻繁に行い、標準で許可されています。一般的に安全でもなく、一般的に安全でもありません。安全な場合もあれば、そうでない場合もあります。これは、C および C++ の主要な課題です。未定義の動作は、関数のローカル検査から常に検出できるとは限りません。より広い文脈でそれがどのように使用されているかを知る必要があります。

言語への最近の変更 (g++ では、-std=c++14これをコンパイルするオプションを使用) は、この灰色の領域を調査する別の方法を導入しています。

auto f()
{
    int n = 1;

    auto g = [&] 
    { 
        ++n; 
        cout << "Incremented to " << n << endl;
    };

    g();

    return g;
}

void h()
{
    auto g = f();

    cout << "Returned into h" << endl;

    g();
}

の内側fnスコープ内です。Andgはラムダのインスタンスを保持し、その本体も のスコープの一部ですng内部への呼び出しfはまったく問題ありません。しかしf、ラムダを という別の変数に格納するとg、それ以降の呼び出しは許可されません! を使用せずに、&n使用できなくなった保存場所を暗黙的にキャプチャしました。これは、その有効期間が への呼び出しの期間に制限されていたためfです。

ちなみに、許可されていないと言うと、コンパイルされます。しかし、私のシステムでは次のように出力されます。

Incremented to 2
Returned into h
Incremented to 167772162

これは、未定義の動作を明確に示しています。

于 2009-03-28T11:59:01.603 に答える
1

コードでは、「t」は main() のローカル変数です。他の場所からはアクセスできません。これはスレッド化とは関係ありません。コードを修正してください。

于 2009-03-28T11:36:21.097 に答える
0

t は main() に対してローカルなポインター変数ですが、「t」内のコンテンツは、明示的に解放するまで/解放しない限り、プログラム全体に存在します。t が保持するアドレスの有効期間は、動的に割り当てられるため、プログラム全体ですそのため、プログラム内のどこでもそのメモリアドレスを使用できますが、変数ローカル変数 't' は使用できません.t はローカル変数であるため、そのブロックスコープのみを持ち、メインでのみ使用できます()>したがって、t とその内容を利用する最善の方法は、t を宣言し、グローバル スコープでメモリを動的に割り当てることです。

つまり、変数 't' はローカルですが、't' の値はヒープにあります。ヒープ メモリにアクセスする唯一の方法は、't' を使用することです。したがって、t をグローバル空間で宣言すると、 「t」を介してプログラムします。それ以外の場合、tが範囲外になったときにヒープメモリにアクセスする方法

于 2013-02-08T08:27:36.560 に答える
0

t は、関数 main で定義された構造体へのポインターであるためです。funct() でアクセスすることはできません。t をグローバルにして funct() でアクセスできるようにするか、ポインターを引数として funct() に渡します。グローバルにすることは最良の選択ではありません。マルチスレッド環境では、複数のスレッドがアクセスしようとする可能性があるため、同時にアクセスを避けるために何らかのロックメカニズムが必要です。ロック機能を追加すると、他のオーバーヘッドが発生する可能性があるため、最良の選択はそれを引数として渡すことです。

于 2009-03-29T07:44:23.377 に答える