我来自java背景(来自我的CS课程)和一个学期的C++.我刚刚为我的Co-Op完成了一个纯C的OpenCV项目,所以我提问这个问题有点晚了.
纯C的设计流程和编码标准是什么?
我熟悉面向对象的编程,设计和最佳实践.我只是在像C这样的非面向对象语言上有点不知所措.每个变量和函数似乎都是全局的.这让我感觉真的很乱.
您可能有兴趣查看我不久前问过的类似问题的答案.此外,如果您对C风格指南感兴趣,您可能需要查看此页面,因为它是C(和C++)样式指南的存储库.如果你想好笑,请看看NASA C风格指南.特别是,看看大量的评论......你会知道我在谈论哪一个.请不要写这样的评论.
我个人推荐使用Indian Hill C Style Guide进行一些修改.此外,如果您在使用C语言设计大型程序时遇到问题,则可能需要购买" C接口和实现 "一书.
老实说,我认为StackOverflow上的任何答案都不会教你如何设计和编写结构良好的C程序.你需要阅读一本好书,显而易见的是Kernighan和Ritchie 编写的The C Programming Language.
我没有C的专业经验(仅限C++),因此不要过于认真地对待我的建议,技巧和提示,因为它们是"面向对象的".
可以轻松地模拟基本类似对象的功能:
在标题中,forward声明你的类型,typedef它,并声明"方法".例如:
/* MyString.h */ #include/* 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"来保持类似OO的想法.
在源文件中,定义您的类型,并定义函数:
/* MyString.c */ #include "MyString.h" #include#include 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) ; }
这包括"静态"变量(不是静态函数).
全局非const变量几乎总是一个坏主意(例如,参见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 ! */
未初始化的变量是导致痛苦的错误的原因.
K&R中描述的C API函数列表非常小.你会在20分钟内阅读整个清单.你必须知道这些功能.
重写C API.例如,尝试编写自己的string.h函数版本,以了解它是如何完成的.
您可以通过将其声明为"静态"来使函数或对象的作用域位于其源文件的本地.这有点帮助.
否则,我看到的避免命名空间冲突的典型习惯是在名称上放置某种设施标识符.例如,foo.c中的所有内容都可能被命名为foo_*