这个问题是关于普通的c函数,而不是c++的静态方法,在评论中已经澄清了。

我知道什么是静态变量,但是什么是静态函数呢?

为什么如果我声明一个函数,让我们说void print_matrix,在让我们说a.c(没有a.h),并包括“a.c”-我得到“print_matrix@@....)已经定义在a.b obj”,但如果我声明它为静态void print_matrix,然后它编译?

只是澄清一下——我知道包含。c是不好的,就像你们很多人指出的那样。我这样做只是为了暂时清除main.c中的空间,直到我更好地了解如何将所有这些函数分组到适当的.h和.c文件中。只是一个临时的,快速的解决方案。


当前回答

因为静态函数只在这个文件中可见。 实际上,编译器可以为你做一些优化,如果你声明“静态”的一些函数。

这里有一个简单的例子。

c

#include <stdio.h>

static void test() 
{
    ghost(); // This is an unexist function.
}

int main()
{
    int ret = 0;

#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

并使用

gcc -o main main.c

你会看到它失败了。因为你甚至没有实现ghost()函数。

但是如果我们使用下面的命令。

gcc -DTEST -O2 -o main main.c

测试成功,程序可以正常运行。

为什么?这里有3个关键点。

-O2:编译器优化级别至少2。 -DTEST:定义TEST,因此TEST()将不会被调用。 为test()定义了“static”。

只有这3个条件都为真,才能通过编译。 由于这个“静态”声明,编译器可以确认test()将永远不会在其他文件中被调用。编译器可以在编译时删除test()。因为我们不需要test(),所以是否定义或实现ghost()并不重要。

其他回答

C语言中的“静态”函数是什么?

让我们从头说起。

这一切都是基于一个叫做“联动”的东西:

在不同作用域中声明的标识符或在同一作用域中声明的标识符可以通过称为链接的进程引用相同的对象或函数。29)有三种联系:外部联系、内部联系和无联系。”

来源:C18, 6.2.2/1


在构成整个程序的翻译单元和库的集合中,带有外部链接的特定标识符的每个声明都表示相同的对象或函数。在一个翻译单元中,带有内部链接的标识符的每个声明都表示相同的对象或函数。没有链接的标识符的每个声明都表示一个唯一的实体。”

来源:C18, 6.2.2/2


如果函数定义时没有存储类说明符,则默认情况下函数具有外部链接:

如果函数的标识符声明没有存储类说明符,则确定其链接的方法与使用存储类说明符extern声明时完全相同。

来源:C18, 6.2.2/5

这意味着,如果程序包含多个翻译单元/源文件(.c或.cpp),则该函数在程序的所有翻译单元/源文件中都可见。

在某些情况下,这可能是个问题。如果你想使用f.e.两个不同的函数(定义),但在两个不同的上下文中(实际上是文件-上下文)使用相同的函数名,该怎么办?

在C和c++中,静态存储类限定符应用于文件范围内的函数(在c++中不是类的静态成员函数或另一个块中的函数)现在可以提供帮助,并表示相应的函数只在定义它的翻译单元/源文件中可见,而在其他tlu /文件中不可见。

如果对象或函数的文件作用域标识符声明中包含存储类说明符static,则该标识符具有内部链接。30)”


函数声明只有在文件范围内才可以包含存储类说明符静态;6.7.1见。

来源:C18, 6.2.2/3


因此,静态函数只有在以下情况下才有意义:

您的程序包含几个翻译单元/源文件(.c或.cpp)。

and

您希望将函数的作用域限制在定义特定函数的文件中。

如果这两个需求不匹配,就不需要考虑将函数限定为静态函数。


边注:

正如前面提到的,静态函数在C和c++之间没有任何区别,因为这是c++从C继承来的特性。

在c++社区中,有一场令人心碎的争论是,与使用未命名的名称空间相比,将合格函数降级为静态函数并不重要。未命名的名称空间最初是由c++ 03标准中一个错误的段落初始化的,宣布静态函数的使用已弃用,很快委员会自己就对其进行了修订,并在c++ 11中删除了。

这涉及到各种SO问题:

未命名/匿名名称空间vs.静态函数

未命名命名空间相对于静态命名空间的优越性?

为什么未命名的名称空间是静态名称空间的“高级”替代品?

弃用静态关键字…没有更多的?

事实上,根据c++标准,它还没有被弃用。因此,使用静态函数仍然是合法的。即使未命名的命名空间有优势,关于在c++中使用或不使用静态函数的讨论也取决于一个人的想法(基于意见),这并不适合本网站。

静态函数是仅对同一文件中的其他函数可见的函数(更准确地说,是同一翻译单元)。

