在C语言中创建自己的头文件

232

有人可以解释一下如何从头到尾创建一个C头文件的简单示例吗?


31
你有没有读过一本C语言入门书?这里有一本在线的:http://publications.gbdirect.co.uk/c_book/。 - Oliver Charlesworth
4个回答

399

foo.h

#ifndef FOO_H_   /* Include guard */
#define FOO_H_

int foo(int x);  /* An example function declaration */

#endif // FOO_H_

foo.c

#include "foo.h"  /* Include the header (not strictly necessary here) */

int foo(int x)    /* Function definition */
{
    return x + 5;
}

main.c

#include <stdio.h>
#include "foo.h"  /* Include the header here, to obtain the function declaration */

int main(void)
{
    int y = foo(3);  /* Use the function here */
    printf("%d\n", y);
    return 0;
}

使用GCC编译

gcc -o my_app main.c foo.c

2
@Anu:我无法以这种格式阅读。您可以编辑原始问题以包含此代码。 - Oliver Charlesworth
3
值得注意的是,如果你尝试通过按钮(例如在Code::Blocks中点击“构建和运行”)构建此代码,则无法正常工作。这对你可能很明显,但对我来说是第一次遇到,花费了我相当长的时间才找到问题所在。 - Jeyekomon
6
那么,问题到底出在哪里呢? - Oliver Charlesworth
2
没有人告诉我,“构建和运行”按钮不足以完成一切。 :-) 对我来说(作为一个新手),这是非常令人惊讶的。现在我猜我必须先学会使用命令行或makefile。 - Jeyekomon
1
使用make,您(可能)仍将使用gcccc,并且仍需要告诉make告诉任一编译器需要哪些文件。为什么您希望避免向编译器提供“foo.c”? - eenblam
显示剩余6条评论

37
#ifndef MY_HEADER_H
# define MY_HEADER_H

//put your function headers here

#endif

MY_HEADER_H 作为一个双重包含保护。

对于函数声明,你只需要定义函数的签名,也就是不带参数名称的方式,像这样:

int foo(char*);

如果你真的想要,也可以包含参数的标识符,但这不是必需的,因为标识符只会在函数的主体(实现)中使用,在头文件中(参数签名)它是不存在的。

这里声明了一个名为foo的函数,它接受一个char*类型的参数并返回一个int类型的值。

在你的源文件中,你需要有:

#include "my_header.h"

int foo(char* name) {
   //do stuff
   return 0;
}

1
它们被称为函数声明或函数原型,而不是“函数头文件”。头文件是你要包含的文件,而不是其中的声明。 - Jonathan Wakely
@JonathanWakely 这些是头文件。名称就说明了一切:头文件包含头部信息。但感谢您的反馈,它让我思考了一秒钟。 - Flavius
1
不,头文件本身就是文件,而不是它们所包含的声明。你能找到一个可靠的参考来支持你使用“头文件”的吗?例如,它与K&R、C标准、《UNIX编程环境》和Wikipedia相矛盾。 - Jonathan Wakely
@JonathanWakely 你有真正读过 K&R 吗?目录中有一个“4.5 头文件”章节,“头文件”使用斜体字表示术语。 在书的其余部分,作者有时仅使用“header”进行简写,但通过格式和目录可以清楚地知道正确的术语是什么。 所以请做一个专业人士,认识到自己的错误。 - Flavius
1
是的,“header”指的是文件,而不是其中的声明。在第二版中,可以参考第241页关于标准头文件的讨论,以及第33页关于“定义”和“声明”的讨论(您错误地称之为“函数头”),并明确定义了一个“header”:“通常的做法是将变量和函数的extern声明收集到一个单独的文件中,历史上称为“header”,每个源文件都在前面用#include包含它。例如,标准库的函数就是在像<stdio.h>这样的头文件中声明的。” - Jonathan Wakely
1
第26页定义了“函数原型”,它在整本书中都有使用,并且它与“头文件”不可互换。另请参见§A7.3.2和§A8.6.3,了解有关函数声明的正式参考资料,它们也从未称其为“头文件”。因为这不是它们的称呼。 - Jonathan Wakely

10

我的文件.h

#ifndef _myfile_h
#define _myfile_h

void function();

#endif

我的文件.c

#include "myfile.h"

void function() {

}

void function(); as a declaration does not prevent calls like function(42);. Use void in the declaration like void function(void); - chux - Reinstate Monica
@chux-ReinstateMonica 为什么会允许这样做?有什么使用场景需要允许这样的定义和调用方式吗?(真的很好奇) - Randomness Slayer
@RandomnessSlayer 在 C 语言中允许的奇怪事情通常是由于历史上的妥协、趋势和不符合今天标准的原因。在 C 语言早期,回忆记忆的成本要高得多,因此许多选择都是基于这一点驱动的,这一点经常是有用的。 - chux - Reinstate Monica
@chux-ReinstateMonica 如果您能够指导我一个具体的或至少是富有趣味性的例子,我将不胜感激。我真的不理解如何在没有参数的函数中调用带有参数的函数会减少内存开销,您知道吗? - Randomness Slayer
@RandomnessSlayer 不清楚为什么 没有参数的函数带有参数会减少内存开销。带参数的无参函数不是一个错误,而只是浪费,更可能是编码错误而不是有用的东西。 我看到声明 void function(); 允许这样做,因为这是早期 C 的风格,其中一个 声明 实际上只是告诉函数名存在和返回类型。 - chux - Reinstate Monica

8

头文件包含了你在 .c 或 .cpp/.cxx 文件中定义的函数原型(取决于你使用的是 c 还是 c++)。你需要在你的 .h 代码周围放置 #ifndef/#define,这样如果你在程序的不同部分两次包含同一个 .h 文件,原型只会被包含一次。

client.h

#ifndef CLIENT_H
#define CLIENT_H

short socketConnect(char *host,unsigned short port,char *sendbuf,char *recievebuf, long rbufsize);


#endif /** CLIENT_H */

然后您可以在一个.c文件中实现.h文件,就像这样:

client.c

#include "client.h"

short socketConnect(char *host,unsigned short port,char *sendbuf,char *recievebuf, long rbufsize) {
 short ret = -1;
 //some implementation here
 return ret;
}

这段内容有误导性。它防止在同一源文件中两次包含相同的头文件(在两个不同的源文件中两次包含头文件是可以的,通常也是必需的!),重新声明函数原型并不是问题,必须防止重新定义类型和全局变量。 - Jonathan Wakely
这对我来说并不完全直观 - 也许更多地与我需要理解的预处理器的具体实现有关。为什么库文件(即上面示例中的foo.c)的源代码中没有使用#ifndef#define指令,而是在该库的头文件中使用?这是因为编译器知道它只有一个实际的定义foo.c吗? - simchuck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接