什么是“静态”的功能?

问题描述 投票:441回答:10

现在的问题是关于纯功能,不 static方法,如在评论中澄清。

好吧,我明白了一个static变量是什么,但什么是static功能?

为什么它,如果我宣布一个功能,让我们说void print_matrix,在假设a.c(不a.h)和包括"a.c" - 我得到"print_matrix@@....) already defined in a.obj",但如果我宣布它作为然后static void print_matrix它编译?

UPDATE只是为了澄清一些事情 - 我知道,包括.c是坏的,正如你们许多人指出。我只是做它在main.c暂时清除空间,直到我有更好的想法如何将所有这些功能集成到适当的.h.c文件。只是一个暂时的,快速的解决方案。

c function static
10个回答
627
投票

static功能是只对在同一个文件(更准确地说是相同translation unit)等功能可见的功能。

编辑:对于那些谁想到,那的问题,笔者意味着“类方法”:由于这个问题被标记C他的意思是一个普通的老C函数。对于(C ++ /爪哇/ ...)类方法,static意味着此方法可以在类调用自身,没有必要使类的实例。


6
投票

未成年人挑剔:静态函数是一个翻译单元,这对于大多数实际情况是函数中定义的文件中看到你所得到的误差通常被称为违反了一个定义规则。

该标准可能说是这样的:

“每一个程序应包含在该程序中使用的每个非内联函数或对象中的正好一个定义;没有诊断必需的。”

这是观察静态函数的C方式。这是用C ++弃用然而。

在C ++中,另外,可以声明成员函数静态的。这些大多是元函数,即他们没有描述/修改全班本身特定对象的行为/状态,但行为。此外,这意味着你不需要创建一个对象来调用静态成员函数。此外,这也意味着,你只有从这样的函数内可以访问静态成员变量。

我想补充鹦鹉的例子是基于这种静态成员函数来获取Singleton模式/使用单个对象在整个项目的生命周期。


187
投票

有在C ++用C静态函数和静态成员函数之间有很大的区别。在C中,一个静态函数不是其翻译单元,这是它被编译成目标文件的外部可见。换句话说,使得一个函数静态限制其范围。你可以把一个静态函数的为“私”,以它的* .c文件(尽管这并不完全正确)。

在C ++中,“静态”也可以适用于成员函数和类数据成员。静态数据成员也被称为“类变量”,而一个非静态数据成员是一个“实例变量”。这是Smalltalk的术语。这意味着,只有一个由一类的所有对象共享的静态数据成员的拷贝,而每个对象具有它自己的非静态数据成员的副本。因此,静态数据成员基本上是一个全局变量,这是一个类的成员。

非静态成员函数可以访问类的所有数据成员:静态和非静态的。静态成员函数只能对静态数据成员进行操作。

思考这个问题的一种方法是,在C ++静态数据成员和静态成员函数不属于任何对象,但对整个班级。


74
投票

有两个用途static关键字,当涉及到的功能在C ++中。

第一种方法是标记功能作为具有内部链接的,因此它不能在其它翻译单元被引用。这种用法在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”指针。


44
投票

最小的可运行的多文件范围例如

在这里我说明static如何影响到多个文件函数定义的范围。

交流转换器

#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();
}

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;
}

GitHub upstream

编译并运行:

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

像往常一样,范围越小越好,所以总是,如果你可以声明功能static

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

一个常见的模式C是围绕传递this结构作为第一个“方法”的说法,这基本上是什么C ++引擎盖下一样。

说一下它的标准是什么

C99 N1256 draft 6.7.1“存储类型修饰符”说static是一个“存储类说明”。

6.2.2 / 3 “标识的联系” 说static意味着internal linkage

如果一个对象或功能的文件范围标识符的声明包含存储类说明静态的,标识符具有内部连接。

和6.2.2 / 2表示,internal linkage行为就像在我们的例子:

在构成整个程序的一组翻译单元和库,具有外部链接的特定标识符的每个声明表示相同对象或功能。内的一个翻译单元,具有内部链接的标识符的每个声明表示相同对象或功能。

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

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被记录在上的http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html ELF规范:

STB_LOCAL局部符号是不包含其定义的目标文件的外部可见。同一个名字的局部符号可以在多个文件不存在相互干扰

这使得一个完美的选择代表static

不带静电的功能是STB_GLOBAL和规范说:

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

这是连贯与多个非静态定义链接错误。

如果我们杀青了-O3优化,sf符号从符号表中完全删除:它不能从外部反正使用。 TODO为何要保留静态函数符号表在所有的时候有没有优化?它们可用于什么吗?

也可以看看

C ++匿名的命名空间

在C ++中,您可能需要使用匿名的命名空间,而不是静态的,这也实现了类似的效果,但进一步的皮革类型定义:Unnamed/anonymous namespaces vs. static functions


19
投票

以下是关于纯C功能 - 在C ++类的改性剂“静态”具有另外的含义。

如果你只有一个文件,这个修改使绝对没有什么区别。所不同的开始与多个文件更大的项目:

在C中,每一个“模块”(sample.c文件和sample.h的组合)独立地和事后那些编译的对象文件(sample.o)的每个编译连接在一起以由链接器的可执行文件。

比方说,你有几个文件你在你的主文件包含其中两个有一个只能用于内部为方便起见称为add(int a, b)功能 - 编译器会轻松地创建这两个模块的目标文件,但链接器将抛出一个错误,因为它发现两个函数具有相同的名称,它不知道它应该使用哪一个(即使没有什么联系起来,因为它们不是用在其他地方,但在它自己的文件)。

这就是为什么你让这个功能,它仅用于内部,静态函数。在这种情况下,编译器不会产生典型的“你可以链接这个事情” -flag为连接器,使连接器没有看到这个功能并不会产生错误。


16
投票

第一:它通常是一个坏主意,包括在另一个文件中的.cpp文件 - 它会导致这样的问题:-)正常的方法是创建一个单独的编译单元,并添加一个头文件包含的文件。

其次:

C ++这里有一些容易混淆的术语 - 我不知道这件事,直到在评论中指出。

一)static functions - 由C继承的,你是在谈论这里。以外的任何类。静态函数意味着它不是当前编译单元外部可见的 - 所以你的情况A.OBJ有副本,你的其他代码中有一个独立的副本。 (腹胀与代码的多个副本最后的可执行)。

B)static member function - 什么面向对象而言的静态方法。住在类中。你带班,而不是通过一个对象实例调用它。

这两种不同的静态函数的定义是完全不同的。要小心 - 这里是龙。


14
投票

静态函数的定义将标志着这个符号内部。因此,它不会对从外部链接可见,但只能在同一编译单元,通常是同一个文件的功能。


8
投票

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

例如一个非静态的将是:

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

此方法适用于类,而不是类本身的实例。但是你可以有一个可以无需实例工作静态方法。这有时用在工厂模式:

Person* tom = Person::createNewPerson();

7
投票

这个问题的答案静态函数依赖于语言:

1)在没有OOPS语言,如C,这意味着该函数仅在其中定义的文件内是可访问的。

2)在OOPS语言,如C ++,它意味着该功能可以直接在类调用,而不创建它的一个实例。

© www.soinside.com 2019 - 2024. All rights reserved.