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

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

31 ответ

Решение

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

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; быть декларацией и struct x {}; определение. (Другими словами, "предварительное объявление" является неправильным, поскольку в C++ нет других форм объявлений классов.)

Спасибо Johannes Schaub - litb, который раскопал настоящую главу и стих в одном из своих ответов.

Из стандартного раздела 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++:

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

Декларация: "Где-то существует фу".

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

В 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.

декларация

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

Определение

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

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

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

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

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

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

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

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

С 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; 

см. ответы.

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

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

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

  • непрозрачный enum - enum X : int;
  • параметр шаблона - T в 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 - 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

Декларация:

int a; // this declares the variable 'a' which is of type 'int'

Таким образом, объявление связывает переменную с типом.

Ниже приведены некоторые примеры декларации.

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 

Иногда объявление и определение группируются в одно утверждение, подобное этому.

int a=10;

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

объявить - объявить официально; провозглашать

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

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

// declaration
int sum(int, int);

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

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

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

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

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

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

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

void  myfunction(); //this is simple declaration

а также

void myfunction()
{
 some statement;    
}

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

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

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)

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

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

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

Найти похожие ответы здесь: Технические вопросы интервью в C

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

Декларация является определением, если

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

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

  • Определение определяет член данных статического класса,
  • Определение определяет не встроенную функцию-член.

В декларации говорится, что "эта штука где-то существует"

      int sampleFunc(); // function
extern int car;  // variable

Определение гласит: «Эта вещь существует здесь; создайте для нее память».

      int sampleFunc() {} // function
int car; // variable

Инициализация необязательна в точке определения объектов и говорит: «Вот начальное значение для этой вещи»:

      int car = 0; // variable

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

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

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

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

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

Добавление примеров определений и объявлений из стандартного документа C++ (из раздела 3.1 Объявления и определения)

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

      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

Декларации:

      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 d

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

 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 и функцию max().

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

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

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

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

Декларация: фото Томаса Джефферсона, выступающего с речью... "Я НАСТОЯЩИМ ОБЪЯВЛЯЮ, ЧТО ЭТО ФУО СУЩЕСТВУЕТ В ЭТОМ КОДЕКСЕ ИСТОЧНИКА!!!"

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

Всякий раз, когда мы пишем функцию после основной функции, компилятор пропускает ошибку, так как не имеет никакого представления о функции во время вызова функции. Если мы предоставим объявление прототипа функции, то наш компилятор не будет искать определение.

int sum(int,int);

main()
{
    int res = sum(10,20);
}
int sum(int n1,int n2)
{
    return(n1+n2);
}

В приведенном выше примере первая строка называется объявлением функции.

int sum(int,int);

Объявление переменной

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

int ivar;
float fvar;

Объявление переменной и определение параметров дифференцирования

А. Бронирование места:

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

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

struct book  {
    int pages;
    float price;
    char *bname;
};

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

struct book b1;

Б. Что это делает?

  1. Декларация идентифицирует тип данных идентификатора.
  2. Определение переменной присвоит ей некоторое значение.

Объявление вводит имя в программу; определение обеспечивает уникальное описание объекта (например, тип, экземпляр и функция). Объявления могут повторяться в заданной области, это вводит имя в заданной области. Должно быть ровно одно определение каждого объекта, функции или класса, используемого в программе на C++. Объявление является определением, если:

* it declares a function without specifying its body,
* it contains an extern specifier and no initializer or function body,
* it is the declaration of a static class data member without a class definition,
* it is a class name definition,
* it is a typedef declaration.

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

* it defines a static class data member,
* it defines a non-inline member function.

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

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

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

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

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

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

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

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

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

Переменная объявляется, когда компилятору сообщают, что переменная существует (и это ее тип); в этой точке он не выделяет память для переменной.

Переменная определяется, когда компилятор выделяет хранилище для переменной.

Этапы исполняемого поколения:

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

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

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

и (3) этап (линкер) нуждается в определении, чтобы связать вещи

Линкер убедился, что: где что? означает определение

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

Ex.

Код:

//Declare
int foo(int);

//Define
int foo(int){
...
}

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

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

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

Он может быть определен и объявлен с помощью оператора объявления вне любой функции без спецификатора класса хранения. Такое объявление выделяет память для переменной. Оператор объявления может также использоваться для простого объявления имени переменной со спецификатором класса внешнего хранилища в начале объявления. Такое объявление указывает, что переменная определена в другом месте, то есть память для этой переменной выделена в другом файле. Таким образом, доступ к внешней переменной в файле, отличном от того, в котором она определена, возможен, если она объявлена ​​с ключевым словом extern; новая память не выделяется. Такое объявление сообщает компилятору, что переменная определена в другом месте, а код скомпилирован с внешней переменной, оставшейся неразрешенной. Ссылка на внешнюю переменную разрешается в процессе связывания.

Ex.

Код

//file1.c
extern char stack[10];
extern int stkptr;
....

//file2.c
char stack[10];
int stkptr;
....

Эти объявления сообщают компилятору, что переменные stack[] и stkptr определены в другом месте, обычно в каком-то другом файле. Если бы ключевое слово extern было опущено, переменные считались бы новыми, и для них выделялась бы память. Помните, что доступ к той же внешней переменной, определенной в другом файле, возможен, только если в объявлении используется ключевое слово extern.

Другие вопросы по тегам