什么是“静态”function?
问题是关于纯C函数,而不是C ++ static
方法,如评论中所澄清的。
好吧,我明白什么是static
variables,但什么是static
函数?
为什么如果我声明一个函数,让我们说void print_matrix
,在我们说ac
(没有ah
),并包括"ac"
– 我得到"print_matrix@@....) already defined in a.obj"
,但是如果我声明它为static void print_matrix
那么它编译?
更新只是为了清除事情 – 我知道包括.c
是不好的,正如你们许多人指出的那样。 我只是暂时清除main.c
空间,直到我更好地了解如何将所有这些函数组合到适当的.h
和.c
文件中。 只是一个临时的快速解决scheme。
static
函数是仅对同一文件中的其他函数(更确切地说是相同的翻译单元 )可见的函数。
编辑 :对于那些认为,问题的作者意味着“类方法”的人:因为问题被标记为C
他意味着一个普通的旧C函数。 对于(C ++ / Java / …)类的方法, static
意思是说这个方法可以在类本身上调用,而不需要那个类的实例。
C中的静态函数和C ++中的静态成员函数有很大的区别。 在C中,一个静态函数在其翻译单元之外是不可见的,它是编译到的目标文件。 换句话说,制作一个静态函数限制了它的范围。 你可以把一个静态函数想象成对它的* .c文件“私有”(尽pipe这不是严格正确的)。
在C ++中,“static”也可以应用于成员函数和类的数据成员。 静态数据成员也被称为“类variables”,而非静态数据成员是“实例variables”。 这是Smalltalk术语。 这意味着只有一个静态数据成员的副本由一个类的所有对象共享,而每个对象都有自己的非静态数据成员副本。 所以一个静态数据成员本质上是一个全局variables,它是一个类的成员。
非静态成员函数可以访问类的所有数据成员:static和non-static。 静态成员函数只能对静态数据成员进行操作。
考虑这一点的一种方法是,在C ++中,静态数据成员和静态成员函数不属于任何对象,而是属于整个类。
当涉及到C ++中的函数时,关键字static有两个用法。
首先是将该function标记为具有内部链接,因此不能在其他翻译单元中引用。 这个用法在C ++中已经被弃用了。 未命名的命名空间是这种用法的首选。
// inside some .cpp file: static void foo(); // old "C" way of having internal linkage // C++ way: namespace { void this_function_has_internal_linkage() { // ... } }
第二个用法是在一个类的上下文中。 如果一个类有一个静态成员函数,那就意味着这个函数是这个类的成员(并且对其他成员有通常的访问权限),但是不需要通过一个特定的对象来调用它。 换句话说,在这个函数里面,没有“this”这个指针。
最小的可运行示例
ac :
#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("af"); }*/ /* 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(); }
main.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; }
汇编 :
gcc -c ac -o ao gcc -c main.c -o main.o gcc -o main main.o ao
输出 :
main f main sf main f a sf
解释
- 有两个单独的函数
sf
,每个文件一个 - 有一个共享函数
f
像往常一样,范围越小越好,所以如果可以的话,总是声明static
函数。
在C编程中,文件通常用于表示“类”, static
函数表示类的“私有”方法。
什么标准说这个
C99 N1256草案 6.7.1“存储类说明符”说static
是一个“存储类说明符”。
6.2.2 / 3“标识符的链接”表示static
意味着internal linkage
:
如果对象或函数的文件范围标识符声明包含存储类说明符static,则标识符具有内部链接。
和6.2.2 / 2表示internal linkage
行为就像我们的例子一样:
在构成整个程序的翻译单元和库的集合中,具有外部链接的特定标识符的每个声明表示相同的对象或function。 在一个翻译单元内,每个具有内部链接的标识符的声明表示相同的对象或function。
其中“翻译单元”是预处理后的源文件。
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
部分的偏移量,所以我们期望它有所不同。
在http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html上的ELF规范中logging了;STB_LOCAL
:
STB_LOCAL在包含其定义的目标文件之外,本地符号不可见。 相同名称的本地符号可以存在于多个文件中而不会相互干扰
这使其成为代表static
的完美select。
没有静态的函数是STB_GLOBAL
,规范说:
当链接编辑器组合多个可重定位目标文件时,它不允许具有相同名称的多个STB_GLOBAL符号的定义。
这与多个非静态定义中的链接错误是一致的。
如果我们使用-O3
来优化,则sf
符号将从符号表中完全删除:无法从外部使用它。 TODO没有优化时,为什么要在符号表上保留静态函数? 他们可以用于任何事情吗?
也可以看看
- 同样的variables: https : //stackoverflow.com/a/14339047/895245
-
extern
与static
相反,默认情况下函数已经是extern
: 如何使用extern在源文件之间共享variables?
亲自尝试一下
在GitHub上的例子 ,你玩。
以下是关于纯C函数 – 在C ++类中,修饰符“static”具有另一个含义。
如果你只有一个文件,这个修饰符完全没有区别。 不同之处在于具有多个文件的更大的项目:
在C中,每个“模块”(sample.c和sample.h的组合)都是独立编译的,然后每个编译的目标文件(sample.o)通过链接器链接到一个可执行文件。
假设你在主文件中包含了几个文件,其中两个文件有一个函数,这个函数只在内部使用,叫做add(int a, b)
– 编译器很容易为这两个模块创build目标文件,但是链接器会抛出一个错误,因为它find了两个具有相同名称的函数,并且不知道应该使用哪一个函数(即使没有链接,因为它们不在别的地方,而是在它自己的文件中)。
这就是为什么你要做这个function,而这个function只是内部使用的一个静态函数。 在这种情况下,编译器不会为链接器创build典型的“可以链接这个东西”的标记,这样链接器就不会看到这个函数,也不会产生错误。
首先:在另一个文件中包含一个.cpp
文件通常是一个坏主意 – 它会导致这样的问题:-)通常的方法是创build单独的编译单元,并为包含的文件添加一个头文件。
其次:
C ++在这里有一些令人困惑的术语 – 直到评论中指出,我都不知道。
a)从Cinheritance的static functions
,以及你在这里讨论的内容。 在任何课外。 静态函数意味着它在当前编译单元之外是不可见的 – 所以在你的情况下,a.obj有一个副本,而你的其他代码有一个独立的副本。 (使用代码的多个副本来膨胀最终的可执行文件)。
b) static member function
– 什么对象方向术语是一种静态方法 。 生活在一个class级。 你可以通过类而不是通过对象实例来调用它。
这两个不同的静态函数定义是完全不同的。 小心 – 这里是龙。
静态函数定义将把这个符号标记为内部。 所以从外部链接看不到,而只能在同一个编译单元中的函数,通常是同一个文件。
静态函数是一个可以在类本身上调用的函数,而不是类的一个实例。
例如,一个非静态的将是:
Person* tom = new Person(); tom->setName("Tom");
此方法适用于类的实例,而不是类本身。 然而,你可以有一个静态方法,可以工作,没有一个实例。 这有时在工厂模式中使用:
Person* tom = Person::createNewPerson();
Minor nit:对于翻译单元来说,静态函数是可见的,对于大多数实际情况来说,函数被定义在文件中。您所得到的错误通常被称为违反“一个定义规则”。
这个标准可能是这样说的:
“每个程序应该包含该程序中使用的每个非内联函数或对象的一个定义;不需要诊断”。
这是看静态函数的C方式。 这在C ++中已经被弃用了。
另外,在C ++中,你可以声明成员函数是静态的。 这些主要是元function,即它们不描述/修改特定对象的行为/状态,而是对整个类别本身进行操作。 另外,这意味着你不需要创build一个对象来调用静态成员函数。 此外,这也意味着,您只能从这样的函数中访问静态成员variables。
我将Parrot的例子join到Singleton模式中,该模式基于这种静态成员函数来在程序的整个生命周期中获取/使用单个对象。
静态函数的答案取决于语言:
1)在没有像C这样的OOPS的语言中,这意味着该函数只能在其定义的文件中访问。
2)在像C ++这样的OOPS语言中,这意味着可以在类上直接调用该函数,而无需创build它的一个实例。
因为“c”compiller中的静态函数不会在堆栈中创build它的内部variables,所以静态函数调用会更快,因此不能使用如下初始化函数:char c ='A'。