0

以下のヘッダーファイルの場合、本体内に「a」が定義されている場合、「未使用の変数「a」」という警告とリンカエラー「「a」への未定義参照です。

header.h:

#ifndef HEADER_H
#define HEADER_H

#include <iostream>

extern int* a;

void f()
{
    std::cout<<*a <<std::endl;
    return;
}

#endif

main.cpp:

#include "header.h"

int main()
{
    int* a = new int(10);

    f();
}

ただし、'a' が main() の外で定義されている場合、プログラムはエラーなしでリンクし、f() は期待どおりに動作します (10 を出力します)。どうしてこれなの?

例:

int* a = new int(10);

int main()
{
    f();
}
4

6 に答える 6

3
int* a = new int(10);

この行では、メイン関数でローカル変数を定義している場合。

したがって、extern int* a;変数を宣言するだけで、定義はしません。次に、そのシンボルでリンケージエラーを取得します

于 2012-06-01T05:33:31.263 に答える
2

同じ名前の2つの宣言がどのように関連しているかを決定する名前バインディングについて学ぶ必要があります。関数内で変数を定義する場合、その名前にはリンクがありません。つまり、それが参照するエンティティは、プログラム内の他のエンティティとは異なります。

より一般的には、宣言(この意味では、定義は宣言でもあります)は、シンボルをエンティティ(オブジェクト(この場合、宣言は変数を宣言します)、関数、参照、型など)に関連付けます。 C++で宣言できます。同じ名前の異なる宣言が同じエンティティに関連付けられているかどうかは、それらのリンケージによって定義されます。C ++は、次の3種類のリンケージを認識します。

  • エンティティが他の変換ユニットの宣言によって参照できる外部リンケージ

  • 同じ翻訳単位内の他の宣言によってエンティティを参照できる内部リンケージ

  • エンティティが他の宣言によって参照されないリンケージはありません

ブロックスコープで宣言された変数(つまり、ローカル変数)は、明示的に宣言されていない限り(つまり、宣言されexternたローカル変数externを定義にすることはできません)、リンクはありません。したがって、int ain は、プログラム内mainの他のエンティティから独立しているエンティティを宣言(および定義)しますa。名前空間スコープで宣言された変数は、宣言されていない限り、外部リンケージを持ちます。宣言さstaticれている場合は、内部リンケージがあります。int a名前空間スコープで 定義すると、外部リンケージがあるためextern int a、ヘッダーで宣言したのと同じエンティティを参照します。

于 2012-06-01T07:47:03.950 に答える
2

何が間違っているかを説明する4つの回答を読むのはかなり面倒ですが、それを修正する正しい方法を説明するものはありません. OPがスコープについて知らない場合、変数を関数に渡すことについてもおそらく知らないというのは、おそらく安全な推測です。

問題

変数の値を取得しようとしていますが、変数は別の関数にあります。どうすればそれを手に入れることができますか?まあ、簡単な答えは、あなたはそれを手に入れたくないということです. あなたは私を正しく聞いた。関数を使用するすべての理由は再利用性です。新しく作成した関数を別の関数に関連付けると、どこでも使用できなくなります。関数は怠惰になるのに役立つことを忘れないでください。そして、優れたプログラマーは怠惰なプログラマーです。関数を 1 回記述して、100 万の場所で使用できれば、それは正しいことです。;)

しかし、私はまだその変数の値を取得したい

次に、関数パラメーターを使用して変数を関数に渡します。

関数は、数学の観点から考えることができるため、名前が付けられています。変数を入れて、関数の実行後に有用なデータを取得し、それらの変数で興味深いことを行います。したがって、数学関数があるとしましょy = f(x)う。これと同等のものは、変数または数値の場所int f(int x) { /*stuff here*/ }を使用してメイン関数で呼び出すことです。int y = f(a)a

グローバル変数は常に期待どおりに機能するとは限らないため、避けたいと考えています (特にコードが多い場合は、誤って同じ名前を使用してしまう可能性が非常に高くなります)。

