Стандарты кодирования для чистого C (не C ++) - PullRequest
27 голосов
/ 11 августа 2009

Я пришел из Java-фона (из моих классов CS) и семестра C ++. Я только заканчиваю проект OpenCV для моего Co-Op, который находится в чистом C, поэтому я немного опаздываю, задавая этот вопрос.

Каковы процессы проектирования и стандарты кодирования для чистого C?

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

Ответы [ 9 ]

26 голосов
/ 12 августа 2009

Возможно, вам будет интересно узнать ответы на подобный вопрос Я задавал не так давно. Более того, если вам интересны руководства по стилю C, вы можете взглянуть на эту страницу , так как это хранилище руководств по стилю C (и C ++). Если вы в настроении для хорошего смеха , пожалуйста, ознакомьтесь с NASA C Style Guide . В частности, взгляните на массивный комментарий ... вы поймете, о каком я говорю. Не пишите такие комментарии, пожалуйста.

Я лично рекомендую Indian Hill C Style Guide с некоторыми изменениями. Кроме того, вы можете приобрести книгу Интерфейсы и реализации C , если у вас возникли проблемы при разработке крупномасштабных программ на языке C.

15 голосов
/ 11 августа 2009

Честно говоря, я не думаю, что любое количество ответов на StackOverflow собираемся научить вас, как разрабатывать и писать хорошо структурированные программы на Си. Вам нужно прочитать хорошую книгу, и очевидная для чтения это Язык программирования C * Кернигана и Ричи.

10 голосов
/ 12 августа 2009

У меня нет профессионального опыта в C (только в C ++), поэтому не принимайте мои советы, рекомендации и подсказки слишком серьезно, поскольку они «объектно-ориентированы».

Почти объект C?

Имитация базовых объектоподобных функций может быть легко выполнена:

В заголовке, вперед объявите ваш тип, напечатайте его и объявите «методы». Например:

/* MyString.h */

#include <string.h>

/* Forward declaration */
struct StructMyString ;

/* Typedef of forward-declaration (note: Not possible in C++) */
typedef struct StructMyString MyString ;

MyString *       MyString_new() ;
MyString *       MyString_create(const char * p_pString) ;
void             MyString_delete(MyString * p_pThis) ;
size_t           MyString_length(const MyString * p_pThis) ;

MyString *       MyString_copy(MyString * p_pThis, const MyString * p_pSource) ;
MyString *       MyString_concat(MyString * p_pThis, const MyString * p_pSource) ;

const char *     MyString_get_c_string(const MyString * p_pThis) ;
MyString *       MyString_copy_c_string(MyString * p_pThis, const char * p_pSource) ;
MyString *       MyString_concat_c_string(MyString * p_pThis, const char * p_pSource) ;

Вы увидите, что каждая функция имеет префикс. Я выбираю имя "struct", чтобы избежать столкновения с другим кодом.

Вы также увидите, что я использовал "p_pThis", чтобы не отставать от ОО-подобной идеи.

В исходном файле укажите свой тип и определите функции:

/* MyString.c */

#include "MyString.h"

#include <string.h>
#include <stdlib.h>

struct StructMyString
{
   char *      m_pString ;
   size_t      m_iSize ;
} ;

MyString * MyString_new()
{
   MyString * pMyString = malloc(sizeof(MyString)) ;

   pMyString->m_iSize = 0 ;
   pMyString->m_pString = malloc((pMyString->m_iSize + 1) * sizeof(char)) ;
   pMyString->m_pString[0] = 0 ;

   return pMyString ;
}

/* etc. */

Если вам нужны «частные» функции (или частные глобальные переменные), объявите их как статические в источнике Си. Таким образом, они не будут видны снаружи:

static void doSomethingPrivate()
{
   /* etc. */
}

static int g_iMyPrivateCounter = 0 ;

Если вы хотите наследования, то вы почти облажались. Если вы полагали, что все в C было глобальным, включая переменные, то вам следует получить больше опыта в C, прежде чем даже пытаться подумать, как наследовать наследование.

Разное. Советы

Избегайте нескольких кодовых путей.

Например, многократный возврат рискован. Например:

void doSomething(int i)
{
   void * p = malloc(25) ;

   if(i > 0)
   {
      /* this will leak memory ! */
      return ;
   }

   free(p) ;
}

Избегайте неконстантных глобалов

