C-结构内部的功能


82

我试图在结构内创建一个函数,到目前为止,我有以下代码:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno AddClient() 

        {
            /* code */
        }

};

int main()
{

    client_t client;

    //code ..

    client.AddClient();

}

错误client.h:24:2:错误:在“ {”令牌之前应为“:”,“,”,“;”,“}”或“属性”。

哪个是正确的方法?


12
您不能在C中的结构中使用函数;您可以尝试通过函数指针粗略地模拟它。
Fingolfin 2013年

1
函数指针可以替代吗? stackoverflow.com/a/840703/635678
Dan O

Answers:


104

它不能直接完成,但是您可以使用函数指针并显式传递“ this”参数来模拟同一件事:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno (*AddClient)(client_t *);    
};

pno client_t_AddClient(client_t *self) { /* code */ }

int main()
{

    client_t client;
    client.AddClient = client_t_AddClient; // probably really done in some init fn

    //code ..

    client.AddClient(&client);

}

事实证明,这样做并不能真正给您带来很多好处。这样,您将不会看到以这种样式实现的许多C API,因为您可能只需调用外部函数并传递实例即可。


3
X11 API做了类似的事情。参见XImage
Hydranix

1
Windows API下的代码充满了这些。从技术上讲,“窗口类”是具有两个回调函数指针且具有开放式结尾的结构(对于“窗口类特定数据”,您可以在注册时提供该结构)
Swift-Friday Pie

1
遵循了这些步骤,但是当我执行struct.function = newFunction时,编译器将打印:error:预期之前为'=',',',';','asm'或' attribute '。令牌
Bonfra

当您想要具有读取功能和写入功能但从一种类型的硬盘驱动器到nexf有所不同的通用目标(例如硬盘驱动器)时,这也将很有用
Menotdan

如果在运行时选择了实际的实现,它将为您带来很多收益。如果只有静态函数,则每次调用函数时始终需要在该函数中重新选择;如果使用函数指针,则选择仅发生一次。而且选择甚至可以在结构的整个生命周期内发生变化。
Mecki

23

正如其他人指出的那样,通常将函数指针直接嵌入结构中是为了特殊目的而保留的,例如回调函数。

您可能想要的更像是虚拟方法表。

typedef struct client_ops_t client_ops_t;
typedef struct client_t client_t, *pno;

struct client_t {
    /* ... */
    client_ops_t *ops;
};

struct client_ops_t {
    pno (*AddClient)(client_t *);
    pno (*RemoveClient)(client_t *);
};

pno AddClient (client_t *client) { return client->ops->AddClient(client); }
pno RemoveClient (client_t *client) { return client->ops->RemoveClient(client); }

现在,添加更多操作不会更改client_t结构的大小。现在,这种灵活性仅在您需要定义多种客户端或希望允许client_t界面用户能够增强操作行为时才有用。

这种结构确实出现在实际代码中。OpenSSL BIO层与此类似,并且UNIX设备驱动程序接口也具有类似的层。


14

这仅适用于C ++。结构中的函数不是C的功能。

您的客户端也是如此。call ...这是对成员函数的调用,成员函数是面向对象的编程,即C ++。

将您的源转换为.cpp文件,并确保进行相应的编译。

如果您需要坚持使用C,则下面的代码是等效的(某种程度上):

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

};


pno AddClient(pno *pclient) 
{
    /* code */
}


int main()
{

    client_t client;

    //code ..

    AddClient(client);

}

1
这是一个uni项目,我必须使用C。有什么办法可以完成我想要的C语言?
xRed

只需将函数移到结构外部,并使其接受指向您实例的指针即可。
user123 2013年

没有一点复杂的魔术,client.AddClient()事情将是不可能的(请参阅其他答案)。
QSQ 2013年

12

这个怎么样?

#include <stdio.h>

typedef struct hello {
    int (*someFunction)();
} hello;

int foo() {
    return 0;
}

hello Hello() {
    struct hello aHello;
    aHello.someFunction = &foo;
    return aHello;
}

int main()
{
    struct hello aHello = Hello();
    printf("Print hello: %d\n", aHello.someFunction());

    return 0;
} 

类似于JavaScrip的“少量”解决方案
The Bitman

7

您正在尝试根据struct对代码进行分组。C分组是通过文件进行的。您将所有函数和内部变量放在标头或标头和从ac源文件编译的对象“ .o”文件中。

对于不是面向对象语言的C程序,无需从头开始重新发明面向对象。

我以前看过 这是一件奇怪的事。编码人员(其中一些编码人员)不愿意将要更改的对象传递给函数以对其进行更改,即使这是这样做的标准方法。

我归咎于C ++,因为它隐藏了以下事实:类对象始终是成员函数中的第一个参数,但它是隐藏的。因此,即使它确实没有将对象传递给函数。

Client.addClient(Client& c); // addClient first parameter is actually 
                             // "this", a pointer to the Client object.

C是灵活的,可以通过引用来传递事物。

AC函数通常仅返回状态字节或int,并且通常将其忽略。在您的情况下,适当的格式可能是

 err = addClient( container_t  cnt, client_t c);
 if ( err != 0 )
   { fprintf(stderr, "could not add client (%d) \n", err ); 

addClient将位于Client.h或Client.c中


这种方法各有利弊。仅仅因为这是一贯的做法,同时提到了非OO(do(object, subject))和OO(subject.do(object))布置函数的策略,并不意味着我们就应该停止尝试它。我完全认为有必要指出,这不是C语言的历史写法,并且C语言也不需要这样写(许多语言都一样,尤其是过程,函数或逻辑范式),但是我不需要我们积极阻止这种模式。它还具有一些好处
hiljusti
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.