C头文件 - 良好实践

时间:2011-04-05 10:56:59

标签: objective-c c

我习惯于Objective C头文件,并且不确定如何在良好实践中使用C头文件。

头文件或.c文件中的#include个其他源文件在哪里?

同样的想法适用于C,其中.c文件包含自己的头文件。和其他文件包括他们想要包含的源的.h文件?

是否有与Objective-C中@class用法等效的内容?

在.h文件中声明指针并初始化它们/在.c文件中分配它们是不错的做法?

3 个答案:

答案 0 :(得分:10)

您通常以与Objective-C区分实现(.m)和接口(.h)文件相同的方式区分源文件和头文件。源文件包含可能执行的所有内容,头文件包含有关其他源文件知道如何与该源文件通信的符号的足够信息。

头文件通常包含其他头文件,因此您将在源文件和实现文件中看到#include#include的操作与#import的操作完全相同,只是它不会自动检查您是否#include同一个文件两次。所以C头文件通常看起来像:

#ifndef __SOME_SYMBOL
#define __SOME_SYMBOL

    ... rest of header file here ...

#endif

与确保头文件主体的效果相同只包含一次。

编辑:根据要求,更多关于此事。显然你永远不会做类似的事情:

#include "File.h"
#include "File.h"

但你可以很容易地得到类似的东西:

#include "FirstComplexThing.h"
#include "SecondComplexThing.h"

FirstComplexThing.h和SecondComplexThing.h都依赖于内部的东西,因此#include SimpleThing.h。所以你最终使用SimpleThing.h #included两次,没有出现任何错误或者出现任何糟糕的设计模式。

C编译器就像Objective-C编译器一样工作 - 每个源文件都是独立编译的,在链接器出现之前没有概述。 #include是一个预处理程序指令,它具有与复制指定文件的内容并将它们粘贴到该位置的源文件相同的逻辑效果,因此,如果您最终将同一文件#included结束两次,您可能会得到一些东西像:

char *somePointer; // I'm from SimpleThing.h

... lots of other things ...

char *somePointer; // I'm from SimpleThing.h

编译器将停止并返回同一事件被声明两次的错误。 Objective-C中的#import通过简写'#include来避免这种情况,但前提是你还没有#included那个文件'。 C#ifndef / #define / #endif约定实现与#import相同的功能,因为#ifndef / #endif对表示如果指定的预处理器符号(在我的示例中为__SOME_SYMBOL,则应将其中的内容传递给编译器)。它往往是从该头文件的名称派生的名称,但确切的约定不同)尚未定义。它不会是第一次遇到构造。因为它是在构造中定义的,所以第二次遇到相同的#ifndef时,所以匹配#endif的东西不会被传递。

虽然这是一个样式问题,但每个C文件都有一个直接连接到它的H文件。

很明显,C语言中没有类,但如果你的意思是一个类似的结构:

@class SomeClass;

@interface SomeOtherClass: NSObject
{
      SomeClass *otherClass; // I can reference SomeClass without importing
                             // the interface file because I've declared the
                             // type above
}

- (void)whatever;
@end

这实际上是声明和定义之间的正常C区别。如果您执行以下操作,则会遇到问题:

struct SomeStruct;

struct SomeOtherStruct
{
    struct SomeStruct otherStruct;
};

因为编译器没有足够的信息。它不知道SomeStruct应该有多大,所以它无法解决SomeOtherStruct应该如何布局。但是,这完全有效:

struct SomeStruct;

struct SomeOtherStruct
{
    struct SomeStruct *otherStruct;
};

因为指针的大小始终是已知的,无论它指向什么。您经常会看到具有不透明类型的C库仅通过指针描述这些类型(有时是void *,但并非总是如此 - 例如stdio.h使用FILE *)或只是给你一个整数(包括OpenGL) ,特别是)。所以他们确保你有一些编译器会知道大小的东西,而不必告诉你他们与之关联的数据,或者给你任何方法来尝试自己操作它。

将指针放在头文件中是非常好的做法(假设显然是全局公开事物的好方法)。同样的事情通常在Objective-C中完成,尽管原因略有不同,例如

// interface/header file

extern NSString *someGlobalIdentifier;

// implementation/source file

NSString *someGlobalIdentifier = @"somethingOrOther";

在Objective-C中,因为您可以测试身份而不是总是必须测试相等性,但基本上相同的规则适用于C,因为正常放置引用(无论是指针或其他什么)一个东西进入标题并在源文件中创建或声明该东西。实际上,如果你开始在头文件中放置声明,那么当程序链接时你最终会出错,因为多个源文件会认为它们声明了这一点。

答案 1 :(得分:1)

- > #include正在c和目标c中工作。 - >但通常在目标c中,始终使用#import。 - > #include和#import是不同的,当你使用#include编译器生成一个单独的.h文件副本时,如果你使用了#import,那么编译器一次只生成一个副本

是否有与Objective-C中@class用法相同的内容? - >没有任何其他等价物 在.h文件中声明指针并初始化它们/在.c文件中分配它们是一种好习惯吗? - >是的,如果您的对象是公共的,那么您必须在.h文件中声明,但始终是在构造函数中初始化它的好习惯。

答案 2 :(得分:0)

这就是我最终弄清楚如何正确地做到这一点的方法。经过长时间的尝试和失败之后,过去很简单。

    //this is the mechanics.h file

    #ifndef ProjectA_mechanics_h
    #define ProjectA_mechanics_h

    #ifdef __cplusplus
    extern "C" {
    #endif

    int funcAdd (int A, int B);


    #ifdef __cplusplus
    }
    #endif

    #endif

    // this is the mechanics.c file

    #include "mechanics.h"
    #include <math.h>

    int funcAdd (int A, int B)
    {
        return A + B;
    }

math.h有“只是因为”

玩得很开心,但是这个很糟糕