2011-04-05 39 views
2

我习惯于Objective C头文件,并不确定C头文件在良好实践方面的用法。C头文件 - 良好实践

哪里会有一个#include其他源文件,在头文件或.c文件中?

这个想法是否适用于C语言,其中.c文件包含它们自己的头文件。其他文件包括他们想要包含的源文件的.h文件?

Objective-C中是否有任何与@class用法等价的东西?

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

回答

10

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

头文件通常包含其他头文件,因此您会在源文件和实现文件中看到#include#include的操作与#import完全一样,不同之处在于它不会自动检查您是否包含同一个文件两次。因此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是一个预处理器指令,它具有与复制指定文件内容并将其粘贴到源文件中相同的逻辑效果,因此,如果最终包含两次相同的文件,则可能会得到一些结果如:

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

... lots of other things ... 

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

并且编译器将停止并且声明两次相同的事件。 Objective-C中的#import避免了#include的缩写,但只有当你还没有#include该文件时。 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相对于它是正常将代表事物的引用(无论是指针还是其他)引入标题中,并在源文件中创建或声明该事物。实际上,如果你开始在头文件中放置声明,那么当程序开始链接时,最终会出现错误,因为多个源文件会认为它们声明了它。

+0

信息化的回答,谢谢。我不完全了解如何使用#ifndef __SOME_SYMBOL #define __SOME_SYMBOL来检查头文件是否已被复制。我想这样做,显然删除不需要的副本。你可以进一步详细一点。 :) – jarryd 2011-04-05 11:39:42

+1

我编辑了我的答案;希望能帮助到你! – Tommy 2011-04-05 12:07:45

+0

非常详细的解释,谢谢@Tommy! – efimovdk 2016-11-24 23:24:18

1

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

是否有任何等效Objective-C中的@class用法? - >不,没有任何其他等效的 在.h文件中声明指针并初始化它们/将它们分配到.c文件中是否是一种好的做法? - >是的,如果你的对象是公开的,那么你必须在.h文件中声明,但总是在构造函数中初始化它们的好习惯。

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中是存在的 “只是因为”

玩得开心,这个党为吸引而