В чем разница между определением и декларацией?

avatar
Maciek
11 сентября 2009 в 12:27
391737
25
940

Значение обоих ускользает от меня.

Источник
David Thornley
11 сентября 2009 в 14:10
13

Честно говоря, у меня было много проблем с изучением того, что есть что, поэтому я не нашел очевидных имен. У меня не было проблем со значениями, просто с тем, какие имена ассоциировать со значениями.

dmckee --- ex-moderator kitten
11 сентября 2009 в 14:17
1

Мы подробно рассмотрели это: coderhelper.com/questions/671925/…

Steve Jessop
11 сентября 2009 в 14:23
6

Тем не менее, это не повторяющийся вопрос, поскольку здесь задается вопрос о C / C ++, тогда как другой вопрос касается всех языков или ни одного в целом. Он просто имеет повторяющиеся ответы (поскольку в этом другом вопросе некоторые ответы игнорировали все языки, кроме C и / или C ++).

Marco Leogrande
27 июля 2012 в 06:39
5

@DavidThornley Я использую этот трюк: определение дает более тонкое описание данной переменной или функции. Чтобы запомнить это, я напоминаю, что середина слова «определение» имеет сходство со словом «тоньше». :)

Lightness Races in Orbit
14 апреля 2013 в 17:41
2

@MarcoLeogrande: Или "определенно"!

Mooing Duck
26 июня 2013 в 19:51
1

Гораздо более интересный вопрос - «разница между объявлением и прототипом »: coderhelper.com/questions/5481579/…

Griffin
18 июля 2013 в 20:41
1

Вот хорошая статья, в которой объясняется, как ключевое слово extern и выделение памяти связаны с объявлениями / определениями: dreamincode.net/forums/topic/171468-declarations-vs-definitions

barlop
19 сентября 2015 в 12:48
1

это может помочь cprogramming.com/declare_vs_define.html

SuwaneeCreek
10 августа 2017 в 16:05
0

Если вам сложно вспомнить, что есть что, помните, что термины расположены в алфавитном порядке - Декларация, Определение, Ссылка. Обычно они должны появляться в таком порядке.

TylerH
26 декабря 2018 в 22:35
0

Возможный дубликат Что такое определения, объявления и назначения C ++?

Ответы (25)

avatar
sbi
11 сентября 2009 в 12:43
935

В объявлении вводится идентификатор и описывается его тип, будь то тип, объект или функция. Объявление - это то, что требуется компилятору , чтобы принимать ссылки на этот идентификатор. Это объявления:

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

A определение фактически создает / реализует этот идентификатор. Это то, что нужно компоновщику , чтобы связать ссылки на эти объекты. Это определения, соответствующие приведенным выше объявлениям:

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 ++ нет.)

Спасибо Литбу (Йоханнесу Шаубу), который откопал настоящую главу и стих в одном из своих ответов.

San Jacinto
11 сентября 2009 в 12:56
1

Законно ли это множественное объявление согласно СТАНДАРТУ, или вашему компилятору, или как? Я не могу делать это несколько раз в одной и той же области.

Steve Jessop
11 сентября 2009 в 13:09
2

@unknown: либо ваш компилятор сломан, либо вы неправильно скопировали код sbi. Например, 6.7.2 (2) в N1124: «Все объявления, которые относятся к одному и тому же объекту или функции, должны иметь совместимый тип; в противном случае поведение не определено».

Steve Jessop
11 сентября 2009 в 13:10
1

@unknown: или, возможно, он выдает предупреждение, которое вы продвигаете к ошибке.

San Jacinto
11 сентября 2009 в 13:13
1

да, это был плохой компилятор. Я попробовал еще раз на GCC, и он работает. Подсказка: не используйте компилятор c imagecraft.

Steve Jessop
11 сентября 2009 в 13:13
1

«Если вы определяете что-то более одного раза, компоновщик не знает, на какое из определений ссылаться». Хотя вы можете обойтись без этого, если определения находятся в разных единицах перевода, эквивалентны и имеют соответствующие модификаторы, сообщающие компоновщику, что можно их свернуть (в основном «встроенные»).

sbi
11 сентября 2009 в 13:39
1

@onebyone: Да, есть исключения, особенно встраиваемые функции, которые компилятор не встраивает по какой-либо причине. Однако я не хотел добавлять исключения к ответу, если спрашивающий не знает разницы между объявлением и определением. Меня очень удивили все вводящие в заблуждение ответы (которые с тех пор исчезли). Я не думал, что такие заблуждения настолько распространены.

