Tanım ve bildirim arasındaki fark nedir?


857

Her ikisinin anlamı beni atlatıyor.


91
@Lasse: doğru değil. Tanım ;-) tanımını ve tanımını yapar
Steve Jessop

13
Açıkçası, öğrenme konusunda çok fazla sorun yaşadım, bu yüzden isimleri açık bulamadım. Anlamlarla ilgili hiçbir sorunum yoktu, sadece hangi isimlerin anlamlarla ilişkilendirileceği.
David Thornley

6
Yine de, bu yinelenen bir soru değil, çünkü bu C / C ++ hakkında soruyor, oysa diğer soru genel olarak tüm diller hakkında soruluyor veya hiç dil sormuyor. Sadece yinelenen cevapları var (diğer soruda, bazı cevaplar C ve / veya C ++ hariç tüm dilleri yoksaymayı seçti).
Steve Jessop

5
@DavidThornley Bu hileyi kullanıyorum: bir tanım , belirli bir değişkenin veya fonksiyonun daha iyi bir tanımını verir . Bunu hatırlamak için, "tanım" kelimesinin ortasının "daha ince" kelimesine benzediğini hatırlıyorum. :)
Marco Leogrande

4
Bu soruda ne kadar saçmalık olduğu şaşırtıcı. Sadece bu dilin ne kadar yanlış anlaşıldığını ve bu yanlış anlamaların rutin olarak nasıl yayıldığını göstermeye gider . Gerçekten üzücü.
Yörüngedeki Hafiflik Yarışları

Yanıtlar:


858

Bir bildirim , bir tanımlayıcı getirir ve bir tür, nesne veya işlev olsun, türünü tanımlar. Bir bildirim, derleyicinin bu tanımlayıcıya yapılan referansları kabul etmesi gerektiğidir. Bunlar beyanlardır:

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

Bir tanım aslında bu tanımlayıcıyı başlatır / uygular. Bu varlıklara referansları bağlamak için linkerin ihtiyacı olan şey budur . Bunlar yukarıdaki beyanlara karşılık gelen tanımlardır:

int bar;
int g(int lhs, int rhs) {return lhs*rhs;}
double f(int i, double d) {return i+d;}
class foo {};

Bir açıklama yerine bir tanım kullanılabilir.

Bir tanımlayıcı istediğiniz sıklıkta bildirilebilir . Bu nedenle, C ve C ++ için aşağıdakiler yasaldır:

double f(int, double);
double f(int, double);
extern double f(int, double); // the same as the two above
extern double f(int, double);

Ancak, tam olarak bir kez tanımlanmalıdır . Bir yerde bildirilen ve referans verilen bir şeyi tanımlamayı unutursanız, bağlayıcı referanslara neyin bağlanacağını bilmez ve eksik sembollerden şikayet eder. Bir kereden fazla bir şey tanımlarsanız, bağlayıcı referanslara hangi tanımların bağlanacağını bilmez ve çoğaltılan sembollerden şikayet eder.


Bir sınıf ne tartışma yana beyanı sınıf vs tanımı C ++ (cevapları ve diğer sorulara yorumlardaki) geliyor tutar, ben C ++ standart buradan bir alıntı yapıştırın.
3.1 / 2'de C ++ 03 diyor ki:

Bir bildirim, [...] bir sınıf adı bildirimi [...] olmadığı sürece bir tanımdır.

3.1 / 3 daha sonra birkaç örnek verir. Aralarında:

