использовать указатель на структуру или нет при использовании typedef в C

Чтобы определить новый тип данных в C, например, тип для связанного списка
, можно использовать одно из следующих определений

struct list_node {
    int x;
    struct list_node * next;
};

//1
typedef struct list_node list1;
//2
typedef struct list_node *list2;

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

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


person kon psych    schedule 23.02.2011    source источник
comment
Аналогичный вопрос: stackoverflow .com / questions / 3781932 /   -  person Blagovest Buyukliev    schedule 24.02.2011


Ответы (5)


Я бы потребовал, чтобы пользователи API вводили «*», то есть typedef для структуры, а не указателя на структуру. Это широко используемый стиль в GLib, который является одним из самых популярных стеков C.

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

Еще одно важное преимущество - это возможность поместить фактическое определение структуры где-нибудь в частном порядке, то есть в файле .c, а не в заголовке. Вы можете поместить только typedef в заголовок, сохранив при этом саму структуру непрозрачной для пользователей API. Это требует, чтобы люди использовали только те экспортированные методы, которые вы предоставляете для управления структурой. Это также означает, что вам не нужно перестраивать мир, если вы измените поля структуры.

Еще одна причина выбрать этот путь заключается в том, что иногда вам действительно нужна структура, которую можно скопировать, и вы все равно хотите ее определить по типу. Возьмем такую ​​вещь, как GdkPoint:

  typedef struct {
     int x, y;
  } GdkPoint;

Здесь полезно разрешить прямой доступ к структуре, например:

 GdkPoint point = { 10, 10 };
 GdkPoint copy = point;
 do_stuff_with_point(&copy);

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

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

Мой шаблон по умолчанию для типа данных C в заголовке выглядит примерно так:

typedef struct MyType MyType;
 MyType* my_type_new(void);
 void    my_type_unref(MyType *t);
 void    my_type_ref(MyType *t);

Затем фактический "struct MyType" в файле .c вместе с new, unref и любыми операциями с типом.

Исключением будут такие типы, как Point, Rectangle, Color, где требуются «простые старые данные» и прямой доступ к полям; другой вариант - использовать my_type_free () вместо _unref (), если вам не нужен счетчик ссылок.

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

person Havoc P    schedule 24.02.2011
comment
Я полностью согласен. Обычно не рекомендуется использовать typedef, чтобы скрыть, является ли тип указателем или структурой (если вы не используете непрозрачные указатели для интерфейса библиотеки). Честно говоря, я обычно предпочитаю называть struct a struct и вообще избегать typedef, но если вы собираетесь что-то typedef, сделайте это структурой, а не указателем. - person bta; 24.02.2011
comment
Стиль glib будет использовать CamelCase для struct typedef и строчные буквы для примитивного typedef, так что это устраняет необходимость вводить struct, сохраняя при этом ясность. также сохраняет возможность иметь непрозрачные структуры не в заголовке - person Havoc P; 24.02.2011

Я предпочитаю первый стиль, чтобы было понятно, какие типы являются объектами, а какие - указателями. Но я считаю, что указатели C легко понять / интерпретировать, чего (по-видимому) многие люди не делают. Поэтому, если вы думаете, что большинство людей, читающих ваш код, не понимают указатели, используйте typedef STRUCT_FOO *PSTRUCT_FOO везде, как это делает Win32 API.

person Conrad Meyer    schedule 24.02.2011

Рассмотрите возможность использования обоих из них:

typedef struct list_node Node;
typedef Node* ListHead;
person Jim Balter    schedule 24.02.2011

Пока идентификатор дает понять, что это указатель, вреда нет (я лично предпочитаю суффикс ala _Pointer или _Ptr, но все, что подходит для проекта). Тем не менее, для кода, предназначенного только для C, нет реальной выгоды в том, чтобы избежать явного *. Для смешанных сред C / C ++ или чистого C ++ я часто видел использование typedefs для рационализированных / обоснованных указателей, облегчающих переход, использование и переключение между типами интеллектуальных указателей. Вероятно, в этом есть доля правды, хотя я никогда не обнаруживал, что хочу перенять эту практику.

person Tony Delroy    schedule 24.02.2011

typedef int * ptr_int; ptr_int x, y, z;

Программиста может сбить с толку тот факт, что все три x, y, z имеют тип int *, то есть указатели на int. Но в реальности только x имеет тип int * y, а z - простое int.

потому что ptr_int x, y, z; равно int * x, y, z; не int * x, * y, * z;

Таким образом, typedef также может запутать программистов и привести к ошибкам и ошибкам.

person Guest Developer    schedule 27.06.2013