C-声明静态的函数的链接

时间:2013-09-06 11:42:59

标签: c static scope

声明为static的函数和变量具有内部链接,它们具有文件范围,并且对于其他文件中的函数不可见。

假设我声明了一个这样的函数: -

  static int foo(int i);

在一个名为file1.c的文件中 我可以通过使用指针从其他文件file2.c访问此函数。

我正在阅读一本书,上面写着它可以完成,但我不知道这是怎么回事。

这些是确切的行: -

因为它有内部链接,所以不能直接调用foo 在它定义的文件之外。(将foo声明为静态不会 完全阻止在另一个文件中调用它;间接的 通过函数指针调用仍然是可能的。)

5 个答案:

答案 0 :(得分:4)

也许你在找这个?

// file1.h
extern void (*fptr)(void);

// file1.c
static void foo(void)
{
    printf("Hello world!\n");
}

void (*fptr)(void) = &foo;  //or void (*fptr)(void) = foo;

// file2.c
#include "file1.h"

fptr();

此处,foo是静态的,但其地址是通过非static全局变量引用的。这完全有可能。

答案 1 :(得分:2)

该函数不能在任何其他文件中按名称调用,因为它在不同的文件中是静态的,但您可以使用函数指针来完成它。

extern int (*pf)(int);

您需要为此指针指定foo,然后才能访问它。

答案 2 :(得分:1)

H2CO3为您提供了正确的答案,另一种方式:

/* a.h */
typedef int (*fptr)(int);

fptr call_foo(void);

/* a.c */
#include "a.h"

static int foo(int i)
{
    return i * 2;
}

fptr call_foo(void)
{
    return foo;
}

/* main.c */
#include <stdio.h>
#include "a.h"

int main(void)
{
    fptr fn = call_foo();

    printf("%d\n", fn(2));
    return 0;
}

答案 3 :(得分:1)

我认为可行的另一种方法是使静态函数成为回调函数。

//------------------------------
// file react.c
#include "event.h"

static void react_on_event_A(void)
{
    // do something if event_A occurs.
}

void react_init(void)
{
    event_setup(EVENT_A, react_on_event_A);
}

在这里,您将react_on_event_A函数设置为事件驱动程序可以调用的回调,但阻止其他任何人通过其标识符调用该函数。你真的在跟别人说这个功能。

事件驱动程序可能如下所示:

//------------------------------
// file event.h
typedef enum {
    EVENT_A,
} event_t;

void event_setup(event_t event, void (*callback)(void));
void event_check_and_run(void);


//------------------------------
// file event.c
static void (*event_A_callback)(void);

void event_setup(event_t event, void (*callback)(void))
{
    if (event == EVENT_A) {
        event_A_callback = callback;
    }
}

// Scheduled periodically, or reacting on interrupt.
void event_check_and_run(void)
{
    if (occured(EVENT_A) && (event_A_callback != NULL)) {
        event_A_callback();
    }
}

这样做的好处是模块react控制哪些其他模块(在这种情况下为event)可以访问自己的静态函数。

使用其他替代方法(使函数static与否,或在头文件中发布指针),您可以访问任何人或每个人。

答案 4 :(得分:0)

简而言之:是的,您可以通过指针访问静态方法。

要理解这一点,最好还是要了解编译器的更多内容。

为清楚起见,编译的程序用机器代码编写。编译程序中有“程序加载器”的额外信息,但程序本身只是处理器执行的指令。

当您在C中调用函数“foo()”时,C编译器会将其转换为“CALL”处理器操作。 CALL操作在代码中跟随foo的地址(字面意思是存储器地址或“偏移”)。请注意,因为它是一个内存地址,所以不使用名称(“foo”)。另请注意,链接器无需知道“foo”即可使用。

当你在C中调用函数“bar()”并且函数在另一个编译单元(另一个C文件)中时,编译器有一点问题,因为它不知道程序中的哪个位置(内存中的位置) )函数是调用。也就是说它不知道在CALL操作之后写入什么地址。当发生这种情况时,它会编写代码,为地址留出空间,但会留下链接器的注释。该注释告诉链接器“将bar的地址放在这里”。因此链接器使用内存地址纠正写入的程序。允许链接器执行此操作;编译器在代码中写入包含每个函数名称和相应地址的表。

静态做什么?这只是告诉编译器不要在传递给链接器的表中写入函数的名称和地址。该函数仍然作为函数存在于代码中,但链接器不知道它在哪里。同一编译单元中的任何代码都将知道该函数的位置。因此,同一编译单元中的任何函数都可以将函数的地址作为编译单元外的指针传递。

用于传递函数指针的c代码如下所示:

file1.h

typedef void (* VoidFunctionPointer)();

extern VoidFunctionPointer somethingInteresting;

bar();

file1.c中

#include "a.h"
VoidFunctionPointer somethingInteresting;

static void foo() {
    // do something interesting;
}


void bar() {
    // we know what foo is because we're in the same compilation unit
    somethingInteresting = foo;
}

file2.c中

#include "a.h"

int main(int argC, char ** argV) {
        bar();
        // we can call somethingInteresting as if it is a function no matter
        // wether it's declared static in code or not
        // this can be foo, just as long as we don't have to use the name "foo"
        somethingInteresting();
}

在此代码中,file2实际上从file1运行静态函数。关键是file2永远不需要该函数的名称,因此static对函数指针没有影响。

我可以推荐阅读微软对PE格式的描述(.EXE和.DLL)[这里]:

http://msdn.microsoft.com/en-us/library/ms809762.aspx

相关问题