你如何定义头文件中的函数?

RBF*_*F06 2 c program-structure include-guards one-definition-rule

设置

如果我有这样的程序

一个声明我的主库函数的头文件,primary()并定义了一个简短的简单辅助函数helper().

/* primary_header.h */
#ifndef _PRIMARY_HEADER_H
#define _PRIMARY_HEADER_H

#include <stdio.h>

/* Forward declare the primary workhorse function */
void primary();

/* Also define a helper function */
void helper()
{
    printf("I'm a helper function and I helped!\n");
}
#endif /* _PRIMARY_HEADER_H */
Run Code Online (Sandbox Code Playgroud)

定义它的主要函数的实现文件。

/* primary_impl.c */
#include "primary_header.h"
#include <stdio.h>

/* Define the primary workhorse function */
void primary()
{
    /* do the main work */
    printf("I'm the primary function, I'm doin' work.\n");

    /* also get some help from the helper function */
    helper();
}
Run Code Online (Sandbox Code Playgroud)

main()通过调用测试代码的文件primary()

/* main.c */
#include "primary_header.h"

int main()
{
    /* just call the primary function */
    primary();
}
Run Code Online (Sandbox Code Playgroud)

问题

使用

gcc main.c primary_impl.c
Run Code Online (Sandbox Code Playgroud)

不链接,因为primary_header.h文件被包含两次,因此函数的双重定义是非法的helper()。为该项目构建源代码以防止双重定义的正确方法是什么?

小智 7

您几乎从不在头文件中编写函数,除非它被标记为始终内联。相反,您将函数写入.c文件并将函数的声明(不是定义)复制到头文件中,以便它可以在其他地方使用。


Mic*_* B. 5

你应该只在头文件中写你的函数原型,你的函数体应该写在一个 .c 文件中。

做这个 :

主要_header.c

/* primary_header.h */
#ifndef PRIMARY_HEADER_H
#define PRIMARY_HEADER_H

#include <stdio.h>

/* Forward declare the primary workhorse function */
void primary(void);

/* Also define a helper function */
void helper(void);

#endif /* PRIMARY_HEADER_H */
Run Code Online (Sandbox Code Playgroud)

primary_impl.c

/* primary_impl.c */
#include "primary_header.h"
#include <stdio.h>

/* Define the primary workhorse function */
void primary()
{
    /* do the main work */
    printf("I'm the primary function, I'm doin' work.\n");

    /* also get some help from the helper function */
    helper();
}

void helper()
{
    printf("I'm a helper function and I helped!\n");
}
Run Code Online (Sandbox Code Playgroud)

编辑:更改_PRIMARY_HEADER_HPRIMARY_HEADER_H. 正如@Jonathan Leffler 和@Pablo 所说,下划线名称是保留标识符

  • 请注意,标头中没有原型。有函数声明,但函数声明只是说函数采用不确定的参数列表(而不是像“printf()”这样的变量参数列表——此类函数需要显式原型)并且不指定原型。您需要在标头中写入“void Primary(void);”以提供原型。为了对称性,在定义该函数的源文件中写入“void Primary(void) { … }”。如果没有原型,如果您将 `primary(13, 29.6, "arbalest");` 编写为函数调用,编译器就不会抱怨。 (3认同)
  • 还要遵守保留标识符的规则。[C11 §7.1.3 保留标识符](https://port70.net/~nsz/c/c11/n1570.html#7.1.3) 部分说明: • _所有以下划线开头的标识符或大写字母或其他下划线始终保留用于任何用途。_ • _所有以下划线开头的标识符始终保留用作普通名称空间和标记名称空间中具有文件范围的标识符。_ 使用 `_PRIMARY_HEADER_H` 作为标题保护违反了这些规则中的第一个 - 尽管您看到“外面”有很多相同的标题,但不要这样做。 (2认同)
  • 人们模仿他们在系统标头中看到的内容,没有意识到系统标头_必须_使用该符号来挡路,就像您_不得_使用该符号来挡路一样。可悲的是,导师不知道这些细节是很常见的——但他们应该意识到这些细节。 (2认同)