Intereting Posts
Предотrotation накладных расходов при создании streamов openMP Почему компилятор не сообщает о недостающей точке с запятой? arm-linux-gnu-gcc фатальная ошибка: stdio.h: Нет такого файла или каталога В чем смысл стандарта C99? Ошибка: неизвестное имя типа Список при попытке создать связанный список Условное расширение макросов Как правильно использовать va_list в последовательности вызовов функций оболочки? Есть ли инструмент анализатора заголовка C для генерации обертки, такой как gccxml? Реализация __builtin_clz Почему перехват системных вызовов дает разные результаты каждый раз в Linux / Android 2.6.29? безопасно ли передавать указатели функций и сравнивать их так же, как указатели обычных объектов? Запись в несколько дескрипторов файлов с помощью одного вызова функции Программирование последовательного порта в Linux Как узнать количество символов в строке utf8 Как написать статическую библиотеку ansi-c с помощью visual studio 2010

В C, хорошо ли использовать typedef для указателя?

Рассмотрим следующий код:

typedef char * MYCHAR; MYCHAR x; 

Я понимаю, что результатом будет то, что x является указателем типа «char». Однако, если объявление x должно происходить далеко от команды typedef, человеческий читатель кода не сразу узнает, что x является указателем. В качестве альтернативы можно было бы использовать

 typedef char MYCHAR; MYCHAR *x; 

Что считается лучшей формой? Это больше, чем вопрос стиля?

Я использовал бы typedefs указателя только в ситуациях, когда природа указателя результирующего типа не имеет значения. Например, typedef указателя оправдан, когда требуется объявить непрозрачный тип «дескриптор», который просто реализуется как указатель, но не должен использоваться пользователем как указатель.

 typedef struct HashTableImpl *HashTable; /* 'struct HashTableImpl' is (or is supposed to be) an opaque type */ 

В приведенном выше примере HashTable является «дескриптором» для хеш-таблицы. Пользователь будет получать этот дескриптор изначально, например, CreateHashTable функции CreateHashTable и передавать его, скажем, в функцию HashInsert и т. Д. Пользователь не должен заботиться (или даже знать), что HashTable является указателем.

Но в тех случаях, когда пользователь должен понимать, что тип на самом деле является указателем и может использоваться в качестве указателя, typedefs указателя значительно запутывают код. Я бы избегал их. Объявление указателей явно делает код более читаемым.

Интересно отметить, что стандартная библиотека C избегает таких указателей typedef. Например, FILE , очевидно, предназначен для использования в качестве непрозрачного типа, что означает, что библиотека могла бы определить его как typedef FILE вместо того, чтобы постоянно использовать FILE * . Но почему-то они решили не делать этого.

Если указатель никогда не должен быть разыменован или иным образом управляться напрямую – IOW, вы передаете его только как аргумент API – тогда можно скрыть указатель за typedef.

В противном случае лучше сделать «pointerness» типа явным.

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

 typedef char *PSTR; ... PSTR str1, str2, str3; 

возможно яснее, чем:

 char *str1, str2, str3; // oops 

Я предпочитаю оставить * , он показывает, что есть указатель. И ваш второй пример должен быть сокращен как char* x; , это не имеет никакого смысла.

Я также думаю, что это вопрос стиля / конвенции. В основной графической библиотеке Apple они часто «скрывают» указатель и используют соглашение о добавлении «Ref» в конец типа. Так, например, CGImage * соответствует CGImageRef . Таким образом, вы все еще знаете, что это ссылка на указатель.

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

Другие случаи, когда полезно объявить тип, являющийся указателем, – это когда характер параметра непрозрачен для пользователя (программиста). Могут быть API, которые хотят вернуть указатель пользователю, и ожидают, что пользователь передаст его обратно в API в какой-то другой точке. Как непрозрачный дескриптор или cookie, который будет использоваться API только внутри страны. Пользователь не заботится о характере параметра. Было бы разумно не загрязнять воды и не раскрывать ее точный характер, раскрывая * в API.

Если вы посмотрите на несколько существующих API-интерфейсов, похоже, что не помещать pointerness в тип кажется более стильным:

  • уже упоминавшийся FILE *
  • MYSQL * возвращаемый mysql_real_connect() MySQL mysql_real_connect()
  • MYSQL * возвращаемый mysql_store_result() MySQL и mysql_use_result()

и, возможно, многие другие.

Для API нет необходимости скрывать определения структуры и указатели за «абстрактными» typedefs.

  /* This is part of the (hypothetical) WDBC- API ** It could be found in wdbc_api.h ** The struct connection and struct statement ar both incomplete types, ** but we are allowed to use pointers to incomplete types, as long as we don't ** dereference them. */ struct connection *wdbc_connect (char *connection_string); int wdbc_disconnect (struct connection *con); int wdbc_prepare (struct connection * con, char *statement); int main(void) { struct connection *conn; struct statement *stmt; int rc; conn = wdbc_connect( "host='localhost' database='pisbak' username='wild' password='plasser'" ); stmt = wdbc_prepare (conn, "Select id FROM users where name='wild'" ); rc = wdbc_disconnect (conn); return 0; } 

Вышеприведенный fragment отлично компилируется. (но он, очевидно, не связывает)