Brian Postow
11 сентября 2009 в 13:59
1

Я бы сказал, что "int i;" также является объявлением, и вы никогда не ОПРЕДЕЛЯЕТЕ переменную типа int ... но кроме этого, +1

David Thornley
11 сентября 2009 в 14:05
4

@ Брайан: "extern int i;" говорит, что я где-то int, не беспокойтесь об этом. "int i;" означает, что i является целым числом, а его адрес и область действия определяются здесь.

sbi
11 сентября 2009 в 14:09
13

@ Брайан: Вы ошибаетесь. extern int i - это объявление, поскольку оно просто вводит / указывает i. Вы можете иметь столько extern int i в каждой единице компиляции, сколько хотите. int i, однако, является определением. Он обозначает пространство для целого числа, которое должно быть в этой единице трансляции, и советует компоновщику связать все ссылки на i с этим объектом. Если у вас больше или меньше одного из этих определений, компоновщик пожалуется.

Steve Jessop
11 сентября 2009 в 14:14
5

@Brian int i; в области файла / глобальной области или области функции является определением как в C, так и в C ++. В C, потому что он выделяет память, и в C ++, потому что у него нет спецификатора extern или спецификации связи. Это одно и то же, о чем говорит sbi: в обоих случаях это объявление определяет объект, с которым должны быть связаны все ссылки на «i» в этой области.

Johannes Schaub - litb
11 сентября 2009 в 16:54
4

@unknown, будьте осторожны, вы не можете повторно объявить элементы в классе области: struct A { double f(int, double); double f(int, double); }; недопустимы, конечно. Хотя это разрешено в других местах. Есть места, где вы можете объявлять вещи, но не определять их: void f() { void g(); } действительный, но не следующие: void f() { void g() { } };. Что такое определение и какое объявление содержит тонкие правила, когда дело касается шаблонов - будьте осторожны! +1 за хороший ответ.

Marc van Leeuwen
26 мая 2014 в 22:44
1

Моя основная проблема с этим ответом заключается в том, что первоначальное объяснение того, что определение - это то, что нужно компоновщику, бесполезно в случае определения класса. Компоновщику никогда не нужно связываться с самим классом (который, как и typedef, на самом деле имеет только декларативный характер); однако он может ссылаться на (статический) экземпляр класса, на методы класса или на класс vtable, для нет для которых используется определение класса. Но компилятор для многих вещей не видит ничего, кроме объявления класса. Поэтому называть это классом определением на самом деле просто вопрос соглашения, не оправданного данным объяснением.

Thomson
1 августа 2014 в 06:12
2

«Вместо декларации можно использовать определение». Это может быть неверно. Если это допустимо, только если не будет введено повторяющееся определение.

sbi
2 августа 2014 в 07:21
1

@Thomson: "Идентификатор можно объявлять сколько угодно раз. [...] Однако он должен быть определен ровно один раз. "

Thomson
2 августа 2014 в 14:10
1

@sbi спасибо за разъяснения. Я знаю, что приведенное мною утверждение верно с некоторым условием. Просто условие немного далекое от процитированного предложения.

Koray Tugay
21 мая 2015 в 15:53
0

Цитата с этой страницы: pubs.opengroup.org/onlinepubs/7908799/xsh/unistd.h.html «Следующие элементы объявлены как функции и могут также определяться как макросы. Для использования должны быть предоставлены прототипы функций. с компилятором ISO C. " Что подразумевается под «прототипами функций должны быть предоставлены для использования с компилятором ISO C». ?

Koray Tugay
21 мая 2015 в 15:55
0

цитата из вашего ответа: «тогда компоновщик не знает, на что ссылаться», вы имеете в виду, тогда компоновщик не знает, на что ссылается ссылка »?

sbi
23 мая 2015 в 07:00
0

@Koray: Я даже не упомянул макросы C, потому что они такие странные груди. По сути, вы не можете объявить макрос, вы можете только определить его. Но препроцессор - это не настоящий компилятор, а в любом случае простой текстовый процессор ... В K&R C функции не объявляли свои параметры, поэтому все, что вам нужно в качестве объявления, - это их имя. В ISO C вам нужны правильные декларации. Полагаю, это то, к чему относится цитируемый вами комментарий. И нет, я имел в виду то предложение, которое я написал.

Destructor
25 августа 2015 в 08:34
0

@sbi: почему ideone.com/wIBBTi не работает при компиляции. может быть сколько угодно объявлений идентификатора, но должно быть ровно одно определение, как я думаю. Я не определил и не вызвал ни одну из функций. Почему ошибка в программе? какова причина?

