定義と宣言の違いは何ですか?
-
05-07-2019 - |
質問
両方の意味が私にはわかりません。
解決
あ 宣言 識別子を導入し、その型 (型、オブジェクト、または関数) を説明します。宣言というのは、 コンパイラに必要なもの その識別子への参照を受け入れるため。これらは宣言です:
extern int bar;
extern int g(int, int);
double f(int, double); // extern can be omitted for function declarations
class foo; // no extern allowed for type declarations
あ 意味 実際にこの識別子をインスタンス化/実装します。その リンカーに必要なもの それらのエンティティへの参照をリンクするため。これらは、上記の宣言に対応する定義です。
int bar;
int g(int lhs, int rhs) {return lhs*rhs;}
double f(int i, double d) {return i+d;}
class foo {};
定義は宣言の代わりに使用できます。
識別子は次のとおりです 宣言された 好きなだけ。したがって、以下は C および C++ では有効です。
double f(int, double);
double f(int, double);
extern double f(int, double); // the same as the two above
extern double f(int, double);
ただし、そうである必要があります 定義済み まさに一度。どこかで宣言され参照されているものを定義するのを忘れた場合、リンカは参照を何にリンクすればよいのかわからず、シンボルが欠落しているというエラーを出します。何かを複数回定義すると、リンカはそれを認識しません。 どれの 参照をリンクするための定義の変更と、重複したシンボルについての苦情。
クラスとは何かという議論以来 宣言 対クラス 意味 C++ では (他の質問への回答やコメントで) 出てくるので、ここに C++ 標準からの引用を貼り付けます。
3.1/2 では、C++03 は次のように述べています。
宣言は、[...] クラス名の宣言 [...] でない限り、定義です。
3.1/3 では、いくつかの例を示します。その中には:
[Example: [...] struct S { int a; int b; }; // defines S, S::a, and S::b [...] struct S; // declares S —end example
要約すると次のようになります。C++ 標準では次のことを考慮しています。 struct x;
になる 宣言 そして struct x {};
ある 意味. 。(言い換えると、 「前方宣言」は誤った呼び名です, C++ には他の形式のクラス宣言がないためです。)
おかげで litb (ヨハネス・シャウブ) 誰が彼の答えの 1 つから実際の章と節を掘り出しました。
他のヒント
C ++標準セクション3.1から:
宣言は、翻訳ユニットに名前を導入するか、以前に導入された名前を再宣言します 宣言。宣言は、これらの名前の解釈と属性を指定します。
次の段落では、宣言が定義であるとは(強調する)、次の場合を除きます...
...関数を指定せずに関数を宣言します<!>#8217; s body:
void sqrt(double); // declares sqrt
...クラス定義内で静的メンバーを宣言します:
struct X
{
int a; // defines a
static int b; // declares b
};
...クラス名を宣言します:
class Y;
...初期化子または関数本体のないextern
キーワードが含まれています:
extern const int i = 0; // defines i
extern int j; // declares j
extern "C"
{
void foo(); // declares foo
}
...またはtypedef
またはusing
ステートメントです。
typedef long LONG_32; // declares LONG_32
using namespace std; // declares std
ここで、宣言と定義の違いを理解することが重要である大きな理由: One Definition Rule 。 C ++標準のセクション3.2.1から:
翻訳単位には、変数、関数、クラス型、列挙型、またはテンプレートの定義を複数含めることはできません。
宣言:<!> quot;どこかにfooが存在します。<!> quot;
定義:<!> quot; ...そしてここにあります!<!> quot;
C ++には興味深いエッジケースがあります(Cにもあります)。検討する
T t;
T
のタイプに応じて、定義または宣言になります。
typedef void T();
T t; // declaration of function "t"
struct X {
T t; // declaration of function "t".
};
typedef int T;
T t; // definition of object "t".
C ++では、テンプレートを使用する場合、別のエッジケースがあります。
template <typename T>
struct X {
static int member; // declaration
};
template<typename T>
int X<T>::member; // definition
template<>
int X<bool>::member; // declaration!
最後の宣言は定義ではありません 。これは、X<bool>
の静的メンバーの明示的な特殊化の宣言です。 <!> quot; X<bool>::member
のインスタンス化に関しては、プライマリテンプレートからメンバーの定義をインスタンス化せず、他の場所にある定義を使用します<!> quot;。定義するには、初期化子を提供する必要があります
template<>
int X<bool>::member = 1; // definition, belongs into a .cpp file.
宣言
宣言は、コンパイラに プログラム要素または名前が存在します。 A 宣言は1つ以上を導入します プログラムへの名前。宣言はできます プログラム内で複数回発生します。 したがって、クラス、構造、 列挙型、およびその他 ユーザー定義型は 各コンパイル単位。
定義
定義は、どのコードまたはデータを指定します 名前が説明しています。名前は 使用する前に宣言されています。
C99標準、6.7(5)から:
宣言は、一連の識別子の解釈と属性を指定します。識別子の定義は、その識別子の宣言です。
- オブジェクトの場合、そのオブジェクト用にストレージが予約されます;
- 関数の場合、関数本体が含まれます;
- 列挙定数またはtypedef名の場合、の(唯一の)宣言 識別子。
C ++標準の3.1(2)から:
宣言は definition です。ただし、関数の本体を指定せずに関数を宣言する場合、extern指定子またはリンケージ仕様を含み、初期化子も関数本体も含まない場合、静的を宣言しますクラス宣言のデータメンバー、クラス名宣言、またはtypedef宣言、using宣言、またはusingディレクティブです。
その後、いくつかの例があります。
興味深いことに(またはそうではありませんが、少し驚いています)、typedef int myint;
はC99の定義ですが、C ++の宣言にすぎません。
wiki.answers.comから:
「宣言」という用語は、(Cで)コンパイラーに型、サイズ、および関数宣言の場合、変数のパラメーターの型とサイズ、またはユーザー定義型またはプログラム内の関数についてコンパイラーに伝えることを意味します。宣言の場合、変数のメモリにはいいえスペースが予約されています。ただし、コンパイラーは、このタイプの変数が作成された場合に確保するスペースを知っています。
たとえば、すべての宣言は次のとおりです。
extern int a;
struct _tagExample { int a; int b; };
int myFunc (int a, int b);
一方、定義とは、宣言が行うすべてのことに加えて、スペースもメモリに予約されることを意味します。 <!> quot; DEFINITION = DECLARATION + SPACE RESERVATION <!> quot;と言うことができます。定義の例を次に示します。
int a;
int b = 0;
int myFunc (int a, int b) { return a + b; }
struct _tagExample example;
回答を参照してください。
C ++ 11アップデート
C ++ 11に関連する答えが表示されないため、ここに1つあります。
a / nを宣言しない限り、宣言は定義です:
- 不透明な列挙-
enum X : int;
- テンプレートパラメータ-
template<typename T> class MyArray;
の T
- パラメータ宣言-
int add(int x, int y);
の x および y
- エイリアス宣言-
using IntVector = std::vector<int>;
- 静的アサート宣言-
static_assert(sizeof(int) == 4, "Yikes!")
- 属性宣言(実装定義)
- 空の宣言
;
上記のリストによってC ++ 03から継承された追加の句:
- 関数宣言-
extern int a;
の add
- 宣言またはリンケージ指定子を含む外部指定子-
extern "C" { ... };
またはclass C { static int x; };
- クラスの静的データメンバー-
struct Point;
の x
- クラス/構造体の宣言-
typedef int Int;
- typedef宣言-
using std::cout;
- using宣言-
using namespace NS;
- usingディレクティブ-<=>
テンプレート宣言は宣言です。テンプレート宣言は、その宣言が関数、クラス、または静的データメンバーを定義する場合の定義でもあります。
それらの間のニュアンスを理解するのに役立つとわかった宣言と定義を区別する標準の例:
// except one all these are definitions
int a; // defines a
extern const int c = 1; // defines c
int f(int x) { return x + a; } // defines f and defines x
struct S { int a; int b; }; // defines S, S::a, and S::b
struct X { // defines X
int x; // defines non-static data member x
static int y; // DECLARES static data member y
X(): x(0) { } // defines a constructor of X
};
int X::y = 1; // defines X::y
enum { up , down }; // defines up and down
namespace N { int d; } // defines N and N::d
namespace N1 = N; // defines N1
X anX; // defines anX
// all these are declarations
extern int a; // declares a
extern const int c; // declares c
int f(int); // declares f
struct S; // declares S
typedef int Int; // declares Int
extern X anotherX; // declares anotherX
using N::d; // declares N::d
// specific to C++11 - these are not from the standard
enum X : int; // declares X with int as the underlying type
using IntVector = std::vector<int>; // declares IntVector as an alias to std::vector<int>
static_assert(X::y == 1, "Oops!"); // declares a static_assert which can render the program ill-formed or have no effect like an empty declaration, depending on the result of expr
template <class T> class C; // declares template class C
; // declares nothing
definitionは、<!> ampで記述された実際の関数を意味します。宣言は、単純な宣言関数を意味します 例えば
void myfunction(); //this is simple declaration
and
void myfunction()
{
some statement;
}
これは関数myfunctionの定義です
経験則:
-
宣言は、メモリ内の変数のデータを解釈する方法をコンパイラに指示します。これはすべてのアクセスに必要です。
-
定義は、変数を既存のものにするためにメモリを予約します。これは、最初のアクセスの前に一度だけ実行する必要があります。
宣言:
int a; // this declares the variable 'a' which is of type 'int'
この宣言により、変数が型に関連付けられます。
宣言の例を次に示します。
int a;
float b;
double c;
今関数宣言:
int fun(int a,int b);
関数の最後にあるセミコロンに注意してください。これは単なる宣言であると言っています。コンパイラーは、プログラムのどこかでその機能がそのプロトタイプで定義されることを知っています。コンパイラーがこのような関数呼び出しを取得した場合
int b=fun(x,y,z);
コンパイラは、そのような関数がないことを示すエラーをスローします。その関数のプロトタイプがないためです。
2つのプログラムの違いに注意してください。
プログラム1
#include <stdio.h>
void print(int a)
{
printf("%d",a);
}
main()
{
print(5);
}
これでは、印刷関数も宣言および定義されます。関数呼び出しは定義の後に来るので。次のプログラムをご覧ください。
プログラム2
#include <stdio.h>
void print(int a); // In this case this is essential
main()
{
print(5);
}
void print(int a)
{
printf("%d",a);
}
関数呼び出しは定義の前にあるため、コンパイラはそのような関数があるかどうかを知る必要があるため、不可欠です。そのため、コンパイラに通知する関数を宣言します。
定義:
関数を定義するこの部分は、定義と呼ばれます。関数内で何をすべきかを示しています。
void print(int a)
{
printf("%d",a);
}
これで変数がわかりました。
int a; //declaration
a=10; //definition
場合によっては、宣言と定義がこのような単一のステートメントにグループ化されます。
int a=10;
名詞を理解するために、まず動詞に注目しましょう。
宣言- 公式に発表する。宣言
定義- (誰かまたは何か)を明確かつ完全に表示または説明する
したがって、何かを宣言するときは、それが何であるかを伝えるだけです。
// declaration
int sum(int, int);
この行は、sum
という2つの引数を取り、int
を返す<=>というC関数を宣言します。ただし、まだ使用できません。
実際に機能する方法を提供すると、それが定義になります。
// definition
int sum(int x, int y)
{
return x + y;
}
宣言と定義の違いを理解するには、アセンブリコードを確認する必要があります。
uint8_t ui8 = 5; | movb $0x5,-0x45(%rbp)
int i = 5; | movl $0x5,-0x3c(%rbp)
uint32_t ui32 = 5; | movl $0x5,-0x38(%rbp)
uint64_t ui64 = 5; | movq $0x5,-0x10(%rbp)
double doub = 5; | movsd 0x328(%rip),%xmm0 # 0x400a20
movsd %xmm0,-0x8(%rbp)
これは唯一の定義です:
ui8 = 5; | movb $0x5,-0x45(%rbp)
i = 5; | movl $0x5,-0x3c(%rbp)
ui32 = 5; | movl $0x5,-0x38(%rbp)
ui64 = 5; | movq $0x5,-0x10(%rbp)
doub = 5; | movsd 0x328(%rip),%xmm0 # 0x400a20
movsd %xmm0,-0x8(%rbp)
ご覧のとおり、何も変化はありません。
宣言は、コンパイラーによってのみ使用される情報を提供するため、定義とは異なります。たとえば、uint8_tはコンパイラにasm関数movbを使用するように指示します。
それを見る:
uint def; | no instructions
printf("some stuff..."); | [...] callq 0x400450 <printf@plt>
def=5; | movb $0x5,-0x45(%rbp)
宣言は、実行されるものではないため、同等の命令を持ちません。
さらに宣言すると、コンパイラに変数のスコープが通知されます。
宣言は、変数の適切な使用を確立するためにコンパイラが使用する情報であり、メモリが特定の変数に属している期間と言うことができます。
可能な限り最も一般的な用語で、宣言はストレージが割り当てられていない識別子であり、定義は宣言された識別子からストレージを実際に割り当てると述べられないでしょうか?
興味深い考え-テンプレートは、クラスまたは関数が型情報にリンクされるまでストレージを割り当てることができません。テンプレート識別子は宣言または定義ですか?ストレージが割り当てられていないため、宣言である必要があり、テンプレートクラスまたは関数を単に「プロトタイプ化」しています。
同様の回答をここで見つけてください:技術インタビューC 。
宣言は、プログラムに名前を付けます。 定義は、プログラム内のエンティティ(タイプ、インスタンス、機能など)の一意の説明を提供します。宣言は特定のスコープで繰り返すことができ、特定のスコープで名前を導入します。
次の場合を除き、宣言は定義です:
- 宣言は、本体を指定せずに関数を宣言します
- 宣言にはextern指定子が含まれ、初期化子または関数本体は含まれません
- 宣言は、クラス定義のない静的クラスデータメンバーの宣言です
- 宣言はクラス名の定義です
次の場合を除き、定義は宣言です:
- 定義は静的クラスデータメンバを定義します。
- 定義は、非インラインメンバー関数を定義します。
これは非常に安っぽい音に聞こえますが、それは頭の中で用語をまっすぐに保つことができた最良の方法です:
宣言:スピーチを行う画像Thomas Jefferson ... <!> quot;私はここで、このFOOがこのソースコードに存在することを宣言します!!! <!> quot;
定義:辞書を想像してください。Fooとその実際の意味を調べています。
宣言は、コンパイラにシンボル名を提示します。定義は、シンボルにスペースを割り当てる宣言です。
int f(int x); // function declaration (I know f exists)
int f(int x) { return 2*x; } // declaration and definition
GNU Cライブラリマニュアル( http:/ /www.gnu.org/software/libc/manual/html_node/Header-Files.html )
Cでは、宣言は単に関数または変数が存在するという情報を提供し、その型を提供します。関数宣言の場合、引数の型に関する情報も提供される場合があります。宣言の目的は、コンパイラが宣言された変数と関数への参照を正しく処理できるようにすることです。一方、定義は、実際に変数にストレージを割り当てるか、関数が何をするかを指示します。
宣言とは、変数に名前と型を与えることです(変数宣言の場合)。例:
int i;
または名前、戻り値の型、パラメータの型を、ボディのない関数に指定します(関数宣言の場合)。例:
int max(int, int);
定義は、変数に値を割り当てることを意味します(変数定義の場合)。例:
i = 20;
または関数にbody(functionality)を提供/追加することは、関数定義と呼ばれます。例:
int max(int a, int b)
{
if(a>b) return a;
return b;
}
何度も宣言と定義を一緒に行うことができます:
int i=20;
and:
int max(int a, int b)
{
if(a>b) return a;
return b;
}
上記の場合、変数i
およびfunction max()
を定義および宣言します。
定義は他の場所にあり、ローカルコードファイル(ページ)で変数を宣言しているため、externストレージクラスを使用している場合、宣言と定義の概念が落とし穴を形成します。 CとC ++の違いの1つは、Cでは関数またはコードページの先頭で宣言が通常行われることです。 C ++では、そうではありません。任意の場所で宣言できます。
私のお気に入りの例は<!> quot; int Num = 5 <!> quot;ここで、変数は1. intとして定義されます2. Numとして宣言され、3。値5でインスタンス化されます。私たち
- オブジェクトのタイプを定義します。これは組み込み、クラス、または構造体の場合があります。
- オブジェクトの名前を宣言します。そのため、変数、関数などを含む名前のあるものはすべて宣言されています。
クラスまたは構造体を使用すると、後で使用するときにオブジェクトを定義する方法を変更できます。例
- 明確に定義されていない異種の変数または配列を宣言できます。
- C ++でオフセットを使用すると、宣言された名前を持たないオブジェクトを定義できます。
プログラミングを学ぶとき、これらの2つの用語は混同されることが多いため、しばしば混同されます。
実行可能世代のステージ:
(1)プリプロセッサ-<!> gt; (2)翻訳者/コンパイラ-<!> gt; (3)リンカー
ステージ2(トランスレーター/コンパイラー)で、コード内の宣言ステートメントは、これらのことを将来使用することをコンパイラーに伝え、後で定義を見つけることができることを意味します:
翻訳者は次のことを確認してください:何とはは宣言を意味します
and(3)ステージ(リンカー)は、ものをバインドするために定義が必要です
リンカーは次のことを確認します。どこが何ですかは定義を意味します