Разница между объявлением и определением в C и C++

В C и C++ есть маленькое, но важное различие между значением слов “объявление” и “определение”. Если вы не понимаете разницы, иногда вы будете получать странные ошибки, такие как:

undefined symbol foo

или

undefined reference to foo

или даже

undefined reference to vtable for foo(в C++)

Что означает “объявление” в C и C++

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

Итак, как же выглядит объявление?

Например, если вы пишете:

int func();

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

 Что это значит, определить что-то в C и C++ ?

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

Например, для компилятора часто достаточно объявления. Вы можете написать такой код:

int func();

int main()
{
    int x = func();
}

int func()
{
    return 2;
}

Так как компилятор знает возвращаемое значение функции, и количество аргументов, которые она принимает, он может скомпилировать вызов функции, даже если она еще не определена. В самом деле, определение функции может быть в другом файле!

Вы также можете объявить класс не определяя его:

class MyClass;

Код, которому нужно знать детали того, что находится в MyClass не будет работать — вы не можете этого сделать:

class MyClass;

MyClass an_object;

class MyClass 
{
    int _a_field;
};

Потому что компилятор должен знать размер переменной an_object, и он не может узнать его из объявления MyClass; ему нужно определение, которое появляется ниже.

 Объявление и определение переменных с Extern

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

int x;
int main()
{
    x = 3;
}

Строка int х, как объявляет так и определяет переменную, она говорит: «создать переменную с именем х, типа int. Кроме того, место хранения переменной определяется тем, что это глобальная переменная, определенная в объектный файл, связанный с этим исходным файлом. Это немного странно, не правда ли? Что произойдет, если кто-то другой напишет второй исходный файл, который имеет такой код:

extern int x;
int func()
{
    x = 3;
}

Использование extern позволяет объявить переменную, не определяя ее, он сообщает, что переменная находится где-то в другом месте. Технически, можно даже написать такой код:

extern int x;
int func()
{
    x = 3;
}

int x;

И таким образом, объявление х находится в верхней части программы, а определение внизу. Но, как правило, extern используется, когда вы хотите получить доступ к глобальной переменной, объявленной в другом исходном файле, как я показал выше, а затем связать два получившихся объектных файла вместе после компиляции. Использование extern для объявления глобальной переменной в значительной степени то же самое, что и, используя объявление функции, объявить функцию в заголовке файла. (На самом деле, вы ставите extern в заголовке файла, а не в исходном файле.)

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

redefinition of ‘foo

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

Разница между объявлением и определением: Вывод

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

Общие случаи

Если вы хотите использовать функцию в нескольких исходных файлах, вы должны объявить функцию в одном заголовочном файле (.h), а затем определить функцию в одном исходном файле (.c или .cpp). Весь код, который использует функцию должен включать в себя только .h файл, и вам надо связать полученные объектные файлы с объектным файлом от компиляции исходного файла.

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

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

Автор: Marienko L.
Дата: 01.10.2012
Поделиться:

Комментарии

  1. lnikaFax

    Работа в интернете

Оставить комментарий

Вы должны войти, чтобы оставить комментарий.