Значение обоих ускользает от меня.
Значение обоих ускользает от меня.
Ответы:
Декларация вводит идентификатор и описывает его тип, будь то тип, объект или функция. Объявление - это то, что нужно компилятору для принятия ссылок на этот идентификатор. Это декларации:
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 :: a и S :: b [...] структура S; // объявляет S - конец примера
Подводя итог: стандарт C ++ считает , struct x;
чтобы быть декларацией и определением . (Другими словами, «предварительное объявление» является неправильным , поскольку в C ++ нет других форм объявлений классов.)struct x {};
Спасибо Литбу (Йоханнесу Шаубу), который раскопал настоящую главу и стих в одном из своих ответов.
extern int i
это объявление, так как оно просто вводит / указывает i
. Вы можете иметь столько, extern int i
сколько хотите в каждом модуле компиляции. int i
Однако это определение. Он обозначает пространство для целого числа, которое должно быть в этом модуле перевода, и рекомендует компоновщику связать все ссылки i
с этим объектом. Если у вас есть более или менее одно из этих определений, компоновщик будет жаловаться.
int i;
в файловой / глобальной области видимости или в области функций - это определение как в C, так и в C ++. В C, потому что он выделяет память, и в C ++, потому что у него нет спецификатора extern или спецификации связи. Это равносильно тому, что говорит sbi: в обоих случаях это объявление указывает объект, с которым должны быть связаны все ссылки на «i» в этой области.
struct A { double f(int, double); double f(int, double); };
недействительный, конечно. Это разрешено в другом месте, хотя. Есть несколько мест , где вы можете объявить вещи, но не определяет, тоже: void f() { void g(); }
действительно, но не следующее: void f() { void g() { } };
. Что такое определение и какое объявление имеет тонкие правила, когда дело доходит до шаблонов - будьте осторожны! +1 за хороший ответ.
Из стандартного раздела 3.1 C ++:
А декларация вводит имена в ЕП или 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 {};
является класс определение , не так ли?
Из стандарта С99 6,7 (5):
Объявление определяет интерпретацию и атрибуты набора идентификаторов. Определение идентификатора является декларацией для этого идентификатора , что:
Из стандарта C ++, 3.1 (2):
Декларация является определением если только оно не объявляет функцию без указания тела функции, оно содержит спецификатор extern или спецификацию связи и не является ни инициализатором, ни телом функции, оно объявляет член статических данных в объявлении класса, это объявление имени класса, или это объявление typedef, объявление-использование или директива-использование.
Тогда есть несколько примеров.
Интересно (или нет, но я немного удивлен этим), typedef int myint;
это определение в C99, но только объявление в C ++.
typedef
, разве это не означает, что это можно повторить в C ++, но не в C99?
С wiki.answers.com:
Термин объявление означает (на языке C), что вы сообщаете компилятору о типе, размере и, в случае объявления функции, типе и размере ее параметров любой переменной, или определенного пользователем типа или функции в вашей программе. В случае объявления в памяти не зарезервировано ни одной переменной. Однако компилятор знает, сколько места нужно зарезервировать в случае создания переменной этого типа.
например, ниже приведены все объявления:
extern int a;
struct _tagExample { int a; int b; };
int myFunc (int a, int b);
Определение, с другой стороны, означает, что в дополнение ко всем вещам, которые делает объявление, пространство также резервируется в памяти. Вы можете сказать «ОПРЕДЕЛЕНИЕ = ДЕКЛАРАЦИЯ + ПРОБЛЕМА ПРОСТРАНСТВА». Ниже приведены примеры определения:
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, вот один.
Объявление является определением, если оно не объявляет / 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), это всегда определения.
определение означает фактическую написанную функцию, а объявление означает простую функцию объявления, например, для
void myfunction(); //this is simple declaration
а также
void myfunction()
{
some statement;
}
это определение функции myfunction
Практическое правило:
Декларация сообщает компилятор , как интерпретировать данные переменные в памяти. Это необходимо для каждого доступа.
Определение оставляет память , чтобы сделать переменную существующие. Это должно произойти ровно один раз перед первым доступом.
Чтобы понять существительные, давайте сначала сосредоточимся на глаголах.
объявить - объявить официально; Proclaim
определить - показать или описать (кого-то или что-то) ясно и полностью
Итак, когда вы заявляете что-то, вы просто говорите, что это такое .
// 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();
. Определения шаблонов отражают определения классов / функций таким же образом. (Обратите внимание, что имя шаблона не является именем типа или функции . Это можно увидеть в одном месте, когда вы не можете передать шаблон в качестве параметра типа другого шаблона. Если вы хотите передавать шаблоны вместо типов, вам нужны параметры шаблона шаблона. )
Найти похожие ответы здесь: Технические вопросы интервью в C .
Декларация предоставляет имя к программе; определение обеспечивает однозначное описание объекта (например , типа, экземпляр, и функцию) в рамках программы. Объявления могут повторяться в заданной области, это вводит имя в данной области.
Объявление является определением, если:
Определение является декларацией, если:
Это будет звучать по-настоящему глупо, но это лучший способ, которым я смог держать слова прямо в голове:
Декларация: фото Томаса Джефферсона, выступающего с речью ... "Я НАСТОЯЩИМ ОБЪЯВЛЯЮ, ЧТО ЭТО ФУО СУЩЕСТВУЕТ В ЭТОМ КОДЕКСЕ ИСТОЧНИКА !!!"
Определение: представьте себе словарь, вы ищете Foo и что это на самом деле означает.
Согласно руководству по библиотеке GNU C ( http://www.gnu.org/software/libc/manual/html_node/Header-Files.html )
В C объявление просто предоставляет информацию о том, что функция или переменная существует, и дает ее тип. Для объявления функции также может быть предоставлена информация о типах ее аргументов. Цель объявлений - позволить компилятору правильно обрабатывать ссылки на объявленные переменные и функции. С другой стороны, определение фактически выделяет память для переменной или говорит, что делает функция.
Концепция объявления и определения образует ловушку, когда вы используете класс внешнего хранилища, потому что ваше определение будет находиться в каком-то другом месте, и вы объявляете переменную в своем локальном кодовом файле (странице). Одно из различий между C и C ++ состоит в том, что в C вы объявления обычно делаются в начале функции или кодовой страницы. В С ++ это не так. Вы можете заявить в любом месте по вашему выбору.
Мой любимый пример - "int Num = 5", здесь ваша переменная 1. определена как int 2. объявлена как Num и 3. создана со значением пять. Мы
Класс или структура позволяет вам изменить способ определения объектов при их последующем использовании. Например
Когда мы учимся программировать, эти два термина часто путаются, потому что мы часто делаем оба одновременно.
Этапы исполняемого поколения:
(1) препроцессор -> (2) переводчик / компилятор -> (3) компоновщик
На этапе 2 (переводчик / компилятор) операторы объявления в нашем коде сообщают компилятору, что эти вещи мы будем использовать в будущем, и вы сможете найти определение позже, что означает:
переводчик удостоверится, что: что к чему? означает декларацию
и (3) стадия (линкер) нуждается в определении, чтобы связать вещи
Линкер убедился, что: где что? означает определение
Есть несколько очень четких определений, разбросанных по всему 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;