C “中声明的静态函数”;";标头档
对我来说,在源文件(我指的是.c文件)中定义和声明静态函数是一条规则 然而,在非常罕见的情况下,我看到人们在头文件中声明它。 由于静态函数有内部链接,我们需要在每个文件中定义它,我们包括声明函数的头文件。这看起来很奇怪,与我们通常在声明某个东西为静态时想要的相去甚远 另一方面,如果一个幼稚的人试图在没有定义的情况下使用该函数,编译器会抱怨。所以从某种意义上说,即使听起来很奇怪,这样做也不是很不安全 我的问题是:C “中声明的静态函数”;";标头档,c,function,static,internal,linkage,C,Function,Static,Internal,Linkage,对我来说,在源文件(我指的是.c文件)中定义和声明静态函数是一条规则 然而,在非常罕见的情况下,我看到人们在头文件中声明它。 由于静态函数有内部链接,我们需要在每个文件中定义它,我们包括声明函数的头文件。这看起来很奇怪,与我们通常在声明某个东西为静态时想要的相去甚远 另一方面,如果一个幼稚的人试图在没有定义的情况下使用该函数,编译器会抱怨。所以从某种意义上说,即使听起来很奇怪,这样做也不是很不安全 我的问题是: 在头文件中声明静态函数有什么问题 风险是什么 对编译时间有什么影响 运行时是否存在
- 在头文件中声明静态函数有什么问题
- 风险是什么李>
- 对编译时间有什么影响李>
- 运行时是否存在任何风险李>
头文件中实现的唯一要求是C++模板函数和模板类成员函数。
首先,我想澄清我对您描述的情况的理解:头包含(仅)静态函数声明,而C文件包含定义,即函数的源代码。比如说 some.h:static void f();
// potentially more declarations
#include "some.h"
static void f() { printf("Hello world\n"); }
// more code, some of it potentially using f()
some.c:
static void f();
// potentially more declarations
#include "some.h"
static void f() { printf("Hello world\n"); }
// more code, some of it potentially using f()
如果这是你描述的情况,我不同意你的说法
由于静态函数有内部链接,我们需要在每个文件中定义它,我们包括声明函数的头文件
如果您声明了函数,但没有在给定的翻译单元中使用它,我认为您不必定义它。gcc接受这一点,并发出警告;标准似乎并不禁止它,除非我遗漏了什么。这在您的场景中可能很重要,因为不使用函数但包含带有声明的标头的转换单元不必提供未使用的定义。
现在让我们来看看问题:
- 在头文件中声明静态函数有什么问题?
这有点不寻常。通常,静态函数是仅在一个文件中需要的函数。它们被声明为静态的,通过限制它们的可见性使其显式化。因此,在标题中声明它们有些矛盾。如果函数确实在多个定义相同的文件中使用,则应使用单个定义将其设置为外部。如果只有一个翻译单元实际使用它,则声明不属于标头。
因此,一种可能的方案是确保各个翻译单元中不同实现的统一函数签名。对于C(和C++)中的不同返回类型,公共头会导致编译时错误;不同的参数类型只会在C中导致编译时错误(但在C++中不会,因为函数重载) - 风险是什么?
我不认为你的情况有风险。(与在头中包含函数定义相反,它可能违反封装原则。) - 对编译时间有什么影响?
函数声明很小,而且复杂度很低,因此在头中添加额外函数声明的开销可能可以忽略不计。但是,如果在许多翻译单元中为声明创建并包含一个额外的头,那么文件处理开销可能会很大(即,编译器在等待头i/O时会大量空闲) - 运行时是否存在任何风险?
我看不到任何风险
静态
(或静态内联
)函数
我个人认为在头文件中声明某些函数static
,只有两个很好的理由:
#if defined(PRNG_NAME) && defined(PRNG_MULTIPLIER) && defined(PRNG_CONSTANT) && defined(PRNG_MODULUS)
#define MERGE3_(a,b,c) a ## b ## c
#define MERGE3(a,b,c) MERGE3_(a,b,c)
#define NAME(name) MERGE3(PRNG_NAME, _, name)
static uint32_t NAME(state) = 0U;
static uint32_t NAME(next)(void)
{
NAME(state) = ((uint64_t)PRNG_MULTIPLIER * (uint64_t)NAME(state) + (uint64_t)PRNG_CONSTANT) % (uint64_t)PRNG_MODULUS;
return NAME(state);
}
#undef NAME
#undef MERGE3
#endif
#undef PRNG_NAME
#undef PRNG_MULTIPLIER
#undef PRNG_CONSTANT
#undef PRNG_MODULUS
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#define PRNG_NAME glibc
#define PRNG_MULTIPLIER 1103515245UL
#define PRNG_CONSTANT 12345UL
#define PRNG_MODULUS 2147483647UL
#include "prng32.h"
/* provides glibc_state and glibc_next() */
#define PRNG_NAME borland
#define PRNG_MULTIPLIER 22695477UL
#define PRNG_CONSTANT 1UL
#define PRNG_MODULUS 2147483647UL
#include "prng32.h"
/* provides borland_state and borland_next() */
int main(void)
{
int i;
glibc_state = 1U;
printf("glibc lcg: Seed %u\n", (unsigned int)glibc_state);
for (i = 0; i < 10; i++)
printf("%u, ", (unsigned int)glibc_next());
printf("%u\n", (unsigned int)glibc_next());
borland_state = 1U;
printf("Borland lcg: Seed %u\n", (unsigned int)borland_state);
for (i = 0; i < 10; i++)
printf("%u, ", (unsigned int)borland_next());
printf("%u\n", (unsigned int)borland_next());
return EXIT_SUCCESS;
}
#ifndef INBUFFER_H
#define INBUFFER_H
typedef struct {
unsigned char *head; /* Next buffered byte */
unsigned char *tail; /* Next byte to be buffered */
unsigned char *ends; /* data + size */
unsigned char *data;
size_t size;
int descriptor;
unsigned int status; /* Bit mask */
} inbuffer;
#define INBUFFER_INIT { NULL, NULL, NULL, NULL, 0, -1, 0 }
int inbuffer_open(inbuffer *, const char *);
int inbuffer_close(inbuffer *);
int inbuffer_skip_slow(inbuffer *, const size_t);
int inbuffer_getc_slow(inbuffer *);
static inline int inbuffer_skip(inbuffer *ib, const size_t n)
{
if (ib->head + n <= ib->tail) {
ib->head += n;
return 0;
} else
return inbuffer_skip_slow(ib, n);
}
static inline int inbuffer_getc(inbuffer *ib)
{
if (ib->head < ib->tail)
return *(ib->head++);
else
return inbuffer_getc_slow(ib);
}
#endif /* INBUFFER_H */
使用上面的示例,示例-prng32.h:
#if defined(PRNG_NAME) && defined(PRNG_MULTIPLIER) && defined(PRNG_CONSTANT) && defined(PRNG_MODULUS)
#define MERGE3_(a,b,c) a ## b ## c
#define MERGE3(a,b,c) MERGE3_(a,b,c)
#define NAME(name) MERGE3(PRNG_NAME, _, name)
static uint32_t NAME(state) = 0U;
static uint32_t NAME(next)(void)
{
NAME(state) = ((uint64_t)PRNG_MULTIPLIER * (uint64_t)NAME(state) + (uint64_t)PRNG_CONSTANT) % (uint64_t)PRNG_MODULUS;
return NAME(state);
}
#undef NAME
#undef MERGE3
#endif
#undef PRNG_NAME
#undef PRNG_MULTIPLIER
#undef PRNG_CONSTANT
#undef PRNG_MODULUS
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#define PRNG_NAME glibc
#define PRNG_MULTIPLIER 1103515245UL
#define PRNG_CONSTANT 12345UL
#define PRNG_MODULUS 2147483647UL
#include "prng32.h"
/* provides glibc_state and glibc_next() */
#define PRNG_NAME borland
#define PRNG_MULTIPLIER 22695477UL
#define PRNG_CONSTANT 1UL
#define PRNG_MODULUS 2147483647UL
#include "prng32.h"
/* provides borland_state and borland_next() */
int main(void)
{
int i;
glibc_state = 1U;
printf("glibc lcg: Seed %u\n", (unsigned int)glibc_state);
for (i = 0; i < 10; i++)
printf("%u, ", (unsigned int)glibc_next());
printf("%u\n", (unsigned int)glibc_next());
borland_state = 1U;
printf("Borland lcg: Seed %u\n", (unsigned int)borland_state);
for (i = 0; i < 10; i++)
printf("%u, ", (unsigned int)borland_next());
printf("%u\n", (unsigned int)borland_next());
return EXIT_SUCCESS;
}
#ifndef INBUFFER_H
#define INBUFFER_H
typedef struct {
unsigned char *head; /* Next buffered byte */
unsigned char *tail; /* Next byte to be buffered */
unsigned char *ends; /* data + size */
unsigned char *data;
size_t size;
int descriptor;
unsigned int status; /* Bit mask */
} inbuffer;
#define INBUFFER_INIT { NULL, NULL, NULL, NULL, 0, -1, 0 }
int inbuffer_open(inbuffer *, const char *);
int inbuffer_close(inbuffer *);
int inbuffer_skip_slow(inbuffer *, const size_t);
int inbuffer_getc_slow(inbuffer *);
static inline int inbuffer_skip(inbuffer *ib, const size_t n)
{
if (ib->head + n <= ib->tail) {
ib->head += n;
return 0;
} else
return inbuffer_skip_slow(ib, n);
}
static inline int inbuffer_getc(inbuffer *ib)
{
if (ib->head < ib->tail)
return *(ib->head++);
else
return inbuffer_getc_slow(ib);
}
#endif /* INBUFFER_H */
请注意,上面的inbuffer\u skip()
和inbuffer\u getc()
不检查ib
是否为非空;这是此类功能的典型特征。假设这些访问器函数处于“快速路径”,即经常调用。在这种情况下,甚至函数调用开销也很重要(使用静态内联
函数可以避免,因为它们在调用站点的代码中是重复的)
简单的访问器函数,如上面的inbuffer\u skip()
和inbuffer\u getc()
,也可以让编译器避免函数调用中涉及的寄存器移动,因为函数期望其参数位于特定寄存器或堆栈中,而