C语言中的静态函数真的不可见吗?

时间:2012-06-17 17:21:26

标签: c

我被告知在一个static文件中定义为.c的函数无法从其他文件访问。但是在以下程序中,我可以从另一个文件访问static void show()函数。我对C中的static函数的理解是错误的吗?

a.h(第一个文件):

static void show()
{
printf("I am in static show function in a.c");
}

b.c(另一个档案):

#include"a.h"
void main()
{
show();
}

3 个答案:

答案 0 :(得分:11)

请记住#include通过复制并粘贴所包含文件的内容来工作。因此,在您的示例中,在处理#include之后,您会得到:

static void show()
{
printf("I am in static show function in a.c");
}

void main()
{
show();
}

很明显main可以看到show 1

解决方案是 #include .c文件。通常,您应该只有#include标题(.h)文件。不应在头文件中声明或定义静态函数,因此main将无法看到它。

<小时/> <子> 1。但是,您现在实际上拥有show函数的两个定义,一个在a.c中,另一个在b.c中。对于static函数,这不是问题,但对于非static函数,您将收到链接器错误。

答案 1 :(得分:6)

static关键字将链接规范更改为 Internal Linkage
标记为static的功能只能在 Translation Unit(TU) 可见

也许,您在该特定TU中可以使用相同的命名符号,您可以在其中访问该函数。只有在向我们展示代码后才能回答它的部分内容。

修改
在头文件中定义static函数时,会在包含它的每个转换单元中创建相同函数的副本。此类函数的每个实例都被视为一个单独的函数(每个函数的地址)函数是不同的),这些函数的每个实例都有自己的static局部变量&amp;字符串文字。

显然,这会起作用,但这可能会增加生成的二进制文件的大小。

答案 2 :(得分:1)

其他答案是正确的,但是说不能从其他文件访问静态函数并不准确。可以通过函数指针访问该函数。更准确地说,该功能的名称无法在另一个翻译单元中访问。

请记住,将C源代码转换为可执行程序包含概念阶段,包括:

  1. 预处理(其中#include指令被包含文件的内容替换
  2. 编译(一次处理一个翻译单元)
  3. 链接(其中翻译单元放在最终程序中)
  4. 假设我们有三个文件。 foo.h

    typedef void (*void_function_p)(void);
    
    extern void_function_p foo(void);
    

    foo.c

    #include "foo.h"
    #include <stdio.h>
    
    static void baz(void) {
        printf("worked!\n");
    }
    
    void_function_p foo(void) {
        return baz;
    }
    

    bar.c

    #include "foo.h"
    #include <stdio.h>
    
    int main(void) {
        (*foo())();
        return 0;
    }
    

    这个程序编译并打印“工作!”当它运行时。

    这里有两个翻译单元。一个是预处理foo.c中的代码(因为#include的工作原理还包括foo.hstdio.h中的代码。另一个是预处理bar.c中的代码(同样,它在foo.hstdio.h中有自己的代码副本。)

    通过让函数foo返回指向静态函数baz的指针,我们可以从baz函数调用main

    现在,考虑如果我们将main修改为如下所示会发生什么:

    int main(void) {
        (*foo())();
        baz();
        return 0;
    }
    

    此代码将导致链接器错误,因为此翻译单元中的名称baz无法链接到其他翻译单元中baz的定义。

    这是静态函数的第一个优点:另一个程序员不会意外地从另一个翻译单元访问我们的baz函数。

    现在,考虑如果我们将bar.c修改为如下所示会发生什么:

    #include "foo.h"
    #include <stdio.h>
    
    static void baz(void) {
            printf("still works!");
    }
    
    int main() {
            (*foo())();
            baz();
            return 0;
    }
    

    此代码将编译,并打印“工作!”其次是“仍然有效!”

    这是静态函数的第二个优点:我们定义了两个具有相同名称的函数(在不同的翻译单元中)。

    如果您尝试将两个静态定义放在同一个翻译单元中,则会出现关于定义baz两次的编译器错误。

    作为最后一点,如果您现在采用该程序并删除所有static,则会导致链接器错误,因为baz已定义两次(使用外部链接) ,这是不允许的。