[Misal: [...]
yapı S {int a; int b; }; // S, S :: a ve S :: b'yi tanımlar [...]
yapı S; // S beyan eder
—End örneği

Özetle: C ++ standart dikkate struct x;bir olduğu beyan ve struct x {};bir tanımlama . (Başka bir deyişle, "ileri bildirim" yanlış adlandırmadır , çünkü C ++ 'da başka tür sınıf bildirimleri yoktur.)

Sayesinde LITB (Johannes Schaub) onun cevapları birinde fiili bölüm ve ayet çıkardı.


2
@unknown: Derleyiciniz bozuldu, yanlış kopyalanmış sbi kodunuz var. Örneğin, N1124'teki 6.7.2 (2): "Aynı nesne veya işleve atıfta bulunan tüm bildirimler uyumlu türde olmalıdır; aksi takdirde davranış tanımsızdır."
Steve Jessop

4
@Brian: "extern int i;" bir yerde int olduğumu söylüyor, endişelenme. "int i;" i'nin bir int olduğu ve adresi ve kapsamının burada belirlendiği anlamına gelir.
David Thornley

12
@Brian: Yanılıyorsun. extern int iyalnızca bir açıklama / belirtme olduğu için bir bildirimdir i. extern int iHer derleme biriminde istediğiniz kadarını kullanabilirsiniz . int iancak bir tanımdır. Tamsayı için bu çeviri biriminde olması gereken alanı belirtir ve bağlayıcıya ibu varlığa karşı tüm referansları bağlamasını önerir. Bu tanımlardan tam olarak birden fazla veya daha azına sahipseniz, bağlayıcı şikayet edecektir.
sbi

4
@Brian int i;/ global kapsam veya işlev kapsamı, hem C hem de C ++ 'da bir tanımdır. C'de depolama alanı ayırdığı için ve C ++ içinde extern belirleyicisi veya bağlantı belirtimi olmadığı için. Bu tutar, sbi'nin söylediği şeyle aynıdır: her iki durumda da bu bildirim, bu kapsamdaki "i" ye yapılan tüm başvuruların bağlanması gereken nesneyi belirtir.
Steve Jessop

4
@unknown, sınıf kapsamındaki üyeleri yeniden bildiremeyeceğinize dikkat edin : struct A { double f(int, double); double f(int, double); };geçersiz, elbette. Yine de başka bir yerde izin verilir. : Orada, tanımlamak çok şeyler beyan, ancak bazı yerlerdir void f() { void g(); }şu geçerli değil: void f() { void g() { } };. Bir tanım nedir ve şablonlar söz konusu olduğunda bir bildirimin ince kuralları vardır - dikkat! Olsa iyi bir cevap için +1.
Johannes Schaub - litb

168

C ++ standart bölüm 3.1'den:

Bir bildirim , bir çeviri birimine adlar ekler veya önceki bildirimlerle verilen adları yeniden bildirir. Bir bildirim, bu adların yorumlanmasını ve niteliklerini belirtir.

Bir sonraki paragraf (benimki vurgu), bir deklarasyonun bir tanım olmadığını belirtiyor ...

... işlevin gövdesini belirtmeden bir işlevi bildirir:

void sqrt(double);  // declares sqrt

... bir sınıf tanımında statik üye bildirir:

struct X
{
    int a;         // defines a
    static int b;  // declares b
};

... bir sınıf adı beyan eder:

class Y;

... externbaşlatıcısı veya işlev gövdesi olmayan bir anahtar kelime içeriyor :

extern const int i = 0;  // defines i
extern int j;  // declares j
extern "C"
{
    void foo();  // declares foo
}

... veya bir typedefveya usingifadedir.

typedef long LONG_32;  // declares LONG_32
using namespace std;   // declares std

Şimdi bir açıklama ve tanım arasındaki farkı anlamak önemli nedenlerden biri : Tek Tanım Kuralı . C ++ standardının 3.2.1 bölümünden:

Hiçbir çeviri birimi, herhangi bir değişken, işlev, sınıf türü, numaralandırma türü veya şablonun birden fazla tanımını içeremez.


"sınıf tanımı içinde statik bir üye bildirir" - Bu statik üye başlatılmış olsa bile doğrudur, değil mi? Örneği yapabilir miyiz struct x {static int b = 3; };?
RJFalconer

@RJFalconer Haklısınız; başlatma yok değil mutlaka bir tanım içine bir deklarasyon çevirmek (bir beklediğiniz aksine; kesinlikle bu şaşırtıcı bulundu). Örnekteki modifikasyonunuz, bbeyan edilmedikçe aslında yasa dışıdır const. Bkz. Stackoverflow.com/a/3536513/1858225 ve daniweb.com/software-development/cpp/threads/140739/… .
Kyle Strand

1
Bu benim için ilginç. Cevabınız göre, C ++, bir deklarasyon olduğu görülmektedir da "C: (Beyannameler C99, bölüm 6.7) C standardında diğer bakış açısıyla phrased oysa bir tanım, (istisnalar hariç) tanımı bir tanımlayıcı olan bu tanımlayıcı için aşağıdakileri içeren bir beyan: [ardından farklı durumlar için ölçütler] ". Sanırım ona bakmak için farklı yollar. :)
Victor Zamanian

Beyan, derleyicinin bir ismi kabul etmesidir (derleyiciye ismin yasal olduğunu söylemek, ismin bir yazım hatası değil, niyetle tanıtıldığını). Tanım, bir adın ve içeriğinin ilişkilendirildiği yerdir. Tanım, bağlayıcı tarafından bir ad referansını adın içeriğine bağlamak için kullanılır.
Gab 是 好人

137

Beyan: "Bir yerlerde bir foo var."

Tanım: "... ve işte burada!"


3
Beyan, derleyicinin bir ismi kabul etmesidir (derleyiciye ismin yasal olduğunu söylemek, ismin bir yazım hatası değil, niyetle tanıtıldığını). Tanım, bir adın ve içeriğinin ilişkilendirildiği yerdir. Tanım, bağlayıcı tarafından bir ad referansını adın içeriğine bağlamak için kullanılır.
Gab 是 好人

46

C ++ 'da ilginç kenar durumlar vardır (bazıları C de). Düşünmek

T t;

Bu, ne tür olduğuna bağlı olarak bir tanım veya bildirim olabilir 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 ++ 'da, şablonlar kullanılırken başka bir uç durum vardır.

template <typename T>
struct X { 
  static int member; // declaration
};

template<typename T>
int X<T>::member; // definition

template<>
int X<bool>::member; // declaration!

Son deklarasyon bir tanım değildi . Bu, statik elemanının açık bir uzmanlığının beyanıdır X<bool>. Derleyiciye şunu söyler: "Eğer örnekleme söz konusuysa X<bool>::member, üye tanımını birincil şablondan örneklemeyin, ancak başka bir yerde bulunan tanımı kullanın". Bir tanım yapmak için, bir başlatıcı sağlamanız gerekir

template<>
int X<bool>::member = 1; // definition, belongs into a .cpp file.

35

deklarasyon

Bildirimler derleyiciye bir program öğesinin veya adının bulunduğunu bildirir. Bir bildirim, bir programa bir veya daha fazla ad ekler. Bildirimler bir programda birden çok kez olabilir. Bu nedenle, sınıflar, yapılar, numaralandırılmış türler ve diğer kullanıcı tanımlı türler her derleme birimi için bildirilebilir.

Tanım

Tanımlar, adın hangi kodu veya verileri açıkladığını belirtir. Kullanılmadan önce bir ad bildirilmelidir.


Her derleme biriminde sınıf ve numaralandırma bile tanımlayabiliyor musunuz? En azından üstbilgilerime sınıf tanımları koydum ve her yere ekledim. Er, class foo {}; olan bir sınıf tanımı bu değil mi?
sbi

1
Evet. Ancak, "sınıf foo;" bir deklarasyon. Derleyiciye foo'nun bir sınıf olduğunu söyler. "class foo {};" bir tanımdır. Derleyiciye tam olarak ne tür bir sınıf foo olduğunu söyler.
David Thornley

1
İstisna, bildirilmeden önce kullanılabilecek sınıf üyesi adlarıdır.
Johannes Schaub - litb

1
Evet, demek istediğim buydu. Böylece aşağıdakileri yapabilirsiniz: struct foo {void b () {f (); } void f (); }, henüz bildirilmemesine rağmen f görünür. Aşağıdakiler de işe yarar: struct foo {void b (int = bar ()); typedef int bar; } ;. "Tüm işlev gövdeleri, varsayılan bağımsız değişkenler, yapıcı ctor-başlatıcıları" nda bildirilmeden önce görünür. Dönüş türünde değil :(
Johannes Schaub - litb

1
@litb: Bildirilmeden önce görünmez, yalnızca tanımlayıcı kullanımının bildirimin arkasına taşınmasıdır. Evet, biliyorum, etki birçok vaka için aynıdır. Ancak tüm durumlar için değil, bu yüzden kesin açıklamayı kullanmamız gerektiğini düşünüyorum. - Hata, bekle. Varsayılan argümanlarda görünür mü? Bu benim anlayışımla kesinlikle yıkıcı. Kahretsin! <
pouts

22

C99 standardından 6.7 (5):

Bir deklarasyon, bir dizi tanımlayıcının yorumunu ve niteliklerini belirtir. Bir tanımlayıcının tanımı , söz konusu tanımlayıcı için aşağıdakileri içeren bir bildirimdir:

  • bir nesne için depolamanın o nesne için ayrılmasına neden olur;
  • bir işlev için işlev gövdesini içerir;
  • numaralandırma sabiti veya typedef adı için, tanımlayıcının (yalnızca) beyanıdır.

C ++ standardından, 3.1 (2):

Bir bildirim, işlev gövdesini belirtmeden bir işlev bildirmedikçe bir tanımdır , extern belirleyicisini veya bağlantı spesifikasyonunu ve ne bir başlatıcıyı ne de işlev gövdesini içerir, sınıf bildiriminde statik bir veri üyesi bildirir, sınıf adı bildirimi veya bir typedef bildirimi, bir kullanım-bildirimi veya bir kullanım-direktifidir.

Sonra bazı örnekler var.

Çok ilginç (ya da değil, ama biraz şaşırdım), typedef int myint;C99'da bir tanımdır, ancak sadece C ++ 'da bir bildiridir.


@onebyone: Bu typedef, C ++ 'da tekrarlanabileceği anlamına gelmez, ancak C99'da tekrarlanamaz mı?
sbi

Beni şaşırtan şey buydu ve tek bir çeviri birimi söz konusu olduğunda, evet bu fark var. Ancak açıkça bir typedef C99'da farklı çeviri birimlerinde tekrarlanabilir. C, C ++ gibi açık bir "tek tanım kuralı" na sahip değildir, bu yüzden kuralları sadece izin verir. C ++ bunu bir bildirime değiştirmeyi seçti, ancak bir tanım kuralı ne tür şeyler için geçerli olduğunu listeliyor ve typedefs bunlardan biri değil. Bu yüzden bir typedef bir tanım olsa bile, ifade edildiği gibi ODR altında C ++ 'da tekrarlara izin verilir. Gereksiz seçici görünüyor.
Steve Jessop

... ama ODR'deki listenin aslında tanımlamaları mümkün olan her şeyi listelediğini tahmin ediyorum. Öyleyse, liste aslında gereksizdir ve sadece yardımcı olmak için oradadır.
Steve Jessop

STD'nin ODR tanımı sınıf tanımları hakkında ne diyor? Onlar sahip tekrarlanmalıdır.
sbi

2
@sbi: ODR, "(1) Hiçbir çeviri birimi, herhangi bir ... sınıf türünün birden fazla tanımını içeremez" ve "(5) Bir programda, sınıf türünün birden fazla tanımı olabilir ... her tanım farklı bir çeviri biriminde görünür "ve ardından" tanımlar aynı "anlamına gelen bazı ekstra gereksinimler.
Steve Jessop

17

Wiki.answers.com adresinden:

Beyanname terimi, derleyiciye tür, boyut ve fonksiyon bildirimi durumunda, herhangi bir değişkenin parametrelerinin tipini ve tipini veya programınızdaki kullanıcı tanımlı tip veya fonksiyonu anlattığınız anlamına gelir. Hiçbir boşluk ilan halinde herhangi değişken için bellekte ayrılmıştır. Ancak derleyici, bu türden bir değişkenin oluşturulması durumunda ne kadar alan ayıracağını bilir.

örneğin, tüm bildirimler şunlardır:

extern int a; 
struct _tagExample { int a; int b; }; 
int myFunc (int a, int b);

Öte yandan tanım, deklarasyonun yaptığı tüm şeylere ek olarak, alanın da hafızada saklandığı anlamına gelir. "TANIM = BEYAN + BOŞLUK REZERVASYONU" diyebilirsiniz.

int a; 
int b = 0; 
int myFunc (int a, int b) { return a + b; } 
struct _tagExample example; 

bkz. Cevaplar .


3
Bu da yanlıştır (diğerlerinden çok daha yakın olsa da): struct foo {};bir tanım değil , bir tanımdır . Bir beyanı fooolurdu struct foo;. Bundan sonra, derleyici foonesneler için ne kadar yer ayıracağını bilmiyor .
sbi

1
@Marcin: sbi "derleyici bu türden bir değişken yaratıldığında ne kadar yer ayıracağını biliyor" diyor her zaman doğru değildir. struct foo;bir deklarasyon, ancak derleyiciye foo boyutunu söylemez. Bu struct _tagExample { int a; int b; };bir tanım ekledim. Bu bağlamda, ona bir deklarasyon demek yanıltıcıdır. Tabii ki bir tanesidir, çünkü tüm tanımlar bildirimlerdir, ancak bunun bir tanım olmadığını öne sürüyorsunuz. _TagExample öğesinin bir tanımıdır.
Steve Jessop

1
@Marcin Gil: Bu, "Cevaplar" wiki'sinin her zaman doğru olmadığı anlamına gelir. Burada yanlış bilgi için aşağıya bakmam gerekiyor.
David Thornley

1
Adatapost'un alıntıladığı şeyin doğru olduğunu ancak (IMO) sorusuna gerçekten cevap vermediğini öğreniyoruz. Marcin'nin aktardığı yanlıştır. Standartlardan alıntı yapmak doğrudur ve soruyu cevaplar, ancak başını veya kuyruğunu yapmak çok zordur.
Steve Jessop

1
@David Thornley - sorun değil :) Bu site hakkında. Biz seçmek ve bilgi doğrulamak.
Marcin Gil

13

C ++ 11 Güncellemesi

C ++ 11 ile ilgili bir cevap görmediğim için burada bir tane var.

Bir bildirim, a / n bildirmediği sürece bir tanımdır :

  • opak enum - enum X : int;
  • şablon parametresi - T girişitemplate<typename T> class MyArray;
  • parametre bildirimi - x ve y girişiint add(int x, int y);
  • takma ad bildirimi - using IntVector = std::vector<int>;
  • statik onay beyanı - static_assert(sizeof(int) == 4, "Yikes!")
  • özellik bildirimi (uygulama tanımlı)
  • boş beyan ;

Yukarıdaki listede C ++ 03'ten devralınan ek maddeler:

  • fonksiyon beyanı - eklemek içindeint add(int x, int y);
  • beyan veya bir bağlantı belirteci içeren harici belirteç - extern int a;veyaextern "C" { ... };
  • sınıftaki statik veri üyesi - x inclass C { static int x; };
  • sınıf / yapı bildirimi - struct Point;
  • typedef bildirimi - typedef int Int;
  • beyan kullanarak - using std::cout;
  • direktif kullanma - using namespace NS;

Şablon bildirimi bir bildiridir. Şablon bildirimi, bildirimi bir işlevi, sınıfı veya statik veri üyesini tanımladığında da bir tanımdır.

Aralarındaki nüansları anlamada yararlı bulduğum beyan ve tanım arasında ayrım yapan standarttan örnekler:

// 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

6

Tanım :

extern int a;      // Declaration 
int a;             // Definition
a = 10             // Initialization
int b = 10;        // Definition & Initialization

Tanım, değişkeni bir türle ilişkilendirir ve belleği ayırır; oysa bildirim yalnızca türü belirtir, ancak bellek ayırmaz. Tanımlamadan önce değişkene başvurmak istediğinizde bildirim daha kullanışlıdır.

* Tanımı başlatma ile karıştırmayın. Her ikisi de farklıdır, başlatma değişkene değer verir. Yukarıdaki örneğe bakın.

Aşağıda bazı tanım örnekleri verilmiştir.

int a;
float b;
double c;

Şimdi işlev bildirimi:

int fun(int a,int b); 

Fonksiyonun sonundaki noktalı virgül dikkat edin, böylece yalnızca bir bildirimdir. Derleyici programın herhangi bir yerinde bu prototip ile bu fonksiyonun tanımlanacağını bilir . Derleyici bir işlev alırsa şimdi böyle bir şey çağır

int b=fun(x,y,z);

Derleyici böyle bir işlev olmadığını söyleyen bir hata atar. Çünkü bu fonksiyon için herhangi bir prototipi yok.

İki program arasındaki farka dikkat edin.

Program 1

#include <stdio.h>
void print(int a)
{
     printf("%d",a);
}
main()
{
    print(5);
}

Burada yazdırma işlevi de bildirilmiş ve tanımlanmıştır. Çünkü işlev çağrısı tanımdan sonra geliyor. Şimdi bir sonraki programa bakın.

Program 2

 #include <stdio.h>
 void print(int a); // In this case this is essential
 main()
 {
    print(5);
 }
 void print(int a)
 {
     printf("%d",a);
 }

Bu önemlidir çünkü fonksiyon çağrısı tanımdan önce gelir, böylece derleyici böyle bir fonksiyonun olup olmadığını bilmelidir. Bu yüzden derleyiciyi bilgilendirecek işlevi beyan ederiz.

Tanım :

Bir işlevi tanımlamanın bu bölümüne Tanım denir. Fonksiyonun içinde ne yapılacağını söyler.

void print(int a)
{
    printf("%d",a);
}

2
int a; //declaration; a=10; //definitionBu tamamen yanlış. Otomatik depolama süresi nesneleri hakkında (extern gibi başka bir depolama sınıfı belirleyicisiyle bildirilmeyen bir işlev tanımının içinde bildirilen nesneler) konuşurken bunlar her zaman tanımlardır.
Joey Pabalinas

Anlaşılması gereken temel fark, bir bildirgenin "bu özelliklere sahip bir yerde (tip vb.) Var olduğunu", bir tanımın ise "bu özelliklerle ilgili bir şey beyan ediyorum ve aynı zamanda burada iyi." Bunun gibi otomatik depolama süresi nesnelerini iletemeyeceğiniz için, bunlar her zaman tanım olacaktır.
Joey Pabalinas

Her zaman unutabileceğim bazı garip typedef köşe vakaları dışında, bir kural tüm tanımların beyan olmasıdır. Bunu düşün; bir şeyi başlatırken, derleyiciye o şeyin var olduğunu ve özelliklerinin doğru olduğunu söylemeniz gerekir?
Joey Pabalinas

İlk yorumunuza göre cevap güncellendi. ancak "bir şeyi başlatırken derleyiciye o şeyin var olduğunu da söylemeniz gerekir" şeklinde yorum yapmıyorum. Örnekleme yaparken her zaman lhs türünü belirtmeyiz. Örn: a = 10. Burada herhangi bir "özellik" belirtmiyoruz.
SRIDHARAN

4

tanım, yazılı gerçek işlev anlamına gelir & beyan, örn.

void  myfunction(); //this is simple declaration

ve

void myfunction()
{
 some statement;    
}

Bu işlev fonksiyonumun tanımıdır


1
Peki ya türleri ve nesneleri?
sbi

4

Temel kural:

  • Bir bildirim derleyiciye değişkenin bellekteki verilerini nasıl yorumlayacağını söyler. Bu, her erişim için gereklidir.

  • Bir tanım , değişkeni var etmek için belleği ayırır. Bu ilk erişimden önce tam olarak bir kez gerçekleşmelidir.


2
Bu sadece nesneler için geçerlidir. Türler ve işlevler ne olacak?
Yörüngedeki Hafiflik Yarışları

4

İsimleri anlamak için önce fiillere odaklanalım.

beyan - resmi olarak ilan etmek ; , açığa

tanımlamak - açıkça ya da tamamen (birini ya da bir şeyi) göstermek ya da tanımlamak

Yani, bir şeyi beyan ettiğinizde, bunun ne olduğunu söylersiniz .

// declaration
int sum(int, int);

Bu satır ,sum iki tür argüman alan intve bir döndüren adlı bir C işlevi bildirirint . Ancak, henüz kullanamazsınız.

Gerçekte nasıl çalıştığını sağladığınızda , bunun tanımı budur.

// definition
int sum(int x, int y)
{
    return x + y;
}

3

Beyan ve tanım arasındaki farkı anlamak için montaj kodunu görmemiz gerekir:

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)

