当前位置:  开发笔记 > 前端 > 正文

什么是"静态"功能?

如何解决《什么是"静态"功能?》经验,为你挑选了10个好方法。

问题是关于普通c函数,而不是c ++ static方法,正如评论中所阐明的那样.

好的,我理解static变量是什么,但什么是static函数?

为什么如果我声明一个函数,让我们说void print_matrix,让我们说a.c(WITHOUT a.h)和include "a.c"- 我得到"print_matrix@@....) already defined in a.obj",但是如果我声明它static void print_matrix然后它编译?

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



1> Johannes Wei..:

static函数是仅对同一文件中的其他函数可见的函数(更准确地说是相同的转换单元).

编辑:对于那些认为,问题的作者意味着"类方法"的人:由于问题被标记,C他意味着一个普通的旧C函数.对于(C++/Java/...)类方法,static意味着可以在类本身上调用此方法,不需要该类的实例.


C++方法通常被称为"成员函数",所以我同意C++引入了一些模糊性.这不是你的错 - 语言只是将关键字用于两个不同的事情.
为了澄清这个答案:函数的*name*仅对同一翻译单元的其他部分可见,在该名称的第一个声明之下.可以通过其他方式(例如函数指针)从其他单元(以及相同单元的较早部分)调用该函数.
@Chuck:C ++术语从不使用“方法”一词;这是Java术语-在C ++标准文档中,它始终被称为“成员函数”(请参见[此答案](/ sf / ask / 601752301 /#8596481)或此[C ++与Java术语词汇表](http:// www .uic.edu / classes / mcs / mcs494 / s02 / handouts / comparable-terms.pdf)(例如,C ++使用“数据成员”,而Java使用“字段”等)。

2> Dima..:

C中的静态函数和C++中的静态成员函数之间存在很大差异.在C中,静态函数在其转换单元之外是不可见的,它是编译成的目标文件.换句话说,使函数静态限制其范围.您可以将静态函数视为其*.c文件的"私有"(尽管这不是严格正确的).

在C++中,"static"也可以应用于类的成员函数和数据成员.静态数据成员也称为"类变量",而非静态数据成员是"实例变量".这是Smalltalk术语.这意味着类的所有对象只共享一个静态数据成员的副本,而每个对象都有自己的非静态数据成员副本.因此,静态数据成员本质上是一个全局变量,它是一个类的成员.

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

考虑这一点的一种方法是在C++中静态数据成员和静态成员函数不属于任何对象,而是属于整个类.


C++也有文件静态.无需将C带入此中.
在C++中,静态函数是静态函数.静态成员函数是静态成员函数,也称为方法.C没有成员的事实并不意味着函数是"C".
全局var和类静态var(命名空间除外)之间有什么区别吗?
命名空间是主要区别.另一个区别是您可以将静态数据成员设为私有,因此只能从类的成员函数中访问.换句话说,与全局变量相比,您可以更多地控制静态数据成员.

3> Brian Neal..:

关于C++中的函数,关键字static有两种用途.

第一种是将函数标记为具有内部链接,因此不能在其他翻译单元中引用它.在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"指针.


@Deqing这个问题最初标记为C++,作者谈到使用".cpp"文件.

4> Ciro Santill..:

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

在这里,我将说明如何static影响多个文件中的函数定义范围.

AC

#include 

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * /sf/ask/17360801/
 */
/*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 

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

像往常一样,范围越小越好,所以static如果可以,总是声明函数.

在C编程中,文件通常用于表示"类",而static函数表示类的"私有"方法.

一个常见的C模式是传递一个thisstruct作为第一个"方法"参数,这基本上是C++在幕后所做的.

标准说了什么

C99 N1256草案 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有关ELF规范的信息,请访问http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html:

STB_LOCAL在包含其定义的目标文件外部不显示本地符号.多个文件中可能存在同名的本地符号,而不会相互干扰

这使它成为一个完美的选择static.

没有静态的函数是STB_GLOBAL,并且规范说:

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

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

如果我们使用了优化-O3,则sf符号将完全从符号表中删除:无论如何都不能从外部使用它.TODO为什么在没有优化时根据符号表保留静态函数?他们可以用于任何事情吗?

也可以看看

变量相同:https://stackoverflow.com/a/14339047/895245

extern与...相反static,函数已extern默认:如何使用extern在源文件之间共享变量?

C++匿名命名空间

在C++中,您可能希望使用匿名命名空间而不是静态,这会产生类似的效果,但会进一步隐藏类型定义:未命名/匿名命名空间与静态函数


注意:`void f(){puts("sf"); ``(即`f()`的两个定义)导致未定义的行为而不需要诊断.实际上看到错误消息是一个链接器质量问题.

5> dersimn..:

以下是关于普通C函数 - 在C++类中,修饰符'static'具有另一种含义.

如果你只有一个文件,这个修饰符绝对没有区别.不同之处在于包含多个文件的更大项目:

在C中,每个"模块"(sample.c和sample.h的组合)都是独立编译的,然后每个编译的目标文件(sample.o)由链接器链接到一个可执行文件.

假设您有几个文件包含在主文件中,其中两个文件只有内部用于方便调用add(int a, b)- 编译器可以轻松地为这两个模块创建目标文件,但链接器会抛出错误,因为它找到两个具有相同名称的函数,它不知道它应该使用哪个函数(即使没有任何内容可以链接,因为它们不会在其他地方使用,而是在它自己的文件中使用).

这就是为什么你使这个函数,它只用于内部,一个静态函数.在这种情况下,编译器不会为链接器创建典型的"您可以链接此事物"-flag,因此链接器不会看到此函数并且不会生成错误.



6> Douglas Leed..:

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

其次:

C++在这里有一些令人困惑的术语 - 直到评论中指出我才知道它.

a)static functions- 继承自C,你在这里谈论的是什么.任何课外.静态函数意味着它在当前编译单元之外是不可见的 - 因此在您的情况下,a.obj具有副本,而您的其他代码具有独立副本.(使用代码的多个副本来膨胀最终的可执行文件).

b)static member function- 面向对象的静态方法.住在课堂里.您可以使用类而不是通过对象实例来调用它.

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



7> raimue..:

静态函数定义将此符号标记为内部.因此,对于从外部链接,但仅对同一编译单元中的函数(通常是同一文件)不可见.



8> Parrots..:

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

例如,非静态将是:

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

此方法适用于类的实例,而不是类本身.但是,您可以使用静态方法,无需实例即可工作.这有时在工厂模式中使用:

Person* tom = Person::createNewPerson();


C++中没有"方法",只有函数.C++标准从未提及"方法",只提到"函数".
在我看来,您是在谈论静态“方法”,而不是“功能”?

9> 小智..:

静态函数的答案取决于语言:

1)在没有像C这样的OOPS的语言中,这意味着该函数只能在其定义的文件中访问.

2)在具有像C++这样的OOPS的语言中,这意味着可以直接在类上调用该函数而无需创建它的实例.



10> dirkgently..:

Minor nit:静态函数对于翻译单元是可见的,对于大多数实际情况,该函数是定义函数的文件.您获得的错误通常被称为违反单一定义规则.

标准可能会说:

"每个程序应该只包含该程序中使用的每个非内联函数或对象的一个​​定义;不需要诊断."

这是查看静态函数的C方式.但是,这在C++中已弃用.

另外,在C++中,您可以将成员函数声明为static.这些主要是元函数,即它们不描述/修改特定对象的行为/状态,而是作用于整个类本身.此外,这意味着您不需要创建一个对象来调用静态成员函数.此外,这也意味着,您只能从这样的函数中访问静态成员变量.

我将Parrot的例子添加到Singleton模式中,该模式基于这种静态成员函数来在程序的整个生命周期中获取/使用单个对象.

推荐阅读
雨天是最美
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有