Destructor
25 августа 2015 в 08:35
0

@ JohannesSchaub-litb: вы говорите, что double f (int, double); двойной f (int, double); } разрешено где угодно, но не в области класса. тогда почему это не разрешено в глобальном масштабе. Почему ideone.com/wIBBTi не удается компиляции?

sbi
25 августа 2015 в 13:39
1

@Pravasi: вы объявляете две функции с одинаковыми именами, но разными типами возвращаемых данных. В C ++ это недопустимо, вы можете перегружать функции только тогда, когда их параметры различаются.

Zebrafish
24 декабря 2016 в 01:04
0

@sbi Я хотел отредактировать ваш ответ, но не уверен в этом. Я пробовал помещать extern перед структурой или классом, и, похоже, он находится в категории с функциями, т.е. добавление extern, похоже, не имеет значения. Я говорю о bit: // no extern, разрешенном для объявлений типов - в верхней части вашего ответа.

YuZ
28 февраля 2017 в 09:10
0

делает int x; неявно инициировать x равным нулю, тогда как extern int x этого не делает?

sbi
28 февраля 2017 в 17:55
0

@ user3921720: Нет.

YuZ
2 марта 2017 в 16:15
0

тогда почему int x; определение и extern int x объявление?

sbi
27 марта 2017 в 14:28
0

Поскольку один определяет переменную x типа int, а другой объявляет x как переменную типа int, которая должна быть определена в другом месте? пожимает плечами Я действительно не знаю, что здесь сказать. (Вы пробовали прочитать мой ответ? Он это объясняет.)

陳 力
5 декабря 2017 в 10:42
0

Может ввести в заблуждение новичка: int x также включает декларацию. Поскольку definition является subset из declaration

sbi
9 декабря 2017 в 09:43
0

@czxyl: «Вместо объявления можно использовать определение.»

rehctawrats
8 февраля 2018 в 08:46
0

Было бы здорово, если бы кто-нибудь мог расширить этот ответ, включив в него значение инициализации . Например, coderhelper.com/q/23345554/6060872

Enlico
17 мая 2020 в 16:09
0

@sbi, Определение может использоваться вместо объявления , похоже, не принимает во внимание, что непостоянный статический член данных должен быть инициализирован вне очереди, поэтому его объявление в классе не может быть заменено определением.

sbi
11 июля 2020 в 20:59
0

@EnricoMariaDeAngelis: Да, я вообще не посещал занятия. Прочтите комментарий Йоханнеса по этому поводу.

avatar
sun1211
15 марта 2021 в 04:09
0

Объявление переменной предназначено для сообщения компилятору следующей информации: имя переменной, тип значения, которое она содержит, и начальное значение, если оно есть. то есть объявление дает подробную информацию о свойствах переменной. Принимая во внимание, что Определение переменной говорит, где хранится переменная. т.е. память для переменной выделяется во время определения переменной.

avatar
Sindhukumari P
15 октября 2020 в 23:02
0

Объявление - это когда примитив или объектная ссылочная переменная или метод создается без присвоения значения или объекта. int a; final int a;

Определение означает присвоение значения или объекта соответственно int a = 10;

Инициализация означает выделение памяти для соответствующей переменной или объекта.

avatar
Brad Solomon
16 августа 2019 в 13:53
0

Есть несколько очень четких определений, разбросанных по всему изданию 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]

avatar
Jeet Parikh
8 августа 2018 в 04:06
0

Этапы создания исполняемого файла:

(1) препроцессор -> (2) транслятор / компилятор -> (3) компоновщик

На этапе 2 (транслятор / компилятор) операторы объявления в нашем коде сообщают компилятору, что эти вещи мы собираемся использовать в будущем, и вы можете найти определение позже, что означает:

переводчик убедитесь, что: что есть что? означает декларацию

и (3) этап (компоновщик) требует определения для связывания вещей

Компоновщик убедитесь, что: где что? означает определение

avatar
LinuxBabe
7 марта 2018 в 23:06
1

