【问题标题】:C - function inside structC - 结构内的函数
【发布时间】:2013-06-07 19:04:27
【问题描述】:

我试图在 struct 中分配一个函数,到目前为止我有这个代码:

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();
}
**Error**: *client.h:24:2: error: expected ‘:’, ‘,’, ‘;’, ‘}’ or ‘__attribute__’ before ‘{’ token.*

正确的做法是什么?

【问题讨论】:

  • C 的结构中不能有函数;你可以尝试通过函数指针来粗略地模拟它。
  • 函数指针是可接受的替代品吗? stackoverflow.com/a/840703/635678

标签: c function struct


【解决方案1】:

它不能直接完成,但您可以使用函数指针并显式传递“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,因为您也可以只调用外部函数并传递实例。

【讨论】:

  • X11 API 做了类似的事情。见XImage
  • Windows API 下的代码就是这样。从技术上讲,“窗口类”是一个具有两个回调函数指针和开放端的结构(对于“窗口类特定数据”,您可以在注册时提供)
  • 遵循了这些步骤,但是当我执行 struct.function = newFunction 时,编译器会打印:error: expected '=', ',', ';', ' asm' 或 'attribute' 在 '.' 之前令牌
  • 当您想要一个具有读取功能和写入功能的通用目标(例如硬盘驱动器)时,这也很有用,但从一种类型的硬盘驱动器到 nexf 会有所不同
  • 如果在运行时选择了实际的实现,它会给你带来很多好处。如果您只有一个静态函数,您总是需要在每次函数调用时在该函数中重新选择,如果您使用函数指针,则选择只发生一次。选择甚至可以在结构的整个生命周期内发生变化。
【解决方案2】:

正如其他人所指出的,直接在结构中嵌入函数指针通常保留用于特殊目的,例如回调函数。

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

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 设备驱动程序接口也有这样的层。

【讨论】:

  • pno 指的是什么?请。
  • @smalinux 来自 OP。
  • 我对这种代码风格印象深刻。但我不知道为什么会出现分段错误codepad.org/sAlHy19b 我不明白AddClient 是否是递归的?将永远称自己为.. thx
  • 你能分享一个样本吗?此代码块解释,但如果您能提供示例使用代码,请提前致谢。
【解决方案3】:

这个怎么样?

#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 之类的解决方案
  • 这是真正的解决方案。最佳解决方案。我检查每个解决方案。其他的不容易使用,但这个。其他人不容易操纵,但这个。谢谢。
【解决方案4】:

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

同样适用于您的 client.AddClient(); 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);

}

【讨论】:

  • 这是 uni 项目,我必须使用 C。有什么办法可以用 C 完成我想要的吗?
  • 只需将该函数移出结构并使其接受指向您的实例的指针。
  • 如果没有一点复杂的魔法,client.AddClient() 是不可能的(见另一个答案)。
【解决方案5】:

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

没有必要从头开始重新发明面向对象 对于 C 程序,它不是面向对象的语言。

我以前见过这个。 这是一件奇怪的事情。编码人员,其中一些人,不愿意将他们想要更改的对象传递给函数来更改它,即使这是这样做的标准方法。

我责怪 C++,因为它隐藏了类对象始终是成员函数中的第一个参数的事实,但它是隐藏的。所以看起来它没有将对象传递给函数,即使它是。

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

C 很灵活,可以通过引用传递东西。

C 函数通常只返回一个状态字节或 int 并且经常被忽略。 在您的情况下,适当的形式可能是

 /* add client to struct, return 0 on success */
 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 不需要以这种方式编写(许多语言也是如此,尤其是程序性的,功能或逻辑范式),但我不需要积极阻止这种模式。它也有一些好处
【解决方案6】:

您可以将结构指针作为函数参数传递给函数。 它称为按引用传递。

如果您修改该指针内的某些内容,则其他内容将更新为。 试试这样:

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 *client)
{
        /* this will change the original client value */
        client.password = "secret";
}

int main()
{
    client_t client;

    //code ..

    AddClient(&client);
}

【讨论】:

  • 没有传递引用是C。你传递的是指针的副本,指针的引用是一样的,但是指针本身没有。
  • 感谢您的指正。我已经从 Tutorialspoint 阅读了C pass by reference tutorial。这和我上面写的有区别吗?
猜你喜欢
  • 2018-01-29
  • 2015-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-19
相关资源
最近更新 更多