ve bu sadece tanım:

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)

Gördüğünüz gibi hiçbir şey değişmiyor.

Yalnızca derleyici tarafından kullanılan bilgileri verdiği için bildirim tanımdan farklıdır. Örneğin uint8_t derleyiciye asm işlevi movb kullanmasını söyler.

Şuna bakın:

uint def;                  |  no instructions
printf("some stuff...");   |  [...] callq   0x400450 <printf@plt>
def=5;                     |  movb    $0x5,-0x45(%rbp)

Yürütülecek bir şey olmadığı için bildirimin eşdeğer bir talimatı yok.

Ayrıca beyan derleyiciye değişkenin kapsamını bildirir.

Bildirimin, derleyici tarafından değişkenin doğru kullanımını ve bir belleğin belirli bir değişkene ne kadar süre ait olduğunu belirlemek için kullandığı bir bilgi olduğunu söyleyebiliriz.


2

Mümkün olan en genel terimlerle, bir bildirimin, hiçbir depolama alanı tahsis edilmediği bir tanımlayıcı olduğunu ve bir tanımlamanın gerçekte bildirilen bir tanımlayıcıdan depolama alanı ayırdığını söyleyemez misiniz?

İlginç bir düşünce - şablon, sınıf veya işlev tür bilgisine bağlanana kadar depolama alanı ayıramaz. Şablon tanımlayıcı bir bildirim veya tanım mı? Depolama alanı ayrılmadığından bir bildirim olmalıdır ve şablon sınıfını veya işlevini basitçe 'prototiplendiriyorsunuz'.