Согласно руководству по библиотеке GNU C (http://www.gnu.org/software/libc/manual/html_node/Header-Files.html)

В C объявление просто предоставляет информацию о том, что функция или переменная существует, и дает ее тип. Для объявления функции также может быть предоставлена ​​информация о типах ее аргументов. Цель объявлений - позволить компилятору правильно обрабатывать ссылки на объявленные переменные и функции. С другой стороны, определение фактически выделяет память для переменной или говорит, что делает функция.

avatar
Karoly Nyisztor
20 февраля 2018 в 18:56
6

Чтобы понять существительные, давайте сначала сосредоточимся на глаголах.

объявить - объявить официально; объявить

определить - показать или описать (кого-то или что-то) ясно и полно

Итак, когда вы что-то объявляете, вы просто говорите , что это такое .

// declaration
int sum(int, int);

В этой строке объявляется функция C с именем sum, которая принимает два аргумента типа int и возвращает int. Однако вы пока не можете его использовать.

Когда вы предоставляете , как это работает на самом деле , это его определение.

// definition
int sum(int x, int y)
{
    return x + y;
}
avatar
princio
3 октября 2017 в 15:30
3

Чтобы понять разницу между объявлением и определением, нам нужно увидеть код сборки:

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)

Объявление не имеет эквивалентной инструкции, потому что это не то, что нужно выполнить.

Кроме того, объявление сообщает компилятору объем переменной.

Мы можем сказать, что объявление - это информация, используемая компилятором для определения правильного использования переменной и того, как долго часть памяти принадлежит определенной переменной.

avatar
hdante
10 мая 2017 в 04:54
1

Объявление представляет компилятору имя символа. Определение - это объявление, которое выделяет место для символа.

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

int f(int x) { return 2*x; } // declaration and definition
avatar
SRIDHARAN
4 января 2017 в 12:13
6

Определение:

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);
}

Joey Pabalinas
11 февраля 2018 в 18:06
3

int a; //declaration; a=10; //definition Это совершенно неверно. Когда речь идет об объектах автоматической продолжительности хранения (объекты, объявленные внутри определения функции, которые не объявлены с другим спецификатором класса хранения, например extern), это определения , всегда .

Joey Pabalinas
11 февраля 2018 в 18:06
0

Основное различие, которое нужно понять, заключается в том, что в объявлении говорится, что «где-то существует вещь, имеющая эти черты (тип и т. Д.)», Тогда как в определении говорится: «Я объявляю вещь с этими чертами, и я также создаю ее здесь как хорошо." Поскольку вы не можете пересылать подобные объекты с объявлением автоматической продолжительности хранения, они всегда будут определениями.

Joey Pabalinas
11 февраля 2018 в 18:07
0

За исключением, может быть, некоторых странных угловых случаев typedef, о которых я всегда забываю, практическое правило состоит в том, что Все определения являются объявлениями. Подумайте об этом; когда вы создаете экземпляр чего-либо, вам также необходимо сообщить компилятору, что этот объект существует и какие его свойства верны?

SRIDHARAN
31 января 2020 в 17:32
0

Обновил ответ согласно вашему первому комментарию. однако я не согласен с этим комментарием: «когда вы создаете что-то, вам также необходимо сообщить компилятору, что эта вещь существует». Мы не всегда указываем тип lhs при создании экземпляра. Пример: а = 10. Мы не указываем здесь никаких «черт».

avatar
Jason K.
9 октября 2016 в 23:15
0

Мой любимый пример - "int Num = 5", здесь ваша переменная 1. определена как int 2. объявлена ​​как Num и 3. создана со значением пять. Мы

  • Определите тип объекта, который может быть встроенным, классом или структурой.
  • Объявить имя объекта, чтобы было объявлено все, что имеет имя, включая переменные, функции и т. Д.

Класс или структура позволяют изменить способ определения объектов при их последующем использовании. Например,

  • Можно объявить гетерогенную переменную или массив, которые специально не определены.
  • Используя смещение в C ++, вы можете определить объект, у которого нет объявленного имени.

Когда мы изучаем программирование, эти два термина часто путают, потому что мы часто делаем оба одновременно.

Jason K.
9 октября 2016 в 23:25
0

Я не понимаю, почему так много людей поддержали ответ SBI. Я поддержал ответ bjhend, который был довольно хорошим, кратким, точным и гораздо более своевременным, чем мой. Мне было грустно видеть, что я был первым, кто сделал это за 4 года.

avatar
achoora
13 ноября 2014 в 11:44
0

Концепция объявления и определения сформирует ловушку, когда вы используете класс хранения extern, потому что ваше определение будет в каком-то другом месте, а вы объявляете переменную в своем локальном файле кода (странице). Одно различие между C и C ++ заключается в том, что в C объявления обычно выполняются в начале функции или кодовой страницы. В C ++ это не так. Вы можете подать заявление в любом месте по вашему выбору.

sbi
31 июля 2015 в 22:27
1