あなたの場合、関数で特定の変数の内容を出力したいので、おそらく特定の変数でその関数を使用する方法を探していると思います。だからここにあなたがそれを行う方法があります。

void f(); //hi, I'm a function prototype
void f(int a); //hi, I'm a function prototype that takes a parameter
void f(int a, int b); //hi, I'm a function prototype that takes two parameters (both ints)
void f(int a, ...); //hi, I'm a function prototype that takes an int then any number of extra parameters (this is how printf works.)

したがって、実際にやりたいことは、コードを次のように変更することです。

header.h:

#ifndef HEADER_H
#define HEADER_H

#include <iostream>

// extern int* a; // We don't need this

void f(int* a)
{
  if (a != NULL) //always, always check to make sure a pointer isn't null (segfaults aren't fun)
    std::cout<<*a <<std::endl;
    //return;  //Don't really need this for a function declared void.
}

#endif

main.cpp:

#include "header.h"

int main()
{
    int* a = new int(10);

    f(a);
    return 0; //main is declared as returning an int, so you should.
}

値、ポインター、および参照による関数

したがって、私が与えたあなたの例では、あなたの例ではintなく使用int*しました。2 つの違いは、最初のパラメーターが値によって渡されることです。もう一方はポインタで。変数を関数に渡すと、常にそのコピーが作成されます。int を渡すと int のコピーが作成され、4 MB 構造体を渡すと 4MB 構造体のコピーが作成され、4MB 構造体へのポインターを渡すとそのコピーが作成されます。ポインター (構造全体ではありません。) これは、次の 2 つの理由から重要です。

  1. パフォーマンス: 4MB 構造のコピーを作成するには時間がかかります。
  2. コンテンツを変更する機能: ポインターのコピーを作成しても、元のデータは同じ場所にあり、ポインターからアクセスできます。

2 ではなく 1 が必要な場合はどうしますか? それでは、ポインタを宣言できますconst。プロトタイプは次のようになります。int f(int const* a);

1 ではなく 2 が必要な場合はどうしますか? タフなクッキー (とにかく正当な理由はありません。)

最後に、ポインターではなく参照を取る関数を宣言することもできます。参照とポインターの大きな違いは、参照が NULL にならないことです (また、参照に対してポインター演算を使用することはできません)。通常、参照渡しまたは値渡しのいずれかを使用します。ポインターで渡す必要があることは、私がほとんどする必要がないことです。私の経験では、それはより特殊なケースのようなものです。

参照int f(int& a);
渡し: const 参照渡し:int f(int const& a);

要約すると:

if you have function that needs parameters:
  then:
    if you do not need to modify the contents:
      then:
        if the size of the variable is small:
          pass by value: int f(int a);
        else if the size of the variable is large:
          then:
            if the value of the address can be NULL:
              pass by const pointer: int f(int const* a);
            else:
              pass by const reference: int f(int const& a);
    else if you do need to modify the contents:
      then:
        if the value of the address can be NULL:
          pass by pointer: int f(int* a);
        else:
          pass by reference: int f(int& a);

他にもいくつかのケースがありますが、これらは主なものです。詳細については、この Web サイトを参照してください。

于 2012-06-01T05:43:42.187 に答える
2

main 内で変数を定義すると、スコープは main 関数内のみになります。グローバルexternはそれに解決できません。つまり、リンカは、グローバルに宣言された extern を main 関数内の変数定義と一致させることができません。

于 2012-06-01T05:29:59.667 に答える
2

aの内部で定義する場合main、そのスコープ (可視性) はに制限されますmain--extern宣言によって他の場所で可視化されることはありません。

他の翻訳単位で表示するには、名前空間スコープ (つまり、関数の外側) で定義する必要があります。

于 2012-06-01T05:30:51.567 に答える
1

関数 main で変数を定義すると、main のスコープ内でのみ有効になります。すべての関数で a という名前の変数を定義できます。しかし、それぞれに独自のスコープがあるため、これらは異なる変数になります。

技術的には、変数は関数の呼び出し中にスタックに割り当てられるため、各インスタンスには独自のストレージがあります。

于 2012-06-01T05:30:58.067 に答える