7

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

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

  static int foo(int i);

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

我正在浏览一本书,其中写着它可以做到,但我不知道这怎么可能。

这些是确切的行:-

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

4

5 回答 5

5

也许你正在寻找这个?

// 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全局变量引用的。这是完全可能的。

于 2013-09-06T11:49:34.963 回答
2

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

extern int (*pf)(int);

您需要分配foo给此指针,然后才能访问它。

于 2013-09-06T11:52:17.703 回答
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;
}
于 2013-09-06T12:07:11.750 回答
1

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

要理解这一点,最好多了解一下编译器的底层原理。

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

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

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

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

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

文件1.h

typedef void (* VoidFunctionPointer)();

extern VoidFunctionPointer somethingInteresting;

bar();

文件1.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;
}

文件2.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

于 2013-09-06T12:48:21.473 回答
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,或在头文件中发布指针),您可以不授予任何人访问权限,也可以授予所有人访问权限。

于 2013-09-06T13:29:07.573 回答