Это путает декларацию с определением и совершенно неверно.

avatar
Santosh
12 марта 2014 в 18:01
2

Похожие ответы можно найти здесь: Вопросы для технических собеседований на C.

Объявление предоставляет имя программе; определение предоставляет уникальное описание объекта (например, типа, экземпляра и функции) в программе. Объявления могут повторяться в заданной области, это вводит имя в заданную область.

Объявление является определением, если:

  • Объявление объявляет функцию без указания ее тела,
  • Объявление содержит спецификатор extern, но не инициализатор или тело функции,
  • Объявление - это объявление члена данных статического класса без определения класса,
  • Объявление - это определение имени класса,

Определение является декларацией, если:

  • Определение определяет член данных статического класса,
  • Определение определяет не встроенную функцию-член.
avatar
It'sPete
2 июля 2013 в 22:46
1

Это будет звучать очень глупо, но это лучший способ, которым я мог держать термины прямо в голове:

Заявление: Представьте, что Томас Джефферсон выступает с речью ... «Я НАСТОЯЩИМ ЗАЯВЛЯЮ, ЧТО ЭТО ФУТ СУЩЕСТВУЕТ В ЭТОМ ИСХОДНОМ КОДЕ !!!»

Определение: представьте словарь, вы ищете Foo и то, что это на самом деле означает.

avatar
legends2k
26 июня 2013 в 19:43
14

Обновление C ++ 11

Поскольку я не вижу ответа, относящегося к C ++ 11, вот один.

Объявление - это определение , если оно не объявляет a / n:

  • непрозрачное перечисление - enum X : int;
  • параметр шаблона - T в template<typename T> class MyArray;
  • объявление параметра - x и y в int add(int x, int y);
  • объявление псевдонима - using IntVector = std::vector<int>;
  • статическое объявление assert - static_assert(sizeof(int) == 4, "Yikes!")
  • объявление атрибута (определяется реализацией)
  • пустая декларация ;

Дополнительные предложения, унаследованные от C ++ 03 указанным выше списком:

  • объявление функции - добавить в int add(int x, int y);
  • спецификатор extern, содержащий объявление или спецификатор связи - extern int a; или extern "C" { ... };
  • статический элемент данных в классе - x в class C { static int x; };
  • объявление класса / структуры - struct Point;
  • объявление typedef - typedef int Int;
  • с использованием декларации - using std::cout;
  • директива using - 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
avatar
Puneet Purohit
3 января 2013 в 06:54
-1

Объявление означает присвоение переменной имени и типа (в случае объявления переменной), например:

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().

Puneet Purohit
3 января 2013 в 06:57
0

фактическое значение определения, если присвоить значение / тело переменной / функции, тогда как объявление означает указать имя, тип для переменной / функции

Lightness Races in Orbit
14 апреля 2013 в 17:36
0

Вы можете определить что-либо, не присваивая ему значения.

Puneet Purohit
15 апреля 2013 в 11:27
0

это объявление переменной x, а не ее определение

Lightness Races in Orbit
15 апреля 2013 в 14:10
2

Нет, и то, и другое. Вы путаете определение с инициализацией.

avatar
bjhend
17 апреля 2012 в 18:15
4

Практическое правило:

  • Объявление сообщает компилятору, как интерпретировать данные переменной в памяти. Это необходимо для каждого доступа.

  • Определение резервирует память, чтобы переменная существовала. Это должно произойти ровно один раз перед первым доступом.

Lightness Races in Orbit
14 апреля 2013 в 17:38
2

Это справедливо только для объектов. А как насчет типов и функций?

avatar
user565367
7 января 2011 в 04:42
4

определение означает, что фактическая функция написана, а объявление означает простую функцию объявления например, для

void  myfunction(); //this is simple declaration

и

void myfunction()
{
 some statement;    
}

это определение функции myfunction

sbi
24 апреля 2013 в 12:57
1

А как насчет типов и объектов?

avatar
plinth
11 сентября 2009 в 18:20
142

Объявление: «Где-то существует foo.»

Определение: «... и вот оно!»

Gab是好人
11 февраля 2016 в 14:41
4

Объявление предназначено для того, чтобы компилятор принял имя (чтобы сообщить компилятору, что имя допустимо, имя вводится намеренно, а не как опечатка). Определение - это то, где связано имя и его содержание. Определение используется компоновщиком, чтобы связать ссылку на имя с содержимым имени.

avatar
Johannes Schaub - litb
11 сентября 2009 в 18:15
51

