Что такое typedef, и чем он отличается от #define? - Языки программирования - Shelek
Часть 1. Чем-то они так похожи...
Код:
#define UL unsigned long
typedef unsigned int UI;

UL var_ul=123456; //переменная типа unsigned long
UI var_ui=1234; //переменная типа unsigned int

Уверен, что почти каждый начинающий программист, изучающий С++ (или С), увидев подобные конструкции, задумывался над тем, а что же, собственно, такое #define и typedef? И чем еще отличаются они друг от друга, кроме перестановки идентификаторов местами (странно, но я сам до сих пор иногда путаю порядок подстановки)?

Ну-с... Для начала немного скучной и, наверняка, всем известной теории.

#define - это директива (команда) препроцессора, а typedef - спецификатор (специальное служебное слово) языка. Это означает, что и применяются они в разных местах и в разное время. #define - на этапе работы препроцессора, т.е. еще до начала компиляции программы, а typedef уже на этапе собственно компиляции. А так, и то и другое, по сути своей, - текстовая подстановка или замена. Но не совсем. Есть тонкости, и мы их увидим дальше.

Директива #define заменяет представленный идентификатор заранее подготовленной последовательностью символов. Эта директива может размещаться в любом месте текста и действует (в обычном случае) от места размещения и до конца текста (а в необычном - до директивы #undef).

Идентификатор, представленный в typedef, не вводит новый тип данных, это, скорее, новое имя (синоним) для существующего типа. typedef также может появляться в любом месте программы, где потребуется ввести имя типа.

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

Код:
typedef float* float_ptr;
#define FLOAT_PTR float*

float_ptr pf1, pf2; //тут все прекрасно
FLOAT_PTR PF1, PF2; //а вот тут будет не так все радужно, после препроцессора это
//будет эквивалентно вот чему: float* PF1, float PF2;
float *ff;
pf1 = ff;
pf2 = ff;
PF1 = ff; //до сих пор все хорошо
PF2 = ff; //а тут ошибка assignment to 'float' from 'float*' lacks a cast

А все потому, что препроцессор сделал простую текстовую замену.
А вот вам еще сюрприз с константами и указателями:
Код:
typedef string* p_string;
#define P_STRING string*

const p_string cstr;
const P_STRING CSTR;

Какой тип имеет переменная CSTR? Правильно - const string* (или эквивалентно string const *) - указатель на константную строку.
Так, а каков тип cstr? Напрашивается ответ - что он тоже является указателем на константную строку. Но это неправильно. Ошибка в размышлении о typedef, как о простом текстовом расширении. А это не так. Когда мы объявляем const p_string, то const изменяет тип p_string, который является указателем. Поэтому, это определение объявляет, что cstr будет константным указателем на строку. Определение эквивалентно string *const cstr.

Директива #define позволяет сделать такой "фокус":
Код:

#define INT_VAR unsigned int
INT_VAR i=123;
...
#define INT_VAR unsigned long
INT_VAR m=456;

И это будет компилироваться и работать. Правда, компилятор выдаст предупреждение о переопределении INT_VAR (что-то типа "INT_VAR redefined" или "Redefinition of INT_VAR is not identical", в зависимости от того, каким компилятором это делается).
А вот после применения директивы #undef компилятор и ругаться уже не будет.
Код:

#define INT_VAR unsigned int
INT_VAR i=123;
#undef INT_VAR
...
#define INT_VAR unsigned long
INT_VAR m=456;


С typedef подобный "фокус" и вовсе не пройдет. Компилятор однозначно выругается на ошибку "Error: conflicting types for 'typedef unsigned long INT_VAR' ". А спецификатора (или директивы) untypdef еще никто не придумал. То есть, как только некое имя использовалось как имя типа, оно уже не может быть переопределено:
Код:

typedef double Money;

class Account
{
public:
Money balance() { return bal; } //используем глобальное определение Money

private:
typedef long double Money; // error: нельзя изменять имя типа Money
Money bal;
// ...
};


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

Вот, наверное, и все о сходстве и различиях typedef и #define. Может, я что-то и упустил...
В следующей части речь пойдет уже только о typedef и его применении.


Если у вас возникает потребность подробного рассмотрения какого-либо вопроса по С++, пишите нам, мы попытаемся вам помочь.

Сергей Малышев (aka Михалыч).
Information
  • Posted on 31.01.2010 23:48
  • Просмотры: 2530