编辑:对于那些认为问题的作者指的是一个“类方法”的人:因为问题被标记为C,他指的是一个普通的C函数。对于(c++ /Java/…)类方法,静态意味着该方法可以在类本身上调用,不需要该类的实例。

最小可运行的多文件作用域示例

在这里,我将说明静态如何跨多个文件影响函数定义的作用域。

a.c

#include <stdio.h>

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * https://stackoverflow.com/questions/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
 */
/*void f() { puts("a f"); }*/

/* OK: only declared, not defined. Will use the one in main. */
void f(void);

/* OK: only visible to this file. */
static void sf() { puts("a sf"); }

void a() {
    f();
    sf();
}

c

#include <stdio.h>

void a(void);        

void f() { puts("main f"); }

static void sf() { puts("main sf"); }

void m() {
    f();
    sf();
}

int main() {
    m();
    a();
    return 0;
}

GitHub上游。

编译并运行:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
./main

输出:

main f
main sf
main f
a sf

解释

有两个单独的函数sf,每个文件一个 只有一个共享函数f

通常,作用域越小越好,所以如果可以,总是将函数声明为静态的。

在C语言编程中,文件通常用来表示“类”,静态函数表示类的“私有”方法。

一个常见的C模式是将this结构体作为第一个“方法”参数传递,这基本上就是c++在底层所做的事情。

这是什么标准啊

C99 N1256草案6.7.1“存储类说明符”说static是一个“存储类说明符”。

6.2.2/3“标识符的关联”说静态意味着内部关联:

如果对象或函数的文件作用域标识符声明中包含存储类说明符static,则该标识符具有内部链接。

6.2.2/2表示内部链接的行为就像我们的例子:

在构成整个程序的翻译单元和库的集合中,带有外部链接的特定标识符的每个声明都表示相同的对象或函数。在一个翻译单元中,带有内部链接的标识符的每个声明都表示相同的对象或函数。

其中“翻译单元”是预处理后的源文件。

GCC如何为ELF (Linux)实现它?

使用STB_LOCAL绑定。

如果我们编译:

int f() { return 0; }
static int sf() { return 0; }

然后分解符号表:

readelf -s main.o

输出信息包括:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 000000000000000b    11 FUNC    LOCAL  DEFAULT    1 sf
  9: 0000000000000000    11 FUNC    GLOBAL DEFAULT    1 f

所以结合是它们之间唯一重要的区别。Value只是它们在.bss部分中的偏移量,所以我们期望它有所不同。

STB_LOCAL在ELF规范http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html:上有文档

局部符号在包含其定义的目标文件之外是不可见的。相同名称的局部符号可以存在于多个文件中而互不干扰

这使得它成为代表静态的完美选择。

不带static的函数是STB_GLOBAL,规范说:

当链接编辑器组合了几个可重定位的目标文件时,它不允许使用相同名称定义多个STB_GLOBAL符号。

这与多个非静态定义的链接错误是一致的。

如果我们使用-O3进行优化,sf符号将完全从符号表中删除:无论如何它都不能从外部使用。当没有优化时,为什么要在符号表上保持静态函数?它们有什么用途吗?

另请参阅

变量也是一样:https://stackoverflow.com/a/14339047/895245 extern是静态的反面,函数默认情况下已经是extern:我如何使用extern在源文件之间共享变量?

C+匿名namespaces

在c++中,您可能希望使用匿名名称空间而不是静态名称空间,这可以达到类似的效果,但进一步隐藏了类型定义:未命名/匿名名称空间vs.静态函数

因为静态函数只在这个文件中可见。 实际上,编译器可以为你做一些优化,如果你声明“静态”的一些函数。

这里有一个简单的例子。

c

#include <stdio.h>

static void test() 
{
    ghost(); // This is an unexist function.
}

int main()
{
    int ret = 0;

#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

并使用

gcc -o main main.c

你会看到它失败了。因为你甚至没有实现ghost()函数。

但是如果我们使用下面的命令。

gcc -DTEST -O2 -o main main.c

测试成功,程序可以正常运行。

为什么?这里有3个关键点。

-O2:编译器优化级别至少2。 -DTEST:定义TEST,因此TEST()将不会被调用。 为test()定义了“static”。

只有这3个条件都为真,才能通过编译。 由于这个“静态”声明,编译器可以确认test()将永远不会在其他文件中被调用。编译器可以在编译时删除test()。因为我们不需要test(),所以是否定义或实现ghost()并不重要。

静态函数是可以在类本身上调用的函数,而不是类的实例。

例如,一个非静态变量是:

Person* tom = new Person();
tom->setName("Tom");

此方法作用于类的实例,而不是类本身。然而,你可以有一个静态方法,它可以在没有实例的情况下工作。有时会在Factory模式中使用:

Person* tom = Person::createNewPerson();