В 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.
avatar
user154171
11 сентября 2009 в 14:46
2

Не могли бы вы заявить в самых общих терминах, что объявление - это идентификатор, в котором память не выделяется, а определение фактически выделяет память из объявленного идентификатора?

Одна интересная мысль - шаблон не может выделить память, пока класс или функция не связаны с информацией о типе. Так является ли идентификатор шаблона декларацией или определением? Это должно быть объявление, поскольку хранилище не выделяется, а вы просто «прототипируете» шаблонный класс или функцию.

sbi
11 сентября 2009 в 15:09
1

Ваше определение само по себе не ошибочно, но «определение хранилища» всегда кажется неудобным, когда дело доходит до определений функций. Что касается шаблонов: этот template<class T> struct foo; является шаблоном декларацией , как и этот template<class T> void f();. Определения шаблонов аналогичным образом отражают определения классов / функций. (Обратите внимание, что имя шаблона не является типом или именем функции . Это можно увидеть, когда вы не можете передать шаблон в качестве другого типа шаблона. параметр. Если вы хотите передавать шаблоны вместо типов, вам потребуются параметры шаблона шаблона.)

user154171
11 сентября 2009 в 15:59
0

Согласились, что «определение хранилища» неудобно, особенно в отношении определений функций. Объявление - int foo (), а определение - int foo () {// здесь какой-то код ..}. Обычно мне нужно наполнить свой маленький мозг понятиями, которые мне знакомы - «хранение» - один из таких способов, по крайней мере, для меня ... :)

avatar
Steve Jessop
11 сентября 2009 в 14:03
23

Из стандарта C99, 6,7 (5):

Объявление определяет интерпретацию и атрибуты набора идентификаторов. определение идентификатора - это объявление для этого идентификатора, которое:

  • для объекта, вызывает резервирование памяти для этого объекта;
  • для функции, включает тело функции;
  • для константы перечисления или имени typedef - (единственное) объявление идентификатор.

Из стандарта C ++, 3.1 (2):

Объявление - это определение , если оно не объявляет функцию без указания тела функции, оно не содержит спецификатор extern или спецификацию связи, а не инициализатор или тело функции, оно объявляет статические данные член в объявлении класса, это объявление имени класса или объявление typedef, объявление-using или директива-using.

Тогда есть несколько примеров.

Интересно (или нет, но я немного удивлен этим), typedef int myint; - это определение в C99, но только объявление в C ++.

sbi
11 сентября 2009 в 14:20
0

@onebyone: Что касается typedef, не означает ли это, что его можно повторить в C ++, но не в C99?

Steve Jessop
11 сентября 2009 в 14:35
0

Это меня удивило, а что касается единой единицы перевода, да, разница есть. Но очевидно, что typedef может повторяться в C99 в разных единицах перевода. В C нет явного «правила одного определения», как в C ++, поэтому правила, которые в нем есть, просто разрешают это. C ++ решил изменить его на объявление, но также одно правило определения перечисляет, к каким вещам оно применяется, а typedefs не входит в их число. Таким образом, повторы будут разрешены в C ++ в соответствии с ODR в том виде, в котором он сформулирован, даже если typedef был определением. Кажется излишне разборчивым.

Steve Jessop
11 сентября 2009 в 14:35
0

... но я предполагаю, что этот список в ODR действительно перечисляет все, что можно дать определениям. Если это так, то список на самом деле избыточен и предназначен только для того, чтобы быть полезным.

sbi
11 сентября 2009 в 15:03
0

Что в стандартном ODR-определении говорится об определениях классов? Они должны повторять .

Johannes Schaub - litb
11 сентября 2009 в 20:52
0

Я подозреваю, что это связано с тем, что typedef объявляет только имя, не производя чего-либо помимо (например, типа, объекта или чего-то еще). Так что это просто объявление, очень похожее на объявление-использование.

Johannes Schaub - litb
11 сентября 2009 в 20:55
0

Однако обратите внимание, что вы можете иметь несколько определений одного и того же пространства имен, хотя это звучит странно: namespace A { } namespace A { }

Steve Jessop
12 сентября 2009 в 12:21
2

@sbi: ODR говорит: «(1) Ни одна единица перевода не должна содержать более одного определения любого ... типа класса» и «(5) Может быть более одного определения типа класса ... в программе при условии, что каждое определение появляется в разных единицах перевода ", а затем некоторые дополнительные требования, которые сводятся к" одинаковым определениям ".

Steve Jessop
12 сентября 2009 в 12:24
0

@litb: да, пространства имен не упоминаются в первом пункте ODR. На самом деле я часто использую это в файлах заголовков, где у меня есть группы функций, которые я закрываю и повторно открываю пространства имен между ними, так что каждый «раздел» файла заголовка стоит отдельно.

sbi
12 сентября 2009 в 13:35
0

@onebyone: Я всегда думал, что ODR имеет дело с определениями в единицах перевода. Я удивлен, что это ограничено ЕП.

Steve Jessop
12 сентября 2009 в 13:52
0

@sbi: это касается обоих. Пункт 1 касается того, что нельзя воспроизвести в одной единице перевода. В разделе 5 перечислены некоторые вещи, которые могут быть продублированы в программе при условии, что они представлены в разных единицах. Есть и другие чудесные статьи, слишком большие, чтобы поместиться на этом поле ^ Hcomment.

sbi
12 сентября 2009 в 21:58
0

@onebyone: Я несколько раз пытался покопаться в Holy Paper, но мне просто не хватает понимания английского языка, чтобы понять standardeze. <sigh> Но опять же, может быть, я просто не из тех, кто читает легалез, на каком бы языке он ни был ...

Steve Jessop
13 сентября 2009 в 10:32
0

@sbi: Я, конечно, никогда не читал стандартные от корки до корки, но индекс неплохой :-)

