У меня нет профессионального опыта в 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, чтобы увидеть, как это делается.