Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/65.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C/C++:头文件中的静态函数,它是什么意思?_C_Function_Static - Fatal编程技术网

C/C++:头文件中的静态函数,它是什么意思?

C/C++:头文件中的静态函数,它是什么意思?,c,function,static,C,Function,Static,我知道在源文件中声明静态函数意味着什么。我正在阅读一些代码,发现头文件中的静态函数可以在其他文件中调用 它将有效地在包含在其中的每个cpp文件中创建一个具有相同名称的单独静态函数。这同样适用于全局变量。它将有效地在包含在其中的每个cpp文件中创建一个具有相同名称的单独静态函数。这同样适用于全局变量。函数是否在头文件中定义?因此,实际代码直接在函数中给出,如下所示: static int addTwo(int x) { return x + 2; } 然后,这只是一种为许多不同的C文件提供有

我知道在源文件中声明静态函数意味着什么。我正在阅读一些代码,发现头文件中的静态函数可以在其他文件中调用

它将有效地在包含在其中的每个cpp文件中创建一个具有相同名称的单独静态函数。这同样适用于全局变量。

它将有效地在包含在其中的每个cpp文件中创建一个具有相同名称的单独静态函数。这同样适用于全局变量。

函数是否在头文件中定义?因此,实际代码直接在函数中给出,如下所示:

static int addTwo(int x)
{
  return x + 2;
}
然后,这只是一种为许多不同的C文件提供有用函数的方法。每个包含头文件的C文件都有自己的定义,可以调用。这当然会浪费内存,而且在我看来,这是一件非常丑陋的事情,因为在头中包含可执行代码通常不是一个好主意

记住,include:ing头基本上只是将头的内容和它包含的任何其他头粘贴到C文件中,就像编译器看到的那样。编译器永远不知道一个特定的函数定义来自头文件

更新:在很多情况下,像上面这样做其实是个好主意,我意识到我的答案听起来非常黑白,这有点过于简单化了。例如,建模或仅使用的代码可以像上面那样表示,甚至可以使用显式内联关键字:

static inline int addTwo(int *x)
{
  __add_two_superquickly(x);
}
这里,uuu add_utwo_superfast函数是一个虚构的内在函数,因为我们希望整个函数基本上编译成一条指令,所以我们真的希望它是内联的。不过,上面的操作比使用宏更简洁


当然,与直接使用内在函数相比的优势在于,将其封装在另一个抽象层中,可以在缺少特定内在函数的编译器上构建代码,通过提供替代实现并根据使用的编译器选择正确的实现。

函数是否在头文件中定义?因此,实际代码直接在函数中给出,如下所示:

static int addTwo(int x)
{
  return x + 2;
}
然后,这只是一种为许多不同的C文件提供有用函数的方法。每个包含头文件的C文件都有自己的定义,可以调用。这当然会浪费内存,而且在我看来,这是一件非常丑陋的事情,因为在头中包含可执行代码通常不是一个好主意

记住,include:ing头基本上只是将头的内容和它包含的任何其他头粘贴到C文件中,就像编译器看到的那样。编译器永远不知道一个特定的函数定义来自头文件

更新:在很多情况下,像上面这样做其实是个好主意,我意识到我的答案听起来非常黑白,这有点过于简单化了。例如,建模或仅使用的代码可以像上面那样表示,甚至可以使用显式内联关键字:

static inline int addTwo(int *x)
{
  __add_two_superquickly(x);
}
这里,uuu add_utwo_superfast函数是一个虚构的内在函数,因为我们希望整个函数基本上编译成一条指令,所以我们真的希望它是内联的。不过,上面的操作比使用宏更简洁


当然,直接使用内在函数的优势在于,将其包装在另一个抽象层中,通过提供替代实现并根据使用的编译器选择正确的实现,可以在缺少特定内在函数的编译器上构建代码。

正如其他人所说,它的含义与.c文件本身中的静态函数完全相同。这是因为.c和.h文件之间没有语义差异;只有由实际传递给编译器(通常名为.c)的文件组成的编译单元,在预处理器看到的流中插入包含行(通常名为.h)中命名的任何和所有文件的内容

C源位于名为.C的文件中,而公共声明位于名为.h的文件中的约定只是一种约定。但总的来说,这是一个好办法。根据该约定,.h文件中只应出现声明,这样通常可以避免在单个程序中多次定义同一符号