Destructor
3 февраля 2016 в 16:40
1

@SteveJessop: обновите свой ответ в соответствии со стандартом C11, потому что, как вы знаете, C11 также позволяет повторять typedef.

avatar
Michael Kristofik
11 сентября 2009 в 13:53
178

Из стандартного раздела 3.1 C ++:

Объявление вводит имена в единицу перевода или повторно объявляет имена, введенные предыдущими декларации. Объявление определяет интерпретацию и атрибуты этих имен.

В следующем абзаце говорится (выделено мной), что объявление является определением , если ...

... он объявляет функцию без указания тела функции:

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 ++:

Ни одна единица перевода не должна содержать более одного определения любой переменной, функции, типа класса, типа перечисления или шаблона.

RJFalconer
4 марта 2014 в 13:27
0

«объявляет статический член в определении класса» - это верно, даже если статический член инициализирован, верно? Можем ли мы сделать пример struct x {static int b = 3; };?

Kyle Strand
14 августа 2014 в 17:08
0

@RJFalconer Вы правы; инициализация не обязательно превращает объявление в определение (вопреки тому, что можно было ожидать; конечно, это меня удивило). Ваша модификация примера фактически незаконна, если b также не объявлен const. См. Coderhelper.com/a/3536513/1858225 и daniweb.com/software-development/cpp/threads/140739/….

Victor Zamanian
7 октября 2014 в 13:52
2

Мне это интересно. Согласно вашему ответу, похоже, что в C ++ объявление является также определением (с исключениями), тогда как в стандарте C оно сформулировано с другой точки зрения (C99, раздел 6.7, Объявления): " Определение идентификатора - это объявление для этого идентификатора, которое: [за которым следуют критерии для различных случаев] ". Полагаю, по-разному взглянуть на это. :)

Gab是好人
11 февраля 2016 в 14:45
0

Объявление предназначено для того, чтобы компилятор принял имя (чтобы сообщить компилятору, что имя допустимо, имя вводится намеренно, а не как опечатка). Определение - это то, где связано имя и его содержание. Определение используется компоновщиком, чтобы связать ссылку на имя с содержимым имени.

avatar
kv-prajapati
11 сентября 2009 в 12:35
35

Декларация

Объявления сообщают компилятору, что программный элемент или имя существует. А объявление вводит один или несколько имена в программу. Заявления могут встречаются в программе более одного раза. Следовательно, классы, структуры, перечислимые типы и другие определяемые пользователем типы могут быть объявлены для каждая единица компиляции.

Определение

Определения указывают, какой код или данные название описывает. Имя должно быть объявляется перед его использованием.

sbi
11 сентября 2009 в 13:03
0

Гм, разве вы не можете определять классы и перечисления в каждой единице компиляции? По крайней мере, я помещаю определения класса в свои заголовки и включаю их повсюду. Эээ, class foo {}; - это класс определение , не так ли?

David Thornley
11 сентября 2009 в 14:01
1

да. Однако "class foo;" это декларация. Он сообщает компилятору, что foo - это класс. "class foo {};" это определение. Он сообщает компилятору, что это за класс foo.

sbi
11 сентября 2009 в 14:17
0

