両方の意味がわからない。
両方の意味がわからない。
回答:
宣言は、識別子を紹介し、そのタイプを記述し、それはタイプ、オブジェクト、または関数です。宣言は、コンパイラがその識別子への参照を受け入れるために必要なものです。これらは宣言です:
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);
ただし、正確に1回定義する必要があります。宣言され、どこかで参照されているものを定義するのを忘れた場合、リンカは参照をリンクする対象を知らず、不足しているシンボルについて文句を言います。あなたが何かを2回以上定義すると、リンカはどちらを知っていますかへのリンク参照の定義の重複シンボルについて文句を言います。
C ++でのクラス宣言とクラス定義の違いについての議論が(他の質問への回答やコメントで)出続けているので、ここにC ++標準からの引用を貼り付けます。
3.1 / 2では、C ++ 03は次のように述べています。
宣言は、それがクラス名宣言[...]でない限り、定義です。
3.1 / 3では、いくつかの例を示します。それらの中で:
[例:[...] struct S {int a; int b; }; // S、S :: a、S :: b [...]を定義します struct S; // Sを宣言します -終了例
それを要約すると:C ++標準では考慮struct x;
することの宣言と定義。(つまり、C ++には他の形式のクラス宣言がないため、「前方宣言」は誤称です。)struct x {};
litb(Johannes Schaub)のおかげで、彼の回答の1つで実際の章と詩を掘り下げました。
extern int i
これは単にを導入/指定するだけなので、宣言i
です。extern int i
各コンパイルユニットには、必要な数だけ含めることができます。int i
ただし、定義です。これは、整数がこの変換単位に入るスペースを示し、i
このエンティティーに対するすべての参照をリンクするようリンカーにアドバイスします。これらの定義の1つより多いか少ない場合、リンカは文句を言うでしょう。
int i;
ファイル/グローバルスコープまたは関数スコープの@Brian は、CとC ++の両方での定義です。Cではストレージを割り当てるため、C ++ではextern指定子またはリンケージ指定がないため。これらは、sbiが言っていることと同じことになります。どちらの場合でも、この宣言は、そのスコープ内の「i」へのすべての参照をリンクする必要があるオブジェクトを指定します。
struct A { double f(int, double); double f(int, double); };
もちろん無効です。他の場所でも許可されています。宣言はできるが、定義もできない場所がいくつかあります。void f() { void g(); }
有効ですが、次のものはありません。void f() { void g() { } };
。テンプレートに関して、定義とは何か、宣言には微妙なルールがあるか-注意してください!良い答えは+1です。
C ++標準セクション3.1から:
宣言前の宣言によって導入された翻訳単位または再宣言の名前に紹介名。宣言は、これらの名前の解釈と属性を指定します。
次の段落では、宣言は定義でないと述べています(私の強調)。
...関数の本体を指定せずに関数を宣言します。
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
or using
ステートメントです。
typedef long LONG_32; // declares LONG_32
using namespace std; // declares std
ここで、宣言と定義の違いを理解することが重要である大きな理由である、単一定義ルールについて説明します。C ++標準のセクション3.2.1から:
どの翻訳単位にも、変数、関数、クラス型、列挙型、またはテンプレートの複数の定義を含めることはできません。
struct x {static int b = 3; };
ますか?
b
が宣言されていない限り、この例の変更は実際には違法const
です。stackoverflow.com/a/3536513/1858225およびdaniweb.com/software-development/cpp/threads/140739/…を参照してください。
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>
。これは、コンパイラーに「インスタンス化する場合X<bool>::member
は、プライマリテンプレートからメンバーの定義をインスタンス化せず、他の場所にある定義を使用する」ように指示します。これを定義するには、初期化子を指定する必要があります
template<>
int X<bool>::member = 1; // definition, belongs into a .cpp file.
宣言
宣言は、プログラムの要素または名前が存在することをコンパイラーに伝えます。宣言は、1つ以上の名前をプログラムに導入します。宣言は、プログラム内で複数回発生する可能性があります。したがって、クラス、構造、列挙型、およびその他のユーザー定義型は、コンパイル単位ごとに宣言できます。
定義
定義は、名前が説明するコードまたはデータを指定します。名前は、使用する前に宣言する必要があります。
class foo {};
はクラス定義ですでは、されていませんか?
C99標準から、6.7(5):
宣言は、一連の識別子の解釈と属性を指定します。定義識別子は、その識別子ことの宣言です。
C ++標準から、3.1(2):
宣言は定義です。ただし、関数の本体を指定せずに関数を宣言し、extern指定子またはリンケージ仕様を含み、初期化子も関数本体も含まない場合、クラス宣言で静的データメンバーを宣言します。クラス名の宣言、またはtypedef宣言、using-declaration、またはusing-directiveです。
次にいくつかの例があります。
興味深いことに(またはそうではありませんが、少し驚いています)、typedef int myint;
C99での定義ですが、C ++での宣言のみです。
typedef
、それがC ++では繰り返されるが、C99では繰り返されないことを意味しませんか?
wiki.answers.comから:
宣言という用語は、(Cで)タイプ、サイズ、および関数宣言の場合は変数のパラメーターのパラメーターのタイプとサイズ、またはプログラム内のユーザー定義のタイプまたは関数についてコンパイラーに指示することを意味します。宣言の場合、変数内にメモリ用のスペースは確保されません。ただし、このタイプの変数が作成された場合に備えて、コンパイラーはどれだけのスペースを確保するかを知っています。
たとえば、以下はすべての宣言です。
extern int a;
struct _tagExample { int a; int b; };
int myFunc (int a, int b);
一方、定義とは、宣言が行うすべてのことに加えて、スペースもメモリに予約されることを意味します。定義の例を次に示します「DEFINITION = DECLARATION + SPACE RESERVATION」。
int a;
int b = 0;
int myFunc (int a, int b) { return a + b; }
struct _tagExample example;
Answersを参照してください。
struct foo {};
ある定義ではなく宣言。宣言foo
になりますstruct foo;
。そのため、コンパイラーはfoo
オブジェクト用に予約するスペースの量を知りません。
struct foo;
宣言ですが、fooのサイズをコンパイラに伝えません。それstruct _tagExample { int a; int b; };
は定義です。したがって、このコンテキストでは、これを宣言と呼ぶのは誤解を招きます。もちろん、すべての定義は宣言なので、1つですが、定義ではないことを示唆しているようです。_tagExampleの定義です。
C ++ 11に関連する答えが見当たらないので、ここに答えがあります。
a / nを宣言しない限り、宣言は定義です。
enum X : int;
template<typename T> class MyArray;
int add(int x, int y);
using IntVector = std::vector<int>;
static_assert(sizeof(int) == 4, "Yikes!")
;
上記のリストによってC ++ 03から継承された追加の句:
int add(int x, int y);
extern int a;
またはextern "C" { ... };
class C { static int x; };
struct Point;
typedef int Int;
using std::cout;
using namespace NS;
テンプレート宣言は宣言です。テンプレート宣言が関数、クラス、または静的データメンバーを定義する場合、テンプレート宣言も定義です。
宣言と定義を区別する標準からの例は、それらの間のニュアンスを理解するのに役立つと私が見つけた:
// 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
定義:
extern int a; // Declaration
int a; // Definition
a = 10 // Initialization
int b = 10; // Definition & Initialization
定義は変数を型に関連付けてメモリを割り当てますが、宣言は型を指定するだけでメモリを割り当てません。宣言は、定義の前に変数を参照する場合に役立ちます。
*定義と初期化を混同しないでください。どちらも異なり、初期化は変数に値を与えます。上記の例を参照してください。
以下に定義の例をいくつか示します。
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);
}
この中で、print関数も宣言され、定義されています。関数呼び出しが定義の後に来ているので。次のプログラムをご覧ください。
プログラム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
これは完全に間違っています。自動ストレージ期間オブジェクト(externなどの別のストレージクラス指定子で宣言されていない関数定義内で宣言されたオブジェクト)について話すとき、これらは常に定義です。
経験則:
宣言は、メモリ内の変数のデータをどのように解釈するかをコンパイラに指示します。これは、すべてのアクセスに必要です。
定義は、変数が既存にするためにメモリを予約します。これは、最初のアクセスの前に正確に1回発生する必要があります。
名詞を理解するために、まず動詞に焦点を当てましょう。
宣言 -正式に発表すること。宣言する
定義 -(誰かまたは何か)を明確かつ完全に表示または説明する
したがって、何かを宣言するときは、それが何であるかを伝えるだけです。
// declaration
int sum(int, int);
この行は、型の2つの引数を取り、を返す、呼び出されたC関数を宣言しています。ただし、まだ使用できません。sum
int
int
実際にどのように機能するかを提供すると、それがその定義になります。
// 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)
宣言は実行するものではないため、同等の命令はありません。
さらに、宣言は変数のスコープをコンパイラーに伝えます。
宣言は、変数の正しい使用を確立するためにコンパイラーが使用する情報であり、一部のメモリーが特定の変数に属している期間です。
可能な最も一般的な用語で、宣言はストレージが割り当てられていない識別子であり、定義は宣言された識別子から実際にストレージを割り当てると述べられませんか?
1つの興味深い考え-テンプレートは、クラスまたは関数が型情報とリンクされるまで、ストレージを割り当てることができません。テンプレート識別子は宣言または定義ですか?ストレージは割り当てられておらず、テンプレートクラスまたは関数を単に「プロトタイプ化」しているだけなので、宣言である必要があります。
template<class T> struct foo;
はテンプレート宣言であり、これもテンプレート宣言template<class T> void f();
です。テンプレート定義は、クラス/関数定義を同じ方法でミラーリングします。(テンプレート名はタイプまたは関数名ではないことに注意してください。これを確認できる場所の1つは、テンプレートを別のテンプレートのタイプパラメータとして渡すことができない場合です。タイプではなくテンプレートを渡す場合は、テンプレートテンプレートパラメータが必要です。 )
ここで同様の答えを見つけてください:Cの技術面接の質問。
宣言プログラムに名前を提供します。定義は、プログラム内のエンティティ(例えばタイプ、インスタンス、および関数)の固有の説明を提供します。宣言は特定のスコープで繰り返すことができ、それは特定のスコープに名前を導入します。
次の場合を除き、宣言は定義です。
以下の場合を除き、定義は宣言です。
GNU Cライブラリのマニュアルによると(http://www.gnu.org/software/libc/manual/html_node/Header-Files.html)
Cでは、宣言は、関数または変数が存在し、その型を与えるという情報を提供するだけです。関数宣言の場合、その引数の型に関する情報も提供される場合があります。宣言の目的は、コンパイラが宣言された変数と関数への参照を正しく処理できるようにすることです。一方、定義は、実際に変数にストレージを割り当てるか、関数が何をするかを示します。
私のお気に入りの例は「int Num = 5」です。ここで、変数は1. intとして定義されます。2。Numとして宣言され、3。値5でインスタンス化されます。我々
クラスまたは構造体を使用すると、後で使用するときにオブジェクトを定義する方法を変更できます。例えば
私たちがプログラミングを学ぶとき、私たちはしばしば両方を同時に行うので、これら2つの用語はしばしば混乱します。
K&R(第2版)全体にいくつかの非常に明確な定義が散りばめられています。それらを1つの場所に置き、1つとして読むと役立ちます。
「定義」とは、変数が作成または割り当てられるストレージを指します。「宣言」とは、変数の性質が記述されているが、ストレージが割り当てられていない場所を指します。[p。33]
...
外部変数の宣言とその定義を区別することが重要です。宣言は、変数(主にその型)のプロパティを通知します。定義により、ストレージも確保されます。行の場合
int sp; double val[MAXVAL]
任意の関数の外に現れ、彼らが定義する外部変数を
sp
とval
ストレージを脇に置き、そのソースファイルの残りの部分の宣言としても機能します。一方、行
extern int sp; extern double val[];
宣言したソースファイルの残りのため
sp
でint
、それはval
ありますdouble
(そのサイズは他の場所で決定される)の配列が、彼らは彼らのために変数や予備記憶を作成しないでください。ソースプログラムを構成するすべてのファイルの中で、外部変数の定義は1つだけでなければなりません。...配列サイズは定義で指定する必要がありますが、
extern
宣言ではオプションです。[pp。80-81]...
宣言は、各識別子に与えられる解釈を指定します。識別子に関連付けられたストレージを必ずしも予約するわけではありません。ストレージを予約する宣言は、定義と呼ばれます。[p。210]
宣言とは、変数に名前と型を与えることを意味します(変数宣言の場合)。例:
int i;
または、本体なしの関数に名前、戻り値の型、パラメータの型を指定します(関数宣言の場合)。例:
int max(int, int);
一方、定義とは、変数に値を割り当てることを意味します(変数定義の場合)。例:
i = 20;
または関数に本体(機能)を提供/追加することを関数定義と呼びます。例:
int max(int a, int b)
{
if(a>b) return a;
return b;
}
多くの時間の宣言と定義は、次のように一緒に行うことができます。
int i=20;
そして:
int max(int a, int b)
{
if(a>b) return a;
return b;
}
上記の場合、変数i
and を定義して宣言しますfunction max()
。
int x;