在这种特殊情况下,static关键字使符号对模块是私有的,因此不会出现多定义冲突。从这个意义上说,这样做是安全的。但是,如果不能保证函数是内联的,那么您就要冒在每个模块中实例化函数的风险,这些模块恰好包含头文件,这充其量只是浪费代码段中的内存

我不确定什么用例可以证明这样做是正确的 g在一个普遍可用的公共头中根本不需要这样做


如果.h文件是由代码生成的,并且只包含在一个.c文件中,那么我个人会将该文件命名为.h以外的名称,以强调它实际上根本不是一个公共头。例如,将二进制文件转换为初始化变量定义的实用程序可能会编写一个文件,该文件将通过include使用,并且很可能包含变量的静态声明,甚至可能包含访问器或其他相关实用程序函数的静态定义。

正如其他人所说,它的含义与.c文件本身中的静态函数完全相同。这是因为.c和.h文件之间没有语义差异;只有由实际传递给编译器(通常名为.c)的文件组成的编译单元,在预处理器看到的流中插入包含行(通常名为.h)中命名的任何和所有文件的内容

C源位于名为.C的文件中,而公共声明位于名为.h的文件中的约定只是一种约定。但总的来说,这是一个好办法。根据该约定,.h文件中只应出现声明,这样通常可以避免在单个程序中多次定义同一符号

在这种特殊情况下,static关键字使符号对模块是私有的,因此不会出现多定义冲突。从这个意义上说,这样做是安全的。但是,如果不能保证函数是内联的,那么您就要冒在每个模块中实例化函数的风险,这些模块恰好包含头文件,这充其量只是浪费代码段中的内存

我不确定什么用例可以在一个普遍可用的公共标题中证明这样做的合理性


如果.h文件是由代码生成的,并且只包含在一个.c文件中,那么我个人会将该文件命名为.h以外的名称,以强调它实际上根本不是一个公共头。例如,将二进制文件转换为初始化变量定义的实用程序可能会编写一个文件,该文件将通过include使用,并且很可能包含变量的静态声明,甚至可能是访问器或其他相关实用程序函数的静态定义。

在源文件或头文件中定义没有语义上的区别,基本上这两种定义在普通C中的意思相同,当使用静态关键字时,您限制了范围

但是,在头文件中写入此函数时存在一个问题,这是因为每次在源文件中包含头文件时,都会有一个具有相同实现的函数副本,这与在头文件中定义普通函数非常相似。通过在header中添加定义,您并没有达到静态函数的目的


因此,我建议您应该只在源文件中实现,而不在头文件中实现。

在源文件或头文件中定义没有语义上的区别,基本上这两者在普通C中的意思相同,当使用静态关键字时,您限制了范围

但是,在头文件中写入此函数时存在一个问题,这是因为每次在源文件中包含头文件时,都会有一个具有相同实现的函数副本,这与在头文件中定义普通函数非常相似。通过在header中添加定义,您并没有达到静态函数的目的


因此,我建议您只在源文件中使用实现,而不要在头文件中使用。

在一些只有头文件的库中使用小型内联函数是很有用的。在这种情况下,您总是希望复制函数,因此这不是一个坏模式。但是,这为您在单个头文件中插入单独的接口和实现部分提供了一种简单的方法:

// header.h

// interface part (for user?!)
static inline float av(float a, float b);

// implementation part (for developer)
static inline float av(float a, float b)
{
    return (a+b)/2.f;
}

GLK框架中的Apple vector math library使用了这样的构造,例如GLKMatrix4.h.

它在一些只有标题的库中使用,这些库具有小的内联函数。在这种情况下,您总是希望复制函数,因此这不是一个坏模式。但是,这为您在单个头文件中插入单独的接口和实现部分提供了一种简单的方法:

// header.h

// interface part (for user?!)
static inline float av(float a, float b);

// implementation part (for developer)
static inline float av(float a, float b)
{
    return (a+b)/2.f;
}

GLK框架中的Apple vector math library使用这样的构造,例如GLKMatrix4.h.

如果您在头文件中定义函数而不是简单地声明它,则函数的副本将在每个翻译单元中生成,基本上在包含此头文件的每个cpp文件中

这可能会增加可执行文件的大小,但如果函数很小,则可以忽略不计。优点是大多数编译器可以内联函数,这可以提高代码性能

但是这样做可能会有很大的不同,这在任何回答中都没有提到。如果函数使用静态局部变量 例如:

static int counter()
{
    static int ctr = 0;
    return ctr++;
}
而不是:

//header
int counter();

//source
int counter()
{
    static int ctr = 0;
    return ctr++;
}
然后,包括此头文件在内的每个源文件都有自己的计数器。如果函数在头文件中声明,并在源文件中定义,那么计数器将在整个程序中共享


所以说唯一的区别是性能和代码大小是错误的。

如果您在头文件中定义函数而不是简单地声明它,那么函数的副本将在每个翻译单元中生成,基本上在每个包含此头文件的cpp文件中

这可能会增加可执行文件的大小,但如果函数很小,则可以忽略不计。优点是大多数编译器可以内联函数,这可以提高代码性能

但是这样做可能会有很大的不同,这在任何回答中都没有提到。如果函数使用静态局部变量,例如:

static int counter()
{
    static int ctr = 0;
    return ctr++;
}
而不是:

//header
int counter();

//source
int counter()
{
    static int ctr = 0;
    return ctr++;
}
然后,包括此头文件在内的每个源文件都有自己的计数器。如果函数在头文件中声明,并在源文件中定义,那么计数器将在整个程序中共享



所以说唯一的区别是性能和代码大小是错误的。

好吧,编译器可能会内联短函数。因此,如果函数足够短,它实际上可以使用更少的内存。但我会预先编写一个内联函数,这样就不会收到关于未使用的静态函数的编译警告。@好的,我已经编辑过了。我希望最好晚一点谢谢。我想知道链接器是否会优化它。它看到b.obj上的addTwo没有被引用,然后从obj中删除定义?如果是这样,开销就是函数的大小*引用它的不同obj文件的数量。仍然大于函数的大小,但没有那么糟糕?@jheriko由于包含头的每个C文件都将获得自己的本地函数,因此函数中的代码将重复多次。与许多用户调用单个共享定义相比,这浪费了内存。编译器可能会内联短函数。因此,如果函数足够短,它实际上可以使用更少的内存。但我会预先编写一个内联函数,这样就不会收到关于未使用的静态函数的编译警告。@好的,我已经编辑过了。我希望最好晚一点谢谢。我想知道链接器是否会优化它。它看到b.obj上的addTwo没有被引用,然后从obj中删除定义?如果是这样,开销就是函数的大小*引用它的不同obj文件的数量。仍然大于函数的大小,但没有那么糟糕?@jheriko由于包含头的每个C文件都将获得自己的本地函数,因此函数中的代码将重复多次。与多个用户调用的单个共享定义相比,这浪费了内存。可能函数包含静态变量,在调用内部状态之间保留它们的值,因此每个模块通过拥有自己的函数克隆获得自己的变量私有副本?@ranreload,这是可能的。我个人会避免使用它,因为聪明的优化器或代码维护人员会有太多机会通过巧妙地消除明显冗余的函数体来打破它。此外,它为每个翻译单元提供一组内部状态。把所有的状态都放在一个显式的状态变量中,传递给每个调用,这样就不会那么混乱和脆弱了。是的,我也不同意这种“设计”。我更愿意将私有数据封装在文件global static vars C或类成员C++中,尽管pro是这样的:私有数据是封装的,在需要的地方可见,在其他地方不可见。也许函数包含静态变量,在调用内部状态之间保留其值,因此,每个模块都可以通过拥有自己的函数克隆获得自己的vars私有副本?@ranreload,这是一种可能性。我个人会避免使用它,因为聪明的优化器或代码维护人员会有太多机会通过巧妙地消除明显冗余的函数体来打破它。此外,它为每个翻译单元提供一组内部状态。把所有的状态都放在一个显式的状态变量中,传递给每个调用,这样就不会那么混乱和脆弱了。是的,我也不同意这种“设计”。我宁愿将私有数据封装在文件global static vars C或类成员C++中,尽管其优点是私有数据是封装的,在需要的地方可见,而在其他地方不可见。如果函数包含静态局部变量,则语义上会有很大的差异。在一种情况下,静态变量将被共享,在另一种情况下,每个编译单元将有多个不同的静态变量。因此,函数的行为可能完全不同。如果函数包含静态局部变量,则会有很强的语义差异。在一种情况下,静态变量将在 其他情况下,每个编译单元将有多个不同的静态变量。因此,函数行为可能完全不同。