@ Дэвид: Верно. И поскольку все мы помещаем определения класса и перечисления в наши файлы заголовков, это определения класса и перечисления , а не объявления, которые могут повторяться для каждой единицы компиляции. Поэтому ответ adatapost, вводит в заблуждение .

Johannes Schaub - litb
11 сентября 2009 в 16:56
1

Исключение составляют имена членов класса, которые можно использовать до объявления.

sbi
11 сентября 2009 в 17:41
0

@litb: Я не думаю, что это правда: class blah { foo bar(); typedef int foo; }; дает ошибку времени компиляции. Вы имеете в виду, что определения функций-членов , даже если они определены в определении класса, анализируются так, как если бы они были определены правильно позади класса. определение.

Johannes Schaub - litb
11 сентября 2009 в 18:00
1

Да, это то, что я имел в виду. Итак, вы можете сделать следующее: struct foo {void b () {f (); } void f (); }, f виден, хотя еще не объявлен. Следующее также работает: struct foo {void b (int = bar ()); typedef int bar; } ;. Он виден перед объявлением во «всех телах функций, аргументах по умолчанию, инициализаторах конструкторов». Не в возвращаемом типе :(

sbi
11 сентября 2009 в 19:27
1

@litb: это не видно до объявления, просто использование идентификатора перемещается за объявление. Да, я знаю, во многих случаях эффект одинаков. Но не для всех случаев, поэтому я думаю, что нам следует использовать точное объяснение. - Ой, подожди. Это видно в аргументах по умолчанию? Что ж, это наверняка нанесет ущерб моему пониманию. Черт возьми! <pouts>

avatar
Marcin Gil
11 сентября 2009 в 12:30
19

С сайта wiki.answers.com:

Термин «объявление» означает (в C), что вы сообщаете компилятору о типе, размере и, в случае объявления функции, типе и размере ее параметров любой переменной или определяемого пользователем типа или функции в вашей программе. Нет места в памяти зарезервировано для любой переменной в случае объявления. Однако компилятор знает, сколько места нужно зарезервировать на случай создания переменной этого типа.

, например, все объявления:

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; 

см. Ответы.

sbi
11 сентября 2009 в 12:37
3

Это тоже неверно (хотя гораздо ближе, чем другие): struct foo {}; - это определение , а не декларация. Объявление foo будет struct foo;. Из этого компилятор не знает, сколько места нужно зарезервировать для объектов foo.

San Jacinto
11 сентября 2009 в 12:43
0

SBI, мой ответ отражает то, что вы говорите. в вашем примере вы определяете foo как пустую структуру. Я не понимаю, чем технически отличаются наши примеры.

Steve Jessop
11 сентября 2009 в 13:01
1

@Marcin: sbi говорит, что «компилятор знает, сколько места нужно зарезервировать на случай создания переменной этого типа» не всегда верно. struct foo; - это объявление, но оно не сообщает компилятору размер foo. Я бы добавил, что struct _tagExample { int a; int b; }; - это определение. Так что в данном контексте называть это декларацией неверно. Конечно, это одно, поскольку все определения являются декларациями, но вы, кажется, предполагаете, что это не определение. Это определение _tagExample.

David Thornley
11 сентября 2009 в 14:07
1

@Marcin Gil: Это означает, что вики «Ответы» не всегда точна. Я должен проголосовать против за дезинформацию.

sbi
11 сентября 2009 в 14:14
0

Итак, у нас есть ответ, скопированный прямо из MSDN (adatapost's) и один из answers.com, и оба они вводят в заблуждение или даже совершенно неверны. Что мы узнаем из этого?

Steve Jessop
11 сентября 2009 в 14:18
1

Мы узнаем, что то, что цитируется в adatapost, является правдой, но (ИМО) на самом деле не отвечает на вопрос. То, что цитирует Марчин, неверно. Цитата из стандартов верна и отвечает на вопрос, но очень трудно разобраться в ней.

sbi
11 сентября 2009 в 14:23
0

@onebyone: очень действительно хорошее резюме! (Я, однако, закрепил то, что я узнал в студенческие годы, и позже попытался вбить в своих учеников: копирование, не задумываясь, может привести к катастрофе. :^>)

Marcin Gil
11 сентября 2009 в 18:15
1

@ Дэвид Торнли - не проблема :) Это то, о чем этот сайт. Мы выбираем и проверяем информацию.

Johannes Schaub - litb
11 сентября 2009 в 18:25
0

Следует отметить, что struct foo { int a; }; в C не является определением. C не знает определений структур. Напротив, typedef - это определение в C.