Her ikisinin anlamı beni atlatıyor.
Her ikisinin anlamı beni atlatıyor.
Yanıtlar:
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ı.
extern int i
yalnızca bir açıklama / belirtme olduğu için bir bildirimdir i
. extern int i
Her derleme biriminde istediğiniz kadarını kullanabilirsiniz . int i
ancak bir tanımdır. Tamsayı için bu çeviri biriminde olması gereken alanı belirtir ve bağlayıcıya i
bu 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.
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.
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.
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;
... extern
baş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 typedef
veya using
ifadedir.
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.
struct x {static int b = 3; };
?
b
beyan edilmedikçe aslında yasa dışıdır const
. Bkz. Stackoverflow.com/a/3536513/1858225 ve daniweb.com/software-development/cpp/threads/140739/… .
Beyan: "Bir yerlerde bir foo var."
Tanım: "... ve işte burada!"
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.
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.
class foo {};
olan bir sınıf tanımı bu değil mi?
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:
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.
typedef
, C ++ 'da tekrarlanabileceği anlamına gelmez, ancak C99'da tekrarlanamaz mı?
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 .
struct foo {};
bir tanım değil , bir tanımdır . Bir beyanı foo
olurdu struct foo;
. Bundan sonra, derleyici foo
nesneler için ne kadar yer ayıracağını bilmiyor .
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.
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 :
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!")
;
Yukarıdaki listede C ++ 03'ten devralınan ek maddeler:
int add(int x, int y);
extern int a;
veyaextern "C" { ... };
class C { static int x; };
struct Point;
typedef int Int;
using std::cout;
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
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);
}
int a; //declaration; a=10; //definition
Bu 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.
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.
İ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 int
ve 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;
}
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.
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'.
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. )
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:
Bir tanım, aşağıdakiler olmadıkça bir bildirimdir:
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.
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.
En sevdiğim örnek "int Num = 5" dir. Burada değişkeniniz 1'dir. İnt 2 olarak tanımlanmıştır. Biz
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
Programlamayı öğrendiğimizde, bu iki terim sıklıkla karıştırılır, çünkü her ikisini de aynı anda yaparız.
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
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ımlar
sp
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
sp
bir olduğunuint
ve buval
bir 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ı olarak
extern
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]
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 i
ve function max()
.
int x;