Почему операнды #define и typedef инвертированы?

Следующее определяет A для замены на B:

#define A B

Принимая во внимание, что это определяет A как псевдоним для типа B:

typedef B A;

Почему ? Разве это не бессвязно?


person fouronnes    schedule 27.02.2011    source источник
comment
PS: Я знаю, что они совершенно разные и используются для совершенно разных целей. Но лично я думаю, что typedef должен был быть наоборот.   -  person fouronnes    schedule 27.02.2011
comment
если тебя это утешит, Кен Томпсон думает то же самое. В Go, хотя нет прямого эквивалента typedef, который создает просто псевдоним типа, способ создания нового типа из старого — type NewType OldType. Интересно, будут ли он и Деннис Ритчи спорить лицом к лицу о синтаксических решениях, которые Go явно делает похожими на C, но отличающимися от C. Порядок объявления в Go в основном противоположный :-)   -  person Steve Jessop    schedule 28.02.2011


Ответы (7)


Проще говоря: рассмотрите следующие объявления переменных:

// declare a variable called "myInt" with type int
int myInt;
// declare a variable called "myDouble" with type double
double myDouble;
// declare a variable called "myLong" with type long
long myLong;
// declare a variable called "myFunc" with type pointer to function
void (*myFunc)(char*);

Тогда typedef имеют смысл:

// declare a type alias called "myInt" with type int
typedef int myInt;
// declare a type alias called "myDouble" with type double
typedef double myDouble;
// declare a type alias called "myLong" with type long
typedef long myLong;
// declare a type alias called "myFunc" with type pointer to function
typedef void (*myFunc)(char*);

Макросы, с другой стороны, могут иметь синтаксис функционального стиля:

#define A(B, C) B, C

A(1, 2) // expands out to 1, 2

Таким образом, для макросов «определение», следующее за «именем», имеет больше смысла.

(Кстати, это относится и к C++.)

person In silico    schedule 27.02.2011

Да, с макросами вообще беда.

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

static int x;
extern int y;
typedef int z;

Они определяют x, y и z как int -- разница в том, что x и y являются объектами типа int, а z в основном является псевдонимом самого int.

Таким образом, typedef достаточно хорошо вписывается в язык, и это (как обычно) препроцессор, который действительно является «лишним». В то же время вы можете утверждать, что остальная часть языка также должна измениться. Просто для наглядного примера Паскаль грубо изменил порядок вещей:

type
    z = integer;

var
    x : integer;

Хотя это не имеет большого значения для тривиальных примеров, я думаю, что это гораздо проще читать, особенно когда вы имеете дело с более сложными объявлениями. Однако, к лучшему или к худшему, Паскаль (в основном) потерял популярность, а более новые языки, такие как Java, сохранили эту конкретную часть синтаксиса C (т. Е. Та часть C, которую они сохранили, больше всего нуждалась в изменении) .

person Jerry Coffin    schedule 27.02.2011
comment
+1: полезно запомнить понятие ключевого слова для объявления объявлений. Всегда, всегда подумайте дважды, прежде чем вводить языковую конструкцию, вызываемую только синтаксическим порядком, без ключевого слова/оператора. - person Potatoswatter; 27.02.2011
comment
Почему вы говорите, что макросы (подразумевается препроцессор C) — это беспорядок? Он полностью отличается в некоторых аспектах от языка C? Абсолютно (например, позднее связывание по сравнению с ранним связыванием). Может быть трудно выучить полностью? Да, оно может. Но это беспорядок? Не то, чтобы я мог видеть. Так не могли бы вы уточнить, что вы подразумеваете под беспорядком? - person hlovdal; 27.02.2011
comment
@hlovdal: Вот несколько наиболее очевидных проблем: 1) отсутствие области видимости, 2) информация, недоступная для отладчиков, 3) чрезвычайно подверженная ошибкам (например, многократно оцениваемые аргументы). Они редко являются лучшим доступным решением. - person Jerry Coffin; 28.02.2011
comment
1) Отсутствие области видимости относится к макроязыкам в целом, вы тогда считаете все макроязыки беспорядочными? 2) Правда. Но тогда это только усложняет отладку кода, а не делает его беспорядочным. 3) Это также во что бы то ни стало можно сказать и о самом языке C (т.е. if (i = 0)..., по умолчанию нет разрыва в операторах switch и т.д.). - person hlovdal; 28.02.2011
comment
Может быть, мы вкладываем разные значения в слово «беспорядок», но я считаю что-то беспорядочным, если в нем отсутствует порядок и последовательность, и что трудно получить обзор и изменить его. Макросы вполне могут способствовать этому, но это не значит, что они будут делать это автоматически (о чем я и слышу ваши слова). Использование макросов вводит дополнительный уровень косвенности, что означает повышенную сложность, но предположительно сам макрос также принесет некоторые дополнительные преимущества, которые перевешивают добавленную сложность. Итак: Макросы нейтральны, не плохи. - person hlovdal; 28.02.2011

Поскольку A в typedef может быть несколькими символами, например. typedef int Integer, *PInteger;. Это согласуется с тем, как определяются переменные (int var, *pvar;).

person Cat Plus Plus    schedule 27.02.2011

Typedef с точки зрения синтаксиса языка находится в группе storage class specifier вместе с extern и static(*), и, таким образом, typedef занимает то же место, что и они. Он явно не принадлежит к этой группе, но я предполагаю, что он, вероятно, был там, где он был наименее неуместным.

(*) Класс хранения также включает auto и register, но их больше никто не использует.

person hlovdal    schedule 27.02.2011

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

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

struct arr { int len; char *chars; } name;
struct arr another_name;

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

typedef struct { int len; char *chars; } arr;
arr name;
arr another_name;

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

person Jeff Mercado    schedule 27.02.2011

Потому что препроцессор и компилятор — это фактически две разные программы, каждая со своим синтаксисом. Можно было совместить препроцессор с другими языками без особого труда (в прежние времена я делал это, используя cpp в программах dBase III и AutoLISP, потому что в этих языках не было хорошего механизма включения констант). Как уже указывали другие, typedef следует синтаксису системы объявлений C, а #define представляет собой простое прямое объявление подстановки.

person Patrick Schlüter    schedule 27.02.2011

Да, синтаксис typedef меня тоже немного сбивает с толку. Я предполагаю, что ваш вопрос больше похож на жалобу - C почти 40 лет, вы не ожидаете, что синтаксис typedef изменится, не так ли?

person Radim Cernej    schedule 27.02.2011