Сюда входят «статические» переменные (которые не являются статическими функциями).

Глобальные неконстантные переменные почти всегда являются плохой идеей (т. Е. См. Пример API-интерфейса C API strtok), и если вы создаете многопоточный безопасный код, то с ними трудно справиться.

Избегать столкновения имен

Выберите «пространство имен» для своих функций и для ваших определений. Это может быть:

#define GROOVY_LIB_x_MY_CONST_INT 42

void GroovyLib_dosomething() ;

Осторожно, определяет

Определений нельзя избежать в C, но они могут иметь побочные эффекты!

#define MAX(a, b) (a > b) ? (a) : (b)

void doSomething()
{
   int i = 0, j = 1, k ;
   k = MAX(i, j) ;   /* now, k == 1, i == 0 and j == 1 */
   k = MAX(i, j++) ; /* now, k == 2, i == 0 and j == 3, NOT 2, and NOT 1 !!! */
}

Инициализируйте ваши переменные

Избегайте объявления переменных без их инициализации:

int i = 42 ; /* now i = 42 */
int j ;      /* now j can have any value */
double k ;   /* now f can have any value, including invalid ones ! */

Неинициализированные переменные являются причинами болезненных ошибок.

Знать все C API

Список функций C API, как описано в K & R, довольно мал. Вы прочитаете весь список за 20 минут. Вы должны знать эти функции.

Хотите немного опыта?

Переписать C API. Например, попробуйте написать свою собственную версию функции string.h, чтобы увидеть, как это делается.

8 голосов
/ 11 августа 2009

Вы можете сделать область действия функции или объекта локальной для ее исходного файла, объявив ее «статической». Это немного помогает.

В противном случае типичная идиома, которую я вижу для избежания столкновений в пространстве имен, заключается в добавлении какого-либо идентификатора объекта в имя. Например, все в foo.c может быть названо foo _ *

3 голосов
/ 12 августа 2009

Хорошая новость заключается в том, что вы можете программировать полуобъектно-ориентированным способом на C. Вы можете защищать данные, предоставлять функции доступа и т. Д. Он может не обладать всей прелестью C ++, но, как я видел в других народный код C ++, многие люди не используют причуды в любом случае. Другими словами, люди пишут код C внутри класса, где в C вы бы написали тот же код без контейнера класса.

Во-первых, прочитайте книгу о программировании и стиле C, K & R в порядке. Во-вторых, я бы порекомендовал проверить CERT Programming Standard . Несмотря на то, что этот сайт в основном ориентирован на «безопасные» стандарты кодирования, большая часть контента здесь - это общие стандарты качества кода, которым должен следовать каждый. Выполнение упомянутых здесь действий улучшит ваше качество, устранит неприятные ошибки и, как побочный эффект, сделает ваш код более безопасным.

3 голосов
/ 12 августа 2009

Работайте с другими хорошими программистами на Си. Имейте обзор кода с ними. Не только пусть они смотрят на ваш код, но вы смотрите на их код.

2 голосов
/ 13 августа 2009

Возможно, вам захочется хорошенько взглянуть на исходный код ядра Linux ..... Кстати, это не самый простой кусок кода, с которого можно начать, но, поскольку вы являетесь программистом, это может помочь ... Как объектно-ориентированный программист, вы, в частности, можете найти обработку ошибок в C трудной задачей. Может быть, это поможет ---> http://www.freetype.org/david/reliable-c.html

1 голос
/ 12 августа 2009

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

Например:

/** foo.c */
static void foo_helper() {...} /* foo_helper cannot be called by name 
                                  outside of foo.c */
static int local_state;        /* local state is visible at file scope,
                                  but is not exported to the linker */
1 голос
/ 12 августа 2009

Вы можете сделать объектно-ориентированное проектирование на чистом языке C. Простой подход - представить модуль как class с открытыми методами как обычные функции, для которых параметр this является явным первым аргументом.

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

Конструктор для объекта с полностью закрытыми членами-данными может предоставлять непрозрачный указатель (даже напечатанный void * или как указатель на неполный тип, если требуется безопасность типов). Если вы хотите иметь только открытые элементы данных, тогда указатель на публично определенный struct работает хорошо.

За этим шаблоном следует ряд библиотек. Функция инициализации возвращает cookie, который должен быть передан обратно всем библиотечным методам, а один метод служит деструктором.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...