Значення обох ухиляється від мене.
Значення обох ухиляється від мене.
Відповіді:
Декларація вводить ідентифікатор і описує його тип, будь то тип, об'єкт або функція. Декларація - це те, що компілятору потрібно прийняти посилання на цей ідентифікатор. Це декларації:
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 наводить кілька прикладів. Серед них:
[Приклад: [...] struct S {int a; int b; }; // визначає S, S :: а й S :: б [...] struct S; // оголошує S —Приклад
Підводячи підсумок: стандарт C ++ вважає , struct x;
щоб бути декларацією і визначенням . (Іншими словами, "переадресація декларації" є неправильним , оскільки в C ++ немає інших форм оголошень класу.)struct x {};
Завдяки лібту (Йоханнесу Шаубу), який розкопав фактичну главу та вірш в одній із своїх відповідей.
extern int i
є декларацією, оскільки вона просто вводить / вказує i
. У extern int i
кожній одиниці компіляції може бути стільки, скільки ви хочете. int i
однак це визначення. Він позначає простір для цілого числа, що знаходиться в цій одиниці перекладу, і радить линкеру зв’язати всі посилання на i
цю сутність. Якщо у вас є більш-менш точно одне з цих визначень, лінкер подасть скаргу.
int i;
у файлі / глобальній області чи області функцій - це визначення як у C, так і в C ++. У C тому, що він виділяє сховище, а в C ++ тому, що у нього немає зовнішнього специфікатора або специфікації зв'язку. Вони складають одне і те ж, про що говорить sbi: в обох випадках ця декларація вказує об'єкт, до якого повинні бути пов'язані всі посилання на "i" у цій галузі.
struct A { double f(int, double); double f(int, double); };
невірно, звичайно. Це дозволено в інших місцях. Є кілька місць , де ви можете оголосити речі, але не визначає, теж: void f() { void g(); }
дійсно, але не таке: void f() { void g() { } };
. Що таке визначення і то , що декларація має тонкі правила , коли справа доходить до шаблонів - стережіться! +1 за хорошу відповідь , хоча.
Із стандартного розділу C ++, розділ 3.1:
А декларація вводить імена в ЕП або redeclares імена , введені раніше декларацій. Декларація визначає тлумачення та ознаки цих назв.
У наступному абзаці зазначено (моє наголос), що декларація - це визначення, якщо ...
... він оголошує функцію, не вказуючи тіло функції:
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
Тепер для великої причини, чому важливо зрозуміти різницю між декларацією та визначенням: Правило одного визначення . З розділу 3.2.1 стандарту C ++:
Жодна одиниця перекладу не містить більше одного визначення будь-якої змінної, функції, типу класу, типу перерахування чи шаблону.
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.
Декларація
Декларації повідомляють компілятору про те, що існує програмний елемент або ім'я. Декларація вносить в програму одне або більше імен. Декларації в програмі можуть виникати не один раз. Тому класи, структури, перелічені типи та інші визначені користувачем типи можуть бути оголошені для кожної одиниці компіляції.
Визначення
Визначення вказують, який код або дані описує ім'я. Ім'я повинно бути оголошено, перш ніж воно може бути використане.
class foo {};
це визначення класу , чи не так?
Згідно зі стандартом C99, 6,7 (5):
Декларація вказує інтерпретацію та атрибути набору ідентифікаторів. Визначення ідентифікатора є декларацією для цього ідентифікатора , що:
Згідно зі стандартом C ++, 3.1 (2):
Декларація - це визначення, якщо воно не оголошує функцію, не вказуючи тіло функції, воно не містить специфікатора extern або специфікації зв'язку і не є ініціалізатором, ані тіло функції, воно оголошує статичний член даних в декларації класу, це оголошення імені класу, або це декларація typedef, декларація з використанням або директива використання.
Тоді є кілька прикладів.
Настільки цікаво (чи ні, але я трохи здивований цим) typedef int myint;
- це визначення в C99, але лише декларація в C ++.
typedef
, чи не означає це, що це може повторюватися в C ++, але не в C99?
З wiki.answers.com:
Термін декларація означає (в С), що ви повідомляєте компілятору про тип, розмір, а в разі оголошення функції, тип і розмір його параметрів будь-якої змінної або визначений користувачем тип або функцію у вашій програмі. У пам'яті не залишається місця для жодної змінної у разі декларування. Однак компілятор знає, скільки місця потрібно резервувати, якщо буде створена змінна цього типу.
Наприклад, наведено всі декларації:
extern int a;
struct _tagExample { int a; int b; };
int myFunc (int a, int b);
Визначення з іншого боку означає, що в доповненнях до всіх речей, які робить декларація, місце також зарезервоване в пам'яті. Ви можете сказати "ВИЗНАЧЕННЯ = ДЕКЛАРАЦІЯ + РЕЗЕРВАЦІЯ ПРОСТА" нижче: приклади визначення:
int a;
int b = 0;
int myFunc (int a, int b) { return a + b; }
struct _tagExample example;
див. відповіді .
struct foo {};
це визначення , а не декларація. Декларація foo
буде struct foo;
. З цього приводу компілятор не знає, скільки місця можна резервувати для foo
об’єктів.
struct foo;
є декларацією, але вона не вказує компілятору розміру foo. Я додам, що struct _tagExample { int a; int b; };
це визначення. Тож у цьому контексті оманливо називати це декларацією. Звичайно, це одне, оскільки всі визначення є деклараціями, але ти, здається, припускаєш, що це не визначення. Це визначення _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);
Компілятор видасть помилку, сказавши, що такої функції немає. Тому що у нього немає жодного прототипу для цієї функції.
Зверніть увагу на різницю між двома програмами.
Програма 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
Це абсолютно неправильно. Коли ми говоримо про об'єкти тривалості автоматичного зберігання (об’єкти, оголошені всередині визначення функції, які не оголошені з іншим специфікатором класу зберігання, як extern), це завжди визначення.
Практичне правило:
Декларація повідомляє компілятор , як інтерпретувати дані змінні в пам'яті. Це потрібно для кожного доступу.
Визначення залишає пам'ять , щоб зробити змінну існуючі. Це має відбутися рівно один раз до першого доступу.
Щоб зрозуміти іменники, давайте спочатку зосередимося на дієсловах.
заявляти - оголошувати офіційно; проголошувати
визначити - показати або описати (когось чи щось) чітко та повністю
Отже, коли декларуєте щось, ви просто повідомте, що це таке .
// declaration
int sum(int, int);
Цей рядок оголошує функцію 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)
Декларація не має еквівалентної інструкції, тому що вона нічого не повинна бути виконана.
Крім того, декларація повідомляє компілятору область застосування змінної.
Можна сказати, що декларація - це інформація, яка використовується компілятором для встановлення правильного використання змінної та за те, як довго деяка пам'ять належить певній змінній.
Не могли б ви сказати на найбільш загальних можливих термінах, що декларація - це ідентифікатор, в якому не виділяється сховище, а визначення фактично виділяє сховище з оголошеного ідентифікатора?
Одна цікава думка - шаблон не може виділити сховище, поки клас чи функція не пов'язані з інформацією про тип. Так ідентифікатор шаблону є декларацією чи визначенням? Це має бути декларація, оскільки не виділено місця для зберігання даних, і ви просто "прототипуєте" шаблон шаблону або функції.
template<class T> struct foo;
шаблону , і це так . Визначення дзеркальних визначень шаблонів / функцій шаблону аналогічно. (Зверніть увагу, що назва шаблону не є ім'ям типу або функції . Одне місце, де ви можете бачити це, коли ви не можете передати шаблон як параметр типу іншого шаблону. Якщо ви хочете передавати шаблони замість типів, вам потрібні параметри шаблону шаблону. )template<class T> void f();
Знайдіть подібні відповіді тут: Питання технічного інтерв'ю в С .
Декларація надає ім'я до програми; визначення забезпечує однозначне опис об'єкта (наприклад , типу, екземпляр, і функцію) в рамках програми. Декларації можна повторити в заданому обсязі, воно вводить назву в задану область.
Декларація - це визначення, якщо:
Визначення - це декларація, якщо:
Це буде звучати насправді зухвало, але це найкращий спосіб, коли мені вдалося дотримати умови прямо в голові:
Декларація: Малюнок Томаса Джефферсона, який виступає з промовою ... "Я ТЕХНЯ ДЕКЛАРУЮТЬСЯ, ЩО ЦЕ ФОТО ІСНУЄ В ЦЬОМУ ДЖЕРЕЛАВІ КОДІ !!!"
Визначення: малюйте словник, ви шукаєте Foo і що він насправді означає.
Відповідно до посібника з бібліотеки GNU C ( http://www.gnu.org/software/libc/manual/html_node/Header-Files.html )
В C декларація просто надає інформацію про те, що функція або змінна існує, і надає її тип. Для декларації функції також може бути надана інформація про типи її аргументів. Мета декларацій - дозволити компілятору правильно обробляти посилання на оголошені змінні та функції. Дефініція, з іншого боку, фактично виділяє сховище для змінної або говорить про те, що робить функція.
Концепція Декларації та Визначення створить підводний камінь, коли ви використовуєте клас зовнішнього зберігання, оскільки ваше визначення буде в якомусь іншому місці, і ви декларуєте змінну у вашому локальному файлі коду (сторінки). Однією з різниць між C і C ++ є те, що в C ви декларації робляться нормально на початку функції або кодової сторінки. У C ++ це не так. Ви можете декларувати в обраному вами місці.
Мій улюблений приклад - "int Num = 5", тут ваша змінна значення 1. визначена як int 2. оголошена як Num і 3. instantiated зі значенням п'ять. Ми
Клас або структура дозволяє змінювати, як будуть визначені об'єкти при подальшому використанні. Наприклад
Коли ми вивчаємо програмування, ці два терміни часто плутають, тому що ми часто робимо обидва одночасно.
Етапи виконуваного покоління:
(1) попередній процесор -> (2) перекладач / компілятор -> (3) лінкер
На етапі 2 (перекладач / компілятор) заяви заяви в нашому коді повідомляють компілятору, що ці речі ми будемо використовувати в майбутньому, і ви можете знайти визначення пізніше, тобто:
перекладач переконайтесь, що: що таке? означає декларацію
та (3) етап (лінкер) потребує визначення для зв'язування речей
Linker переконайтесь, що: де що? означає визначення
Є декілька дуже чітких визначень, викладених у K&R (2-е видання); це допомагає розмістити їх в одному місці та прочитати як одне:
"Визначення" відноситься до місця, де створена змінна або призначена сховище; "декларація" відноситься до місць, де вказано характер змінної, але зберігання не виділено. [с. 33]
...
Важливо розрізняти декларацію зовнішньої змінної та її визначення . Декларація оголошує властивості змінної (насамперед її тип); Визначення також приводить до відміни пам’яті. Якщо лінії
int sp; double val[MAXVAL]
з'являються поза будь-якою функцією, вони визначають зовнішні змінні
sp
таval
викликають відкладення сховища, а також служать декларацією для решти цього вихідного файлу.З іншого боку, лінії
extern int sp; extern double val[];
оголосити для решти вихідного файлу, що
sp
єint
іval
цеdouble
масив (розмір якого визначено в іншому місці), але вони не створюють змінних і не резервують для них сховище.Серед усіх файлів, що складають вихідну програму, повинно бути лише одне визначення зовнішньої змінної. ... Розміри масиву повинні бути вказані з визначенням, але необов'язкові з
extern
оголошенням. [с. 80-81]...
Декларації визначають інтерпретацію, що надається кожному ідентифікатору; вони не обов'язково резервують сховище, пов'язане з ідентифікатором. Декларації, що резервують зберігання, називаються визначеннями . [с. 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
та function max()
.
int x;