1
Tanımınız kendi başına yanlış değildir, ancak işlev tanımları söz konusu olduğunda "depolama tanımı" her zaman garip görünür. Şablonlarla ilgili olarak: Bu template<class T> struct foo;bir şablon bildirimidir ve bu da böyledir template<class T> void f();. Şablon tanımları, sınıf / işlev tanımlarını aynı şekilde yansıtır. (Bir şablon adının bir tür veya işlev adı olmadığını unutmayın . Bunu görebileceğiniz bir yer, bir şablonu başka bir şablonun tür parametresi olarak geçirememenizdir. Türler yerine şablonları geçmek istiyorsanız, şablon şablonu parametrelerine ihtiyacınız vardır. )
sbi

'Depolama tanımının', özellikle işlev tanımlarıyla ilgili olarak garip olduğunu kabul etti. Bildirim int foo () ve tanım int foo () {// burada bazı kodlar ..}. Genellikle küçük beynimi tanıdığım kavramlarla

2

Benzer cevapları burada bulabilirsiniz: Teknik Mülakat Soruları C .

Bir bildirim , programa bir ad sağlar; bir tanım , program içindeki bir varlığın (örneğin, tür, örnek ve işlev) benzersiz sağlar. Beyanlar belirli bir kapsamda tekrarlanabilir, belirli bir kapsamda bir isim verir.

Bir beyan, aşağıdakiler olmadıkça bir tanımdır:

  • Beyanname, bir işlevi vücudunu belirtmeden bildirir,
  • Beyanta bir extern belirteci bulunur ve başlatıcı veya işlev gövdesi yoktur,
  • Beyanname, sınıf tanımı olmayan bir statik sınıf veri üyesinin beyanıdır,
  • Beyanname bir sınıf adı tanımıdır,

Bir tanım, aşağıdakiler olmadıkça bir bildirimdir:

  • Tanım statik sınıf veri üyesini tanımlar,
  • Tanım, satır içi olmayan üye işlevini tanımlar.

1

Bu gerçekten sevimsiz olacak, ama terimleri kafamda düz tutmanın en iyi yolu:

Beyan: Picture Thomas Jefferson bir konuşma yapıyor ... "BU KAYBIN BU KAYNAK KODUNDA VAR OLDUĞUNU BURADA BÜYÜYORUM !!!"

Tanımı: sözlük sözlük, sen Foo ve aslında ne anlama geliyor.


1

Bir bildirim derleyiciye bir sembol adı sunar. Tanım, sembol için yer ayıran bir bildirimdir.

int f(int x); // function declaration (I know f exists)

int f(int x) { return 2*x; } // declaration and definition

1

GNU C kütüphane kılavuzuna göre ( http://www.gnu.org/software/libc/manual/html_node/Header-Files.html )

C dilinde bir bildirim yalnızca bir fonksiyonun veya değişkenin var olduğu bilgisini verir ve tipini verir. Bir işlev bildirimi için, argümanlarının türleri hakkında da bilgi verilebilir. Beyanların amacı, derleyicinin beyan edilen değişkenlere ve fonksiyonlara referansları doğru bir şekilde işlemesine izin vermektir. Öte yandan, bir tanım aslında bir değişken için depolama alanı ayırır veya bir işlevin ne yaptığını söyler.


0

Tanımlama başka bir konumda olacağı ve değişkeni yerel kod dosyanızda (sayfa) bildirdiğiniz için, extern depolama sınıfını kullandığınızda Bildirim ve Tanım kavramı bir tuzak oluşturacaktır. C ve C ++ arasındaki bir fark, C'de bildirimlerin normalde bir işlev veya kod sayfasının başında yapılmasıdır. C ++ 'da böyle değil. İstediğiniz bir yerde ilan edebilirsiniz.


1
Bu, beyanı tanımla karıştırır ve basit bir şekilde yanlıştır.
sbi

0

En sevdiğim örnek "int Num = 5" dir. Burada değişkeniniz 1'dir. İnt 2 olarak tanımlanmıştır. Biz

  • Yerleşik veya bir sınıf ya da yapı olabilecek bir nesnenin türünü tanımlayın.
  • Bir nesnenin adını bildirin, böylece Değişkenler, İşlevler vb. İçeren bir ad bildirildi.

Bir sınıf veya yapı, nesnelerin daha sonra kullanıldığında nasıl tanımlanacağını değiştirmenize olanak tanır. Örneğin

  • Özellikle tanımlanmamış heterojen bir değişken veya dizi bildirilebilir.
  • C ++ 'da bir uzaklık kullanarak, bildirilen adı olmayan bir nesne tanımlayabilirsiniz.

Programlamayı öğrendiğimizde, bu iki terim sıklıkla karıştırılır, çünkü her ikisini de aynı anda yaparız.


Pek çok insanın sbi'nin cevabını neden iptal ettiğini anlamıyorum. Cevabı bjhend tarafından oldukça iyi, özlü, doğru ve benimkinden çok daha zamanında onayladım. 4 yıl içinde bunu yapan ilk kişi olduğumu görünce üzüldüm.
Jason K.9

0

Yürütülebilir bir neslin aşamaları:

(1) ön işlemci -> (2) çevirmen / derleyici -> (3) bağlayıcı

Aşama 2'de (çevirmen / derleyici), kodumuzdaki beyan ifadeleri derleyiciye gelecekte kullanacağımız şeyleri söyler ve daha sonra tanımı bulabilirsiniz, yani:

çevirmen şunlardan emin olun: ne nedir? beyan anlamına gelir

ve (3) aşama (bağlayıcı) şeyleri bağlamak için tanımlamaya ihtiyaç duyar

Bağlayıcı şunlardan emin olun: nerede? tanımı anlamına gelir


0

K&R (2. baskı) boyunca serpiştirilmiş bazı çok net tanımlar vardır; onları tek bir yere koyup tek olarak okumak yardımcı olur:

"Tanım", değişkenin oluşturulduğu veya depolandığı yeri belirtir; "beyan", değişkenin niteliğinin belirtildiği ancak herhangi bir depolama alanı tahsis edilmeyen yerleri ifade eder. [S. 33]

...

Ayırt etmek önemlidir beyanı harici değişkenin ve tanımı . Bir bildirim, bir değişkenin özelliklerini (esas olarak türü) duyurur; bir tanım, depolama alanının bir kenara bırakılmasına da neden olur. Eğer çizgiler

int sp;
double val[MAXVAL]

herhangi bir fonksiyonun dışında görünür , harici değişkenleri tanımlarsp veval depolamanın bir kenara bırakılmasına neden olurlar ve aynı zamanda bu kaynak dosyanın geri kalanı için bildirim görevi görürler.

Öte yandan, çizgiler

extern int sp;
extern double val[];

beyan kaynak dosyanın geri kalanı için spbir olduğunu intve bu valbir olduğunudouble (olan boyutu başka yerde belirlenir) dizi ama onlar için değişkenleri veya rezerv depolama oluşturmaz.

Kaynak programı oluşturan tüm dosyalar arasında harici bir değişkenin yalnızca bir tanımı olmalıdır . ... Dizi boyutları tanımla belirtilmelidir, ancak isteğe bağlı olarakextern bildirimle . [s. 80-81]

...

Beyanlar, her bir tanımlayıcıya verilen yorumu belirtir; tanımlayıcıyla ilişkili depolama alanını ayırmaları gerekmez. Depolamayı ayıran beyanlara tanım denir . [s. 210]


-1

Beyanname, bir değişkene isim ve tip vermek anlamına gelir (değişken beyanı olması durumunda), örneğin:

int i;

veya gövdesiz bir işleve ad, döndürme türü ve parametre (ler) türünü verin (işlev bildirimi durumunda), örneğin:

int max(int, int);

tanım ise bir değişkene değer atamak anlamına gelir (değişken tanımlaması durumunda), örneğin:

i = 20;

veya bir işleve gövde (işlevsellik) sağlama / ekleme işlev tanımı olarak adlandırılır, örneğin:

int max(int a, int b)
{
   if(a>b)   return a;
   return b;  
}

birçok zaman beyanı ve tanımı aşağıdaki gibi birlikte yapılabilir:

int i=20;

ve:

int max(int a, int b)
{
    if(a>b)   return a;
    return b;    
} 

Yukarıdaki durumlarda değişken ive function max().


bir değişkene / işleve değer / gövde atanacaksa tanımın asıl ortalaması, bildirim ise ad sağlamak, değişkene / işleve yazmak anlamına gelir
Puneet Purohit

Bir şeyi bir değer atamadan tanımlayabilirsiniz.
Yörüngedeki Hafiflik Yarışları

1
Tıpkı şöyle:int x;
Orbit'teki Hafiflik Yarışları

onun değişken bir beyanı değil onun tanımı
Puneet Purohit

2
Hayır, ikisi de. Tanımı başlatma ile karıştırıyorsunuz.
Yörüngedeki Hafiflik Yarışları
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.