我没有 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) ;
您会看到每个函数都有前缀。我选择“结构”的名称以确保不会与其他代码发生冲突。
您也会看到,我使用“p_pThis”来保持类似 OO 的想法。
在源文件中,定义你的类型,定义函数:
/* 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. */
如果您想要“私有”函数(或私有全局变量),请在 C 源代码中将它们声明为静态。这样,它们就不会在外面可见:
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) ;
}
避免非常量全局变量
这包括“静态”变量(不是静态函数)。
全局非常量变量几乎总是一个坏主意(例如,请参阅 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
K&R 中描述的 C API 函数列表非常小。您将在 20 分钟内阅读整个列表。你必须知道这些函数。
想来点经验吗?
重写 C API。例如,尝试编写您自己版本的 string.h 函数